67831e7ce55efb5f2107a60783779d0d70abd1b2
[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         
405     },
406     /**
407      * Hide a component - adds 'hidden' class
408      */
409     hide: function()
410     {
411         if(!this.getVisibilityEl()){
412             return;
413         }
414         
415         this.getVisibilityEl().addClass('hidden');
416         
417     }
418 });
419
420  /*
421  * - LGPL
422  *
423  * Body
424  *
425  */
426
427 /**
428  * @class Roo.bootstrap.Body
429  * @extends Roo.bootstrap.Component
430  * Bootstrap Body class
431  *
432  * @constructor
433  * Create a new body
434  * @param {Object} config The config object
435  */
436
437 Roo.bootstrap.Body = function(config){
438
439     config = config || {};
440
441     Roo.bootstrap.Body.superclass.constructor.call(this, config);
442     this.el = Roo.get(config.el ? config.el : document.body );
443     if (this.cls && this.cls.length) {
444         Roo.get(document.body).addClass(this.cls);
445     }
446 };
447
448 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
449
450     is_body : true,// just to make sure it's constructed?
451
452         autoCreate : {
453         cls: 'container'
454     },
455     onRender : function(ct, position)
456     {
457        /* Roo.log("Roo.bootstrap.Body - onRender");
458         if (this.cls && this.cls.length) {
459             Roo.get(document.body).addClass(this.cls);
460         }
461         // style??? xttr???
462         */
463     }
464
465
466
467
468 });
469 /*
470  * - LGPL
471  *
472  * button group
473  * 
474  */
475
476
477 /**
478  * @class Roo.bootstrap.ButtonGroup
479  * @extends Roo.bootstrap.Component
480  * Bootstrap ButtonGroup class
481  * @cfg {String} size lg | sm | xs (default empty normal)
482  * @cfg {String} align vertical | justified  (default none)
483  * @cfg {String} direction up | down (default down)
484  * @cfg {Boolean} toolbar false | true
485  * @cfg {Boolean} btn true | false
486  * 
487  * 
488  * @constructor
489  * Create a new Input
490  * @param {Object} config The config object
491  */
492
493 Roo.bootstrap.ButtonGroup = function(config){
494     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
495 };
496
497 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
498     
499     size: '',
500     align: '',
501     direction: '',
502     toolbar: false,
503     btn: true,
504
505     getAutoCreate : function(){
506         var cfg = {
507             cls: 'btn-group',
508             html : null
509         };
510         
511         cfg.html = this.html || cfg.html;
512         
513         if (this.toolbar) {
514             cfg = {
515                 cls: 'btn-toolbar',
516                 html: null
517             };
518             
519             return cfg;
520         }
521         
522         if (['vertical','justified'].indexOf(this.align)!==-1) {
523             cfg.cls = 'btn-group-' + this.align;
524             
525             if (this.align == 'justified') {
526                 console.log(this.items);
527             }
528         }
529         
530         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
531             cfg.cls += ' btn-group-' + this.size;
532         }
533         
534         if (this.direction == 'up') {
535             cfg.cls += ' dropup' ;
536         }
537         
538         return cfg;
539     }
540    
541 });
542
543  /*
544  * - LGPL
545  *
546  * button
547  * 
548  */
549
550 /**
551  * @class Roo.bootstrap.Button
552  * @extends Roo.bootstrap.Component
553  * Bootstrap Button class
554  * @cfg {String} html The button content
555  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
556  * @cfg {String} size ( lg | sm | xs)
557  * @cfg {String} tag ( a | input | submit)
558  * @cfg {String} href empty or href
559  * @cfg {Boolean} disabled default false;
560  * @cfg {Boolean} isClose default false;
561  * @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)
562  * @cfg {String} badge text for badge
563  * @cfg {String} theme (default|glow)  
564  * @cfg {Boolean} inverse dark themed version
565  * @cfg {Boolean} toggle is it a slidy toggle button
566  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
567  * @cfg {String} ontext text for on slidy toggle state
568  * @cfg {String} offtext text for off slidy toggle state
569  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
570  * @cfg {Boolean} removeClass remove the standard class..
571  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
572  * 
573  * @constructor
574  * Create a new button
575  * @param {Object} config The config object
576  */
577
578
579 Roo.bootstrap.Button = function(config){
580     Roo.bootstrap.Button.superclass.constructor.call(this, config);
581     this.weightClass = ["btn-default", 
582                        "btn-primary", 
583                        "btn-success", 
584                        "btn-info", 
585                        "btn-warning",
586                        "btn-danger",
587                        "btn-link"
588                       ],  
589     this.addEvents({
590         // raw events
591         /**
592          * @event click
593          * When a butotn is pressed
594          * @param {Roo.bootstrap.Button} btn
595          * @param {Roo.EventObject} e
596          */
597         "click" : true,
598          /**
599          * @event toggle
600          * After the button has been toggles
601          * @param {Roo.bootstrap.Button} btn
602          * @param {Roo.EventObject} e
603          * @param {boolean} pressed (also available as button.pressed)
604          */
605         "toggle" : true
606     });
607 };
608
609 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
610     html: false,
611     active: false,
612     weight: '',
613     size: '',
614     tag: 'button',
615     href: '',
616     disabled: false,
617     isClose: false,
618     glyphicon: '',
619     badge: '',
620     theme: 'default',
621     inverse: false,
622     
623     toggle: false,
624     ontext: 'ON',
625     offtext: 'OFF',
626     defaulton: true,
627     preventDefault: true,
628     removeClass: false,
629     name: false,
630     target: false,
631      
632     pressed : null,
633      
634     
635     getAutoCreate : function(){
636         
637         var cfg = {
638             tag : 'button',
639             cls : 'roo-button',
640             html: ''
641         };
642         
643         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
644             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
645             this.tag = 'button';
646         } else {
647             cfg.tag = this.tag;
648         }
649         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
650         
651         if (this.toggle == true) {
652             cfg={
653                 tag: 'div',
654                 cls: 'slider-frame roo-button',
655                 cn: [
656                     {
657                         tag: 'span',
658                         'data-on-text':'ON',
659                         'data-off-text':'OFF',
660                         cls: 'slider-button',
661                         html: this.offtext
662                     }
663                 ]
664             };
665             
666             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
667                 cfg.cls += ' '+this.weight;
668             }
669             
670             return cfg;
671         }
672         
673         if (this.isClose) {
674             cfg.cls += ' close';
675             
676             cfg["aria-hidden"] = true;
677             
678             cfg.html = "&times;";
679             
680             return cfg;
681         }
682         
683          
684         if (this.theme==='default') {
685             cfg.cls = 'btn roo-button';
686             
687             //if (this.parentType != 'Navbar') {
688             this.weight = this.weight.length ?  this.weight : 'default';
689             //}
690             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
691                 
692                 cfg.cls += ' btn-' + this.weight;
693             }
694         } else if (this.theme==='glow') {
695             
696             cfg.tag = 'a';
697             cfg.cls = 'btn-glow roo-button';
698             
699             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
700                 
701                 cfg.cls += ' ' + this.weight;
702             }
703         }
704    
705         
706         if (this.inverse) {
707             this.cls += ' inverse';
708         }
709         
710         
711         if (this.active || this.pressed === true) {
712             cfg.cls += ' active';
713         }
714         
715         if (this.disabled) {
716             cfg.disabled = 'disabled';
717         }
718         
719         if (this.items) {
720             Roo.log('changing to ul' );
721             cfg.tag = 'ul';
722             this.glyphicon = 'caret';
723         }
724         
725         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
726          
727         //gsRoo.log(this.parentType);
728         if (this.parentType === 'Navbar' && !this.parent().bar) {
729             Roo.log('changing to li?');
730             
731             cfg.tag = 'li';
732             
733             cfg.cls = '';
734             cfg.cn =  [{
735                 tag : 'a',
736                 cls : 'roo-button',
737                 html : this.html,
738                 href : this.href || '#'
739             }];
740             if (this.menu) {
741                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
742                 cfg.cls += ' dropdown';
743             }   
744             
745             delete cfg.html;
746             
747         }
748         
749        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
750         
751         if (this.glyphicon) {
752             cfg.html = ' ' + cfg.html;
753             
754             cfg.cn = [
755                 {
756                     tag: 'span',
757                     cls: 'glyphicon glyphicon-' + this.glyphicon
758                 }
759             ];
760         }
761         
762         if (this.badge) {
763             cfg.html += ' ';
764             
765             cfg.tag = 'a';
766             
767 //            cfg.cls='btn roo-button';
768             
769             cfg.href=this.href;
770             
771             var value = cfg.html;
772             
773             if(this.glyphicon){
774                 value = {
775                             tag: 'span',
776                             cls: 'glyphicon glyphicon-' + this.glyphicon,
777                             html: this.html
778                         };
779                 
780             }
781             
782             cfg.cn = [
783                 value,
784                 {
785                     tag: 'span',
786                     cls: 'badge',
787                     html: this.badge
788                 }
789             ];
790             
791             cfg.html='';
792         }
793         
794         if (this.menu) {
795             cfg.cls += ' dropdown';
796             cfg.html = typeof(cfg.html) != 'undefined' ?
797                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
798         }
799         
800         if (cfg.tag !== 'a' && this.href !== '') {
801             throw "Tag must be a to set href.";
802         } else if (this.href.length > 0) {
803             cfg.href = this.href;
804         }
805         
806         if(this.removeClass){
807             cfg.cls = '';
808         }
809         
810         if(this.target){
811             cfg.target = this.target;
812         }
813         
814         return cfg;
815     },
816     initEvents: function() {
817        // Roo.log('init events?');
818 //        Roo.log(this.el.dom);
819         // add the menu...
820         
821         if (typeof (this.menu) != 'undefined') {
822             this.menu.parentType = this.xtype;
823             this.menu.triggerEl = this.el;
824             this.addxtype(Roo.apply({}, this.menu));
825         }
826
827
828        if (this.el.hasClass('roo-button')) {
829             this.el.on('click', this.onClick, this);
830        } else {
831             this.el.select('.roo-button').on('click', this.onClick, this);
832        }
833        
834        if(this.removeClass){
835            this.el.on('click', this.onClick, this);
836        }
837        
838        this.el.enableDisplayMode();
839         
840     },
841     onClick : function(e)
842     {
843         if (this.disabled) {
844             return;
845         }
846         
847         Roo.log('button on click ');
848         if(this.preventDefault){
849             e.preventDefault();
850         }
851         
852         if (this.pressed === true || this.pressed === false) {
853             this.toggleActive(e);
854         }
855         
856         
857         this.fireEvent('click', this, e);
858     },
859     
860     /**
861      * Enables this button
862      */
863     enable : function()
864     {
865         this.disabled = false;
866         this.el.removeClass('disabled');
867     },
868     
869     /**
870      * Disable this button
871      */
872     disable : function()
873     {
874         this.disabled = true;
875         this.el.addClass('disabled');
876     },
877      /**
878      * sets the active state on/off, 
879      * @param {Boolean} state (optional) Force a particular state
880      */
881     setActive : function(v) {
882         
883         this.el[v ? 'addClass' : 'removeClass']('active');
884         this.pressed = v;
885     },
886      /**
887      * toggles the current active state 
888      */
889     toggleActive : function(e)
890     {
891         this.setActive(!this.pressed);
892         this.fireEvent('toggle', this, e, !this.pressed);
893     },
894      /**
895      * get the current active state
896      * @return {boolean} true if it's active
897      */
898     isActive : function()
899     {
900         return this.el.hasClass('active');
901     },
902     /**
903      * set the text of the first selected button
904      */
905     setText : function(str)
906     {
907         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
908     },
909     /**
910      * get the text of the first selected button
911      */
912     getText : function()
913     {
914         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
915     },
916     hide: function() {
917        
918      
919         this.el.hide();   
920     },
921     show: function() {
922        
923         this.el.show();   
924     },
925     setWeight : function(str)
926     {
927         this.el.removeClass(this.weightClass);
928         this.el.addClass('btn-' + str);        
929     }
930     
931     
932 });
933
934  /*
935  * - LGPL
936  *
937  * column
938  * 
939  */
940
941 /**
942  * @class Roo.bootstrap.Column
943  * @extends Roo.bootstrap.Component
944  * Bootstrap Column class
945  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
946  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
947  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
948  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
949  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
950  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
951  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
952  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
953  *
954  * 
955  * @cfg {Boolean} hidden (true|false) hide the element
956  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
957  * @cfg {String} fa (ban|check|...) font awesome icon
958  * @cfg {Number} fasize (1|2|....) font awsome size
959
960  * @cfg {String} icon (info-sign|check|...) glyphicon name
961
962  * @cfg {String} html content of column.
963  * 
964  * @constructor
965  * Create a new Column
966  * @param {Object} config The config object
967  */
968
969 Roo.bootstrap.Column = function(config){
970     Roo.bootstrap.Column.superclass.constructor.call(this, config);
971 };
972
973 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
974     
975     xs: false,
976     sm: false,
977     md: false,
978     lg: false,
979     xsoff: false,
980     smoff: false,
981     mdoff: false,
982     lgoff: false,
983     html: '',
984     offset: 0,
985     alert: false,
986     fa: false,
987     icon : false,
988     hidden : false,
989     fasize : 1,
990     
991     getAutoCreate : function(){
992         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
993         
994         cfg = {
995             tag: 'div',
996             cls: 'column'
997         };
998         
999         var settings=this;
1000         ['xs','sm','md','lg'].map(function(size){
1001             //Roo.log( size + ':' + settings[size]);
1002             
1003             if (settings[size+'off'] !== false) {
1004                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1005             }
1006             
1007             if (settings[size] === false) {
1008                 return;
1009             }
1010             
1011             if (!settings[size]) { // 0 = hidden
1012                 cfg.cls += ' hidden-' + size;
1013                 return;
1014             }
1015             cfg.cls += ' col-' + size + '-' + settings[size];
1016             
1017         });
1018         
1019         if (this.hidden) {
1020             cfg.cls += ' hidden';
1021         }
1022         
1023         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1024             cfg.cls +=' alert alert-' + this.alert;
1025         }
1026         
1027         
1028         if (this.html.length) {
1029             cfg.html = this.html;
1030         }
1031         if (this.fa) {
1032             var fasize = '';
1033             if (this.fasize > 1) {
1034                 fasize = ' fa-' + this.fasize + 'x';
1035             }
1036             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1037             
1038             
1039         }
1040         if (this.icon) {
1041             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1042         }
1043         
1044         return cfg;
1045     }
1046    
1047 });
1048
1049  
1050
1051  /*
1052  * - LGPL
1053  *
1054  * page container.
1055  * 
1056  */
1057
1058
1059 /**
1060  * @class Roo.bootstrap.Container
1061  * @extends Roo.bootstrap.Component
1062  * Bootstrap Container class
1063  * @cfg {Boolean} jumbotron is it a jumbotron element
1064  * @cfg {String} html content of element
1065  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1066  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1067  * @cfg {String} header content of header (for panel)
1068  * @cfg {String} footer content of footer (for panel)
1069  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1070  * @cfg {String} tag (header|aside|section) type of HTML tag.
1071  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1072  * @cfg {String} fa font awesome icon
1073  * @cfg {String} icon (info-sign|check|...) glyphicon name
1074  * @cfg {Boolean} hidden (true|false) hide the element
1075  * @cfg {Boolean} expandable (true|false) default false
1076  * @cfg {Boolean} expanded (true|false) default true
1077  * @cfg {String} rheader contet on the right of header
1078  * @cfg {Boolean} clickable (true|false) default false
1079
1080  *     
1081  * @constructor
1082  * Create a new Container
1083  * @param {Object} config The config object
1084  */
1085
1086 Roo.bootstrap.Container = function(config){
1087     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1088     
1089     this.addEvents({
1090         // raw events
1091          /**
1092          * @event expand
1093          * After the panel has been expand
1094          * 
1095          * @param {Roo.bootstrap.Container} this
1096          */
1097         "expand" : true,
1098         /**
1099          * @event collapse
1100          * After the panel has been collapsed
1101          * 
1102          * @param {Roo.bootstrap.Container} this
1103          */
1104         "collapse" : true,
1105         /**
1106          * @event click
1107          * When a element is chick
1108          * @param {Roo.bootstrap.Container} this
1109          * @param {Roo.EventObject} e
1110          */
1111         "click" : true
1112     });
1113 };
1114
1115 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1116     
1117     jumbotron : false,
1118     well: '',
1119     panel : '',
1120     header: '',
1121     footer : '',
1122     sticky: '',
1123     tag : false,
1124     alert : false,
1125     fa: false,
1126     icon : false,
1127     expandable : false,
1128     rheader : '',
1129     expanded : true,
1130     clickable: false,
1131   
1132      
1133     getChildContainer : function() {
1134         
1135         if(!this.el){
1136             return false;
1137         }
1138         
1139         if (this.panel.length) {
1140             return this.el.select('.panel-body',true).first();
1141         }
1142         
1143         return this.el;
1144     },
1145     
1146     
1147     getAutoCreate : function(){
1148         
1149         var cfg = {
1150             tag : this.tag || 'div',
1151             html : '',
1152             cls : ''
1153         };
1154         if (this.jumbotron) {
1155             cfg.cls = 'jumbotron';
1156         }
1157         
1158         
1159         
1160         // - this is applied by the parent..
1161         //if (this.cls) {
1162         //    cfg.cls = this.cls + '';
1163         //}
1164         
1165         if (this.sticky.length) {
1166             
1167             var bd = Roo.get(document.body);
1168             if (!bd.hasClass('bootstrap-sticky')) {
1169                 bd.addClass('bootstrap-sticky');
1170                 Roo.select('html',true).setStyle('height', '100%');
1171             }
1172              
1173             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1174         }
1175         
1176         
1177         if (this.well.length) {
1178             switch (this.well) {
1179                 case 'lg':
1180                 case 'sm':
1181                     cfg.cls +=' well well-' +this.well;
1182                     break;
1183                 default:
1184                     cfg.cls +=' well';
1185                     break;
1186             }
1187         }
1188         
1189         if (this.hidden) {
1190             cfg.cls += ' hidden';
1191         }
1192         
1193         
1194         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1195             cfg.cls +=' alert alert-' + this.alert;
1196         }
1197         
1198         var body = cfg;
1199         
1200         if (this.panel.length) {
1201             cfg.cls += ' panel panel-' + this.panel;
1202             cfg.cn = [];
1203             if (this.header.length) {
1204                 
1205                 var h = [];
1206                 
1207                 if(this.expandable){
1208                     
1209                     cfg.cls = cfg.cls + ' expandable';
1210                     
1211                     h.push({
1212                         tag: 'i',
1213                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1214                     });
1215                     
1216                 }
1217                 
1218                 h.push(
1219                     {
1220                         tag: 'span',
1221                         cls : 'panel-title',
1222                         html : (this.expandable ? '&nbsp;' : '') + this.header
1223                     },
1224                     {
1225                         tag: 'span',
1226                         cls: 'panel-header-right',
1227                         html: this.rheader
1228                     }
1229                 );
1230                 
1231                 cfg.cn.push({
1232                     cls : 'panel-heading',
1233                     style : this.expandable ? 'cursor: pointer' : '',
1234                     cn : h
1235                 });
1236                 
1237             }
1238             
1239             body = false;
1240             cfg.cn.push({
1241                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1242                 html : this.html
1243             });
1244             
1245             
1246             if (this.footer.length) {
1247                 cfg.cn.push({
1248                     cls : 'panel-footer',
1249                     html : this.footer
1250                     
1251                 });
1252             }
1253             
1254         }
1255         
1256         if (body) {
1257             body.html = this.html || cfg.html;
1258             // prefix with the icons..
1259             if (this.fa) {
1260                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1261             }
1262             if (this.icon) {
1263                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1264             }
1265             
1266             
1267         }
1268         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1269             cfg.cls =  'container';
1270         }
1271         
1272         return cfg;
1273     },
1274     
1275     initEvents: function() 
1276     {
1277         if(this.expandable){
1278             var headerEl = this.headerEl();
1279         
1280             if(headerEl){
1281                 headerEl.on('click', this.onToggleClick, this);
1282             }
1283         }
1284         
1285         if(this.clickable){
1286             this.el.on('click', this.onClick, this);
1287         }
1288         
1289     },
1290     
1291     onToggleClick : function()
1292     {
1293         var headerEl = this.headerEl();
1294         
1295         if(!headerEl){
1296             return;
1297         }
1298         
1299         if(this.expanded){
1300             this.collapse();
1301             return;
1302         }
1303         
1304         this.expand();
1305     },
1306     
1307     expand : function()
1308     {
1309         if(this.fireEvent('expand', this)) {
1310             
1311             this.expanded = true;
1312             
1313             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1314             
1315             this.el.select('.panel-body',true).first().removeClass('hide');
1316             
1317             var toggleEl = this.toggleEl();
1318
1319             if(!toggleEl){
1320                 return;
1321             }
1322
1323             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1324         }
1325         
1326     },
1327     
1328     collapse : function()
1329     {
1330         if(this.fireEvent('collapse', this)) {
1331             
1332             this.expanded = false;
1333             
1334             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1335             this.el.select('.panel-body',true).first().addClass('hide');
1336         
1337             var toggleEl = this.toggleEl();
1338
1339             if(!toggleEl){
1340                 return;
1341             }
1342
1343             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1344         }
1345     },
1346     
1347     toggleEl : function()
1348     {
1349         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1350             return;
1351         }
1352         
1353         return this.el.select('.panel-heading .fa',true).first();
1354     },
1355     
1356     headerEl : function()
1357     {
1358         if(!this.el || !this.panel.length || !this.header.length){
1359             return;
1360         }
1361         
1362         return this.el.select('.panel-heading',true).first()
1363     },
1364     
1365     bodyEl : function()
1366     {
1367         if(!this.el || !this.panel.length){
1368             return;
1369         }
1370         
1371         return this.el.select('.panel-body',true).first()
1372     },
1373     
1374     titleEl : function()
1375     {
1376         if(!this.el || !this.panel.length || !this.header.length){
1377             return;
1378         }
1379         
1380         return this.el.select('.panel-title',true).first();
1381     },
1382     
1383     setTitle : function(v)
1384     {
1385         var titleEl = this.titleEl();
1386         
1387         if(!titleEl){
1388             return;
1389         }
1390         
1391         titleEl.dom.innerHTML = v;
1392     },
1393     
1394     getTitle : function()
1395     {
1396         
1397         var titleEl = this.titleEl();
1398         
1399         if(!titleEl){
1400             return '';
1401         }
1402         
1403         return titleEl.dom.innerHTML;
1404     },
1405     
1406     setRightTitle : function(v)
1407     {
1408         var t = this.el.select('.panel-header-right',true).first();
1409         
1410         if(!t){
1411             return;
1412         }
1413         
1414         t.dom.innerHTML = v;
1415     },
1416     
1417     onClick : function(e)
1418     {
1419         e.preventDefault();
1420         
1421         this.fireEvent('click', this, e);
1422     }
1423 });
1424
1425  /*
1426  * - LGPL
1427  *
1428  * image
1429  * 
1430  */
1431
1432
1433 /**
1434  * @class Roo.bootstrap.Img
1435  * @extends Roo.bootstrap.Component
1436  * Bootstrap Img class
1437  * @cfg {Boolean} imgResponsive false | true
1438  * @cfg {String} border rounded | circle | thumbnail
1439  * @cfg {String} src image source
1440  * @cfg {String} alt image alternative text
1441  * @cfg {String} href a tag href
1442  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1443  * @cfg {String} xsUrl xs image source
1444  * @cfg {String} smUrl sm image source
1445  * @cfg {String} mdUrl md image source
1446  * @cfg {String} lgUrl lg image source
1447  * 
1448  * @constructor
1449  * Create a new Input
1450  * @param {Object} config The config object
1451  */
1452
1453 Roo.bootstrap.Img = function(config){
1454     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1455     
1456     this.addEvents({
1457         // img events
1458         /**
1459          * @event click
1460          * The img click event for the img.
1461          * @param {Roo.EventObject} e
1462          */
1463         "click" : true
1464     });
1465 };
1466
1467 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1468     
1469     imgResponsive: true,
1470     border: '',
1471     src: 'about:blank',
1472     href: false,
1473     target: false,
1474     xsUrl: '',
1475     smUrl: '',
1476     mdUrl: '',
1477     lgUrl: '',
1478
1479     getAutoCreate : function()
1480     {   
1481         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1482             return this.createSingleImg();
1483         }
1484         
1485         var cfg = {
1486             tag: 'div',
1487             cls: 'roo-image-responsive-group',
1488             cn: []
1489         };
1490         var _this = this;
1491         
1492         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1493             
1494             if(!_this[size + 'Url']){
1495                 return;
1496             }
1497             
1498             var img = {
1499                 tag: 'img',
1500                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1501                 html: _this.html || cfg.html,
1502                 src: _this[size + 'Url']
1503             };
1504             
1505             img.cls += ' roo-image-responsive-' + size;
1506             
1507             var s = ['xs', 'sm', 'md', 'lg'];
1508             
1509             s.splice(s.indexOf(size), 1);
1510             
1511             Roo.each(s, function(ss){
1512                 img.cls += ' hidden-' + ss;
1513             });
1514             
1515             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1516                 cfg.cls += ' img-' + _this.border;
1517             }
1518             
1519             if(_this.alt){
1520                 cfg.alt = _this.alt;
1521             }
1522             
1523             if(_this.href){
1524                 var a = {
1525                     tag: 'a',
1526                     href: _this.href,
1527                     cn: [
1528                         img
1529                     ]
1530                 };
1531
1532                 if(this.target){
1533                     a.target = _this.target;
1534                 }
1535             }
1536             
1537             cfg.cn.push((_this.href) ? a : img);
1538             
1539         });
1540         
1541         return cfg;
1542     },
1543     
1544     createSingleImg : function()
1545     {
1546         var cfg = {
1547             tag: 'img',
1548             cls: (this.imgResponsive) ? 'img-responsive' : '',
1549             html : null,
1550             src : 'about:blank'  // just incase src get's set to undefined?!?
1551         };
1552         
1553         cfg.html = this.html || cfg.html;
1554         
1555         cfg.src = this.src || cfg.src;
1556         
1557         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1558             cfg.cls += ' img-' + this.border;
1559         }
1560         
1561         if(this.alt){
1562             cfg.alt = this.alt;
1563         }
1564         
1565         if(this.href){
1566             var a = {
1567                 tag: 'a',
1568                 href: this.href,
1569                 cn: [
1570                     cfg
1571                 ]
1572             };
1573             
1574             if(this.target){
1575                 a.target = this.target;
1576             }
1577             
1578         }
1579         
1580         return (this.href) ? a : cfg;
1581     },
1582     
1583     initEvents: function() 
1584     {
1585         if(!this.href){
1586             this.el.on('click', this.onClick, this);
1587         }
1588         
1589     },
1590     
1591     onClick : function(e)
1592     {
1593         Roo.log('img onclick');
1594         this.fireEvent('click', this, e);
1595     },
1596     /**
1597      * Sets the url of the image - used to update it
1598      * @param {String} url the url of the image
1599      */
1600     
1601     setSrc : function(url)
1602     {
1603         this.src =  url;
1604         
1605         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1606             this.el.dom.src =  url;
1607             return;
1608         }
1609         
1610         this.el.select('img', true).first().dom.src =  url;
1611     }
1612     
1613     
1614    
1615 });
1616
1617  /*
1618  * - LGPL
1619  *
1620  * image
1621  * 
1622  */
1623
1624
1625 /**
1626  * @class Roo.bootstrap.Link
1627  * @extends Roo.bootstrap.Component
1628  * Bootstrap Link Class
1629  * @cfg {String} alt image alternative text
1630  * @cfg {String} href a tag href
1631  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1632  * @cfg {String} html the content of the link.
1633  * @cfg {String} anchor name for the anchor link
1634  * @cfg {String} fa - favicon
1635
1636  * @cfg {Boolean} preventDefault (true | false) default false
1637
1638  * 
1639  * @constructor
1640  * Create a new Input
1641  * @param {Object} config The config object
1642  */
1643
1644 Roo.bootstrap.Link = function(config){
1645     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1646     
1647     this.addEvents({
1648         // img events
1649         /**
1650          * @event click
1651          * The img click event for the img.
1652          * @param {Roo.EventObject} e
1653          */
1654         "click" : true
1655     });
1656 };
1657
1658 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1659     
1660     href: false,
1661     target: false,
1662     preventDefault: false,
1663     anchor : false,
1664     alt : false,
1665     fa: false,
1666
1667
1668     getAutoCreate : function()
1669     {
1670         var html = this.html || '';
1671         
1672         if (this.fa !== false) {
1673             html = '<i class="fa fa-' + this.fa + '"></i>';
1674         }
1675         var cfg = {
1676             tag: 'a'
1677         };
1678         // anchor's do not require html/href...
1679         if (this.anchor === false) {
1680             cfg.html = html;
1681             cfg.href = this.href || '#';
1682         } else {
1683             cfg.name = this.anchor;
1684             if (this.html !== false || this.fa !== false) {
1685                 cfg.html = html;
1686             }
1687             if (this.href !== false) {
1688                 cfg.href = this.href;
1689             }
1690         }
1691         
1692         if(this.alt !== false){
1693             cfg.alt = this.alt;
1694         }
1695         
1696         
1697         if(this.target !== false) {
1698             cfg.target = this.target;
1699         }
1700         
1701         return cfg;
1702     },
1703     
1704     initEvents: function() {
1705         
1706         if(!this.href || this.preventDefault){
1707             this.el.on('click', this.onClick, this);
1708         }
1709     },
1710     
1711     onClick : function(e)
1712     {
1713         if(this.preventDefault){
1714             e.preventDefault();
1715         }
1716         //Roo.log('img onclick');
1717         this.fireEvent('click', this, e);
1718     }
1719    
1720 });
1721
1722  /*
1723  * - LGPL
1724  *
1725  * header
1726  * 
1727  */
1728
1729 /**
1730  * @class Roo.bootstrap.Header
1731  * @extends Roo.bootstrap.Component
1732  * Bootstrap Header class
1733  * @cfg {String} html content of header
1734  * @cfg {Number} level (1|2|3|4|5|6) default 1
1735  * 
1736  * @constructor
1737  * Create a new Header
1738  * @param {Object} config The config object
1739  */
1740
1741
1742 Roo.bootstrap.Header  = function(config){
1743     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1744 };
1745
1746 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1747     
1748     //href : false,
1749     html : false,
1750     level : 1,
1751     
1752     
1753     
1754     getAutoCreate : function(){
1755         
1756         
1757         
1758         var cfg = {
1759             tag: 'h' + (1 *this.level),
1760             html: this.html || ''
1761         } ;
1762         
1763         return cfg;
1764     }
1765    
1766 });
1767
1768  
1769
1770  /*
1771  * Based on:
1772  * Ext JS Library 1.1.1
1773  * Copyright(c) 2006-2007, Ext JS, LLC.
1774  *
1775  * Originally Released Under LGPL - original licence link has changed is not relivant.
1776  *
1777  * Fork - LGPL
1778  * <script type="text/javascript">
1779  */
1780  
1781 /**
1782  * @class Roo.bootstrap.MenuMgr
1783  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1784  * @singleton
1785  */
1786 Roo.bootstrap.MenuMgr = function(){
1787    var menus, active, groups = {}, attached = false, lastShow = new Date();
1788
1789    // private - called when first menu is created
1790    function init(){
1791        menus = {};
1792        active = new Roo.util.MixedCollection();
1793        Roo.get(document).addKeyListener(27, function(){
1794            if(active.length > 0){
1795                hideAll();
1796            }
1797        });
1798    }
1799
1800    // private
1801    function hideAll(){
1802        if(active && active.length > 0){
1803            var c = active.clone();
1804            c.each(function(m){
1805                m.hide();
1806            });
1807        }
1808    }
1809
1810    // private
1811    function onHide(m){
1812        active.remove(m);
1813        if(active.length < 1){
1814            Roo.get(document).un("mouseup", onMouseDown);
1815             
1816            attached = false;
1817        }
1818    }
1819
1820    // private
1821    function onShow(m){
1822        var last = active.last();
1823        lastShow = new Date();
1824        active.add(m);
1825        if(!attached){
1826           Roo.get(document).on("mouseup", onMouseDown);
1827            
1828            attached = true;
1829        }
1830        if(m.parentMenu){
1831           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1832           m.parentMenu.activeChild = m;
1833        }else if(last && last.isVisible()){
1834           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1835        }
1836    }
1837
1838    // private
1839    function onBeforeHide(m){
1840        if(m.activeChild){
1841            m.activeChild.hide();
1842        }
1843        if(m.autoHideTimer){
1844            clearTimeout(m.autoHideTimer);
1845            delete m.autoHideTimer;
1846        }
1847    }
1848
1849    // private
1850    function onBeforeShow(m){
1851        var pm = m.parentMenu;
1852        if(!pm && !m.allowOtherMenus){
1853            hideAll();
1854        }else if(pm && pm.activeChild && active != m){
1855            pm.activeChild.hide();
1856        }
1857    }
1858
1859    // private this should really trigger on mouseup..
1860    function onMouseDown(e){
1861         Roo.log("on Mouse Up");
1862         
1863         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1864             Roo.log("MenuManager hideAll");
1865             hideAll();
1866             e.stopEvent();
1867         }
1868         
1869         
1870    }
1871
1872    // private
1873    function onBeforeCheck(mi, state){
1874        if(state){
1875            var g = groups[mi.group];
1876            for(var i = 0, l = g.length; i < l; i++){
1877                if(g[i] != mi){
1878                    g[i].setChecked(false);
1879                }
1880            }
1881        }
1882    }
1883
1884    return {
1885
1886        /**
1887         * Hides all menus that are currently visible
1888         */
1889        hideAll : function(){
1890             hideAll();  
1891        },
1892
1893        // private
1894        register : function(menu){
1895            if(!menus){
1896                init();
1897            }
1898            menus[menu.id] = menu;
1899            menu.on("beforehide", onBeforeHide);
1900            menu.on("hide", onHide);
1901            menu.on("beforeshow", onBeforeShow);
1902            menu.on("show", onShow);
1903            var g = menu.group;
1904            if(g && menu.events["checkchange"]){
1905                if(!groups[g]){
1906                    groups[g] = [];
1907                }
1908                groups[g].push(menu);
1909                menu.on("checkchange", onCheck);
1910            }
1911        },
1912
1913         /**
1914          * Returns a {@link Roo.menu.Menu} object
1915          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1916          * be used to generate and return a new Menu instance.
1917          */
1918        get : function(menu){
1919            if(typeof menu == "string"){ // menu id
1920                return menus[menu];
1921            }else if(menu.events){  // menu instance
1922                return menu;
1923            }
1924            /*else if(typeof menu.length == 'number'){ // array of menu items?
1925                return new Roo.bootstrap.Menu({items:menu});
1926            }else{ // otherwise, must be a config
1927                return new Roo.bootstrap.Menu(menu);
1928            }
1929            */
1930            return false;
1931        },
1932
1933        // private
1934        unregister : function(menu){
1935            delete menus[menu.id];
1936            menu.un("beforehide", onBeforeHide);
1937            menu.un("hide", onHide);
1938            menu.un("beforeshow", onBeforeShow);
1939            menu.un("show", onShow);
1940            var g = menu.group;
1941            if(g && menu.events["checkchange"]){
1942                groups[g].remove(menu);
1943                menu.un("checkchange", onCheck);
1944            }
1945        },
1946
1947        // private
1948        registerCheckable : function(menuItem){
1949            var g = menuItem.group;
1950            if(g){
1951                if(!groups[g]){
1952                    groups[g] = [];
1953                }
1954                groups[g].push(menuItem);
1955                menuItem.on("beforecheckchange", onBeforeCheck);
1956            }
1957        },
1958
1959        // private
1960        unregisterCheckable : function(menuItem){
1961            var g = menuItem.group;
1962            if(g){
1963                groups[g].remove(menuItem);
1964                menuItem.un("beforecheckchange", onBeforeCheck);
1965            }
1966        }
1967    };
1968 }();/*
1969  * - LGPL
1970  *
1971  * menu
1972  * 
1973  */
1974
1975 /**
1976  * @class Roo.bootstrap.Menu
1977  * @extends Roo.bootstrap.Component
1978  * Bootstrap Menu class - container for MenuItems
1979  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1980  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1981  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1982  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1983  * 
1984  * @constructor
1985  * Create a new Menu
1986  * @param {Object} config The config object
1987  */
1988
1989
1990 Roo.bootstrap.Menu = function(config){
1991     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1992     if (this.registerMenu && this.type != 'treeview')  {
1993         Roo.bootstrap.MenuMgr.register(this);
1994     }
1995     this.addEvents({
1996         /**
1997          * @event beforeshow
1998          * Fires before this menu is displayed
1999          * @param {Roo.menu.Menu} this
2000          */
2001         beforeshow : true,
2002         /**
2003          * @event beforehide
2004          * Fires before this menu is hidden
2005          * @param {Roo.menu.Menu} this
2006          */
2007         beforehide : true,
2008         /**
2009          * @event show
2010          * Fires after this menu is displayed
2011          * @param {Roo.menu.Menu} this
2012          */
2013         show : true,
2014         /**
2015          * @event hide
2016          * Fires after this menu is hidden
2017          * @param {Roo.menu.Menu} this
2018          */
2019         hide : true,
2020         /**
2021          * @event click
2022          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2023          * @param {Roo.menu.Menu} this
2024          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2025          * @param {Roo.EventObject} e
2026          */
2027         click : true,
2028         /**
2029          * @event mouseover
2030          * Fires when the mouse is hovering over this menu
2031          * @param {Roo.menu.Menu} this
2032          * @param {Roo.EventObject} e
2033          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2034          */
2035         mouseover : true,
2036         /**
2037          * @event mouseout
2038          * Fires when the mouse exits this menu
2039          * @param {Roo.menu.Menu} this
2040          * @param {Roo.EventObject} e
2041          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2042          */
2043         mouseout : true,
2044         /**
2045          * @event itemclick
2046          * Fires when a menu item contained in this menu is clicked
2047          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2048          * @param {Roo.EventObject} e
2049          */
2050         itemclick: true
2051     });
2052     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2053 };
2054
2055 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2056     
2057    /// html : false,
2058     //align : '',
2059     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2060     type: false,
2061     /**
2062      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2063      */
2064     registerMenu : true,
2065     
2066     menuItems :false, // stores the menu items..
2067     
2068     hidden:true,
2069         
2070     parentMenu : false,
2071     
2072     stopEvent : true,
2073     
2074     isLink : false,
2075     
2076     getChildContainer : function() {
2077         return this.el;  
2078     },
2079     
2080     getAutoCreate : function(){
2081          
2082         //if (['right'].indexOf(this.align)!==-1) {
2083         //    cfg.cn[1].cls += ' pull-right'
2084         //}
2085         
2086         
2087         var cfg = {
2088             tag : 'ul',
2089             cls : 'dropdown-menu' ,
2090             style : 'z-index:1000'
2091             
2092         };
2093         
2094         if (this.type === 'submenu') {
2095             cfg.cls = 'submenu active';
2096         }
2097         if (this.type === 'treeview') {
2098             cfg.cls = 'treeview-menu';
2099         }
2100         
2101         return cfg;
2102     },
2103     initEvents : function() {
2104         
2105        // Roo.log("ADD event");
2106        // Roo.log(this.triggerEl.dom);
2107         
2108         this.triggerEl.on('click', this.onTriggerClick, this);
2109         
2110         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2111         
2112         this.triggerEl.addClass('dropdown-toggle');
2113         
2114         if (Roo.isTouch) {
2115             this.el.on('touchstart'  , this.onTouch, this);
2116         }
2117         this.el.on('click' , this.onClick, this);
2118
2119         this.el.on("mouseover", this.onMouseOver, this);
2120         this.el.on("mouseout", this.onMouseOut, this);
2121         
2122     },
2123     
2124     findTargetItem : function(e)
2125     {
2126         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2127         if(!t){
2128             return false;
2129         }
2130         //Roo.log(t);         Roo.log(t.id);
2131         if(t && t.id){
2132             //Roo.log(this.menuitems);
2133             return this.menuitems.get(t.id);
2134             
2135             //return this.items.get(t.menuItemId);
2136         }
2137         
2138         return false;
2139     },
2140     
2141     onTouch : function(e) 
2142     {
2143         Roo.log("menu.onTouch");
2144         //e.stopEvent(); this make the user popdown broken
2145         this.onClick(e);
2146     },
2147     
2148     onClick : function(e)
2149     {
2150         Roo.log("menu.onClick");
2151         
2152         var t = this.findTargetItem(e);
2153         if(!t || t.isContainer){
2154             return;
2155         }
2156         Roo.log(e);
2157         /*
2158         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2159             if(t == this.activeItem && t.shouldDeactivate(e)){
2160                 this.activeItem.deactivate();
2161                 delete this.activeItem;
2162                 return;
2163             }
2164             if(t.canActivate){
2165                 this.setActiveItem(t, true);
2166             }
2167             return;
2168             
2169             
2170         }
2171         */
2172        
2173         Roo.log('pass click event');
2174         
2175         t.onClick(e);
2176         
2177         this.fireEvent("click", this, t, e);
2178         
2179         var _this = this;
2180         
2181         if(!t.href.length || t.href == '#'){
2182             (function() { _this.hide(); }).defer(100);
2183         }
2184         
2185     },
2186     
2187     onMouseOver : function(e){
2188         var t  = this.findTargetItem(e);
2189         //Roo.log(t);
2190         //if(t){
2191         //    if(t.canActivate && !t.disabled){
2192         //        this.setActiveItem(t, true);
2193         //    }
2194         //}
2195         
2196         this.fireEvent("mouseover", this, e, t);
2197     },
2198     isVisible : function(){
2199         return !this.hidden;
2200     },
2201      onMouseOut : function(e){
2202         var t  = this.findTargetItem(e);
2203         
2204         //if(t ){
2205         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2206         //        this.activeItem.deactivate();
2207         //        delete this.activeItem;
2208         //    }
2209         //}
2210         this.fireEvent("mouseout", this, e, t);
2211     },
2212     
2213     
2214     /**
2215      * Displays this menu relative to another element
2216      * @param {String/HTMLElement/Roo.Element} element The element to align to
2217      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2218      * the element (defaults to this.defaultAlign)
2219      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2220      */
2221     show : function(el, pos, parentMenu){
2222         this.parentMenu = parentMenu;
2223         if(!this.el){
2224             this.render();
2225         }
2226         this.fireEvent("beforeshow", this);
2227         
2228         this.triggerEl.addClass('open');
2229         
2230         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2231     },
2232      /**
2233      * Displays this menu at a specific xy position
2234      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2235      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2236      */
2237     showAt : function(xy, parentMenu, /* private: */_e){
2238         this.parentMenu = parentMenu;
2239         if(!this.el){
2240             this.render();
2241         }
2242         if(_e !== false){
2243             this.fireEvent("beforeshow", this);
2244             // xy = this.el.adjustForConstraints(xy);
2245         }
2246         
2247         //this.el.show();
2248         this.hideMenuItems();
2249         this.hidden = false;
2250         this.triggerEl.addClass('open');
2251         
2252         // xy = this.el.getAlignToXY(this.triggerEl, '?');
2253         
2254         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255             xy[0] = xy[0] - (Roo.lib.Dom.getViewWidth() -  this.triggerEl.getBox().right); 
2256         }
2257         
2258         /*
2259         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2260             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2261         }
2262         */
2263         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2264             this.el.setXY(xy);
2265         }
2266         
2267         this.focus();
2268         this.fireEvent("show", this);
2269     },
2270     
2271     focus : function(){
2272         return;
2273         if(!this.hidden){
2274             this.doFocus.defer(50, this);
2275         }
2276     },
2277
2278     doFocus : function(){
2279         if(!this.hidden){
2280             this.focusEl.focus();
2281         }
2282     },
2283
2284     /**
2285      * Hides this menu and optionally all parent menus
2286      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2287      */
2288     hide : function(deep)
2289     {
2290         
2291         this.hideMenuItems();
2292         if(this.el && this.isVisible()){
2293             this.fireEvent("beforehide", this);
2294             if(this.activeItem){
2295                 this.activeItem.deactivate();
2296                 this.activeItem = null;
2297             }
2298             this.triggerEl.removeClass('open');;
2299             this.hidden = true;
2300             this.fireEvent("hide", this);
2301         }
2302         if(deep === true && this.parentMenu){
2303             this.parentMenu.hide(true);
2304         }
2305     },
2306     
2307     onTriggerClick : function(e)
2308     {
2309         Roo.log('trigger click');
2310         
2311         var target = e.getTarget();
2312         
2313         Roo.log(target.nodeName.toLowerCase());
2314         
2315         if(target.nodeName.toLowerCase() === 'i'){
2316             e.preventDefault();
2317         }
2318         
2319     },
2320     
2321     onTriggerPress  : function(e)
2322     {
2323         Roo.log('trigger press');
2324         //Roo.log(e.getTarget());
2325        // Roo.log(this.triggerEl.dom);
2326        
2327         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2328         var pel = Roo.get(e.getTarget());
2329         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2330             Roo.log('is treeview or dropdown?');
2331             return;
2332         }
2333         
2334         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2335             return;
2336         }
2337         
2338         if (this.isVisible()) {
2339             Roo.log('hide');
2340             this.hide();
2341         } else {
2342             Roo.log('show');
2343             this.show(this.triggerEl, false, false);
2344         }
2345         
2346         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2347             e.stopEvent();
2348         }
2349         
2350     },
2351        
2352     
2353     hideMenuItems : function()
2354     {
2355         Roo.log("hide Menu Items");
2356         if (!this.el) { 
2357             return;
2358         }
2359         //$(backdrop).remove()
2360         this.el.select('.open',true).each(function(aa) {
2361             
2362             aa.removeClass('open');
2363           //var parent = getParent($(this))
2364           //var relatedTarget = { relatedTarget: this }
2365           
2366            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2367           //if (e.isDefaultPrevented()) return
2368            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2369         });
2370     },
2371     addxtypeChild : function (tree, cntr) {
2372         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2373           
2374         this.menuitems.add(comp);
2375         return comp;
2376
2377     },
2378     getEl : function()
2379     {
2380         Roo.log(this.el);
2381         return this.el;
2382     },
2383     
2384     clear : function()
2385     {
2386         this.getEl().dom.innerHTML = '';
2387         this.menuitems.clear();
2388     }
2389 });
2390
2391  
2392  /*
2393  * - LGPL
2394  *
2395  * menu item
2396  * 
2397  */
2398
2399
2400 /**
2401  * @class Roo.bootstrap.MenuItem
2402  * @extends Roo.bootstrap.Component
2403  * Bootstrap MenuItem class
2404  * @cfg {String} html the menu label
2405  * @cfg {String} href the link
2406  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2407  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2408  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2409  * @cfg {String} fa favicon to show on left of menu item.
2410  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2411  * 
2412  * 
2413  * @constructor
2414  * Create a new MenuItem
2415  * @param {Object} config The config object
2416  */
2417
2418
2419 Roo.bootstrap.MenuItem = function(config){
2420     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2421     this.addEvents({
2422         // raw events
2423         /**
2424          * @event click
2425          * The raw click event for the entire grid.
2426          * @param {Roo.bootstrap.MenuItem} this
2427          * @param {Roo.EventObject} e
2428          */
2429         "click" : true
2430     });
2431 };
2432
2433 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2434     
2435     href : false,
2436     html : false,
2437     preventDefault: false,
2438     isContainer : false,
2439     active : false,
2440     fa: false,
2441     
2442     getAutoCreate : function(){
2443         
2444         if(this.isContainer){
2445             return {
2446                 tag: 'li',
2447                 cls: 'dropdown-menu-item'
2448             };
2449         }
2450         var ctag = {
2451             tag: 'span',
2452             html: 'Link'
2453         };
2454         
2455         var anc = {
2456             tag : 'a',
2457             href : '#',
2458             cn : [  ]
2459         };
2460         
2461         if (this.fa !== false) {
2462             anc.cn.push({
2463                 tag : 'i',
2464                 cls : 'fa fa-' + this.fa
2465             });
2466         }
2467         
2468         anc.cn.push(ctag);
2469         
2470         
2471         var cfg= {
2472             tag: 'li',
2473             cls: 'dropdown-menu-item',
2474             cn: [ anc ]
2475         };
2476         if (this.parent().type == 'treeview') {
2477             cfg.cls = 'treeview-menu';
2478         }
2479         if (this.active) {
2480             cfg.cls += ' active';
2481         }
2482         
2483         
2484         
2485         anc.href = this.href || cfg.cn[0].href ;
2486         ctag.html = this.html || cfg.cn[0].html ;
2487         return cfg;
2488     },
2489     
2490     initEvents: function()
2491     {
2492         if (this.parent().type == 'treeview') {
2493             this.el.select('a').on('click', this.onClick, this);
2494         }
2495         
2496         if (this.menu) {
2497             this.menu.parentType = this.xtype;
2498             this.menu.triggerEl = this.el;
2499             this.menu = this.addxtype(Roo.apply({}, this.menu));
2500         }
2501         
2502     },
2503     onClick : function(e)
2504     {
2505         Roo.log('item on click ');
2506         
2507         if(this.preventDefault){
2508             e.preventDefault();
2509         }
2510         //this.parent().hideMenuItems();
2511         
2512         this.fireEvent('click', this, e);
2513     },
2514     getEl : function()
2515     {
2516         return this.el;
2517     } 
2518 });
2519
2520  
2521
2522  /*
2523  * - LGPL
2524  *
2525  * menu separator
2526  * 
2527  */
2528
2529
2530 /**
2531  * @class Roo.bootstrap.MenuSeparator
2532  * @extends Roo.bootstrap.Component
2533  * Bootstrap MenuSeparator class
2534  * 
2535  * @constructor
2536  * Create a new MenuItem
2537  * @param {Object} config The config object
2538  */
2539
2540
2541 Roo.bootstrap.MenuSeparator = function(config){
2542     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2543 };
2544
2545 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2546     
2547     getAutoCreate : function(){
2548         var cfg = {
2549             cls: 'divider',
2550             tag : 'li'
2551         };
2552         
2553         return cfg;
2554     }
2555    
2556 });
2557
2558  
2559
2560  
2561 /*
2562 * Licence: LGPL
2563 */
2564
2565 /**
2566  * @class Roo.bootstrap.Modal
2567  * @extends Roo.bootstrap.Component
2568  * Bootstrap Modal class
2569  * @cfg {String} title Title of dialog
2570  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2571  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2572  * @cfg {Boolean} specificTitle default false
2573  * @cfg {Array} buttons Array of buttons or standard button set..
2574  * @cfg {String} buttonPosition (left|right|center) default right
2575  * @cfg {Boolean} animate default true
2576  * @cfg {Boolean} allow_close default true
2577  * @cfg {Boolean} fitwindow default false
2578  * @cfg {String} size (sm|lg) default empty
2579  *
2580  *
2581  * @constructor
2582  * Create a new Modal Dialog
2583  * @param {Object} config The config object
2584  */
2585
2586 Roo.bootstrap.Modal = function(config){
2587     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2588     this.addEvents({
2589         // raw events
2590         /**
2591          * @event btnclick
2592          * The raw btnclick event for the button
2593          * @param {Roo.EventObject} e
2594          */
2595         "btnclick" : true,
2596         /**
2597          * @event resize
2598          * Fire when dialog resize
2599          * @param {Roo.bootstrap.Modal} this
2600          * @param {Roo.EventObject} e
2601          */
2602         "resize" : true
2603     });
2604     this.buttons = this.buttons || [];
2605
2606     if (this.tmpl) {
2607         this.tmpl = Roo.factory(this.tmpl);
2608     }
2609
2610 };
2611
2612 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2613
2614     title : 'test dialog',
2615
2616     buttons : false,
2617
2618     // set on load...
2619
2620     html: false,
2621
2622     tmp: false,
2623
2624     specificTitle: false,
2625
2626     buttonPosition: 'right',
2627
2628     allow_close : true,
2629
2630     animate : true,
2631
2632     fitwindow: false,
2633
2634
2635      // private
2636     dialogEl: false,
2637     bodyEl:  false,
2638     footerEl:  false,
2639     titleEl:  false,
2640     closeEl:  false,
2641
2642     size: '',
2643
2644
2645     onRender : function(ct, position)
2646     {
2647         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2648
2649         if(!this.el){
2650             var cfg = Roo.apply({},  this.getAutoCreate());
2651             cfg.id = Roo.id();
2652             //if(!cfg.name){
2653             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2654             //}
2655             //if (!cfg.name.length) {
2656             //    delete cfg.name;
2657            // }
2658             if (this.cls) {
2659                 cfg.cls += ' ' + this.cls;
2660             }
2661             if (this.style) {
2662                 cfg.style = this.style;
2663             }
2664             this.el = Roo.get(document.body).createChild(cfg, position);
2665         }
2666         //var type = this.el.dom.type;
2667
2668
2669         if(this.tabIndex !== undefined){
2670             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2671         }
2672
2673         this.dialogEl = this.el.select('.modal-dialog',true).first();
2674         this.bodyEl = this.el.select('.modal-body',true).first();
2675         this.closeEl = this.el.select('.modal-header .close', true).first();
2676         this.headerEl = this.el.select('.modal-header',true).first();
2677         this.titleEl = this.el.select('.modal-title',true).first();
2678         this.footerEl = this.el.select('.modal-footer',true).first();
2679
2680         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2681         
2682         //this.el.addClass("x-dlg-modal");
2683
2684         if (this.buttons.length) {
2685             Roo.each(this.buttons, function(bb) {
2686                 var b = Roo.apply({}, bb);
2687                 b.xns = b.xns || Roo.bootstrap;
2688                 b.xtype = b.xtype || 'Button';
2689                 if (typeof(b.listeners) == 'undefined') {
2690                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2691                 }
2692
2693                 var btn = Roo.factory(b);
2694
2695                 btn.render(this.el.select('.modal-footer div').first());
2696
2697             },this);
2698         }
2699         // render the children.
2700         var nitems = [];
2701
2702         if(typeof(this.items) != 'undefined'){
2703             var items = this.items;
2704             delete this.items;
2705
2706             for(var i =0;i < items.length;i++) {
2707                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2708             }
2709         }
2710
2711         this.items = nitems;
2712
2713         // where are these used - they used to be body/close/footer
2714
2715
2716         this.initEvents();
2717         //this.el.addClass([this.fieldClass, this.cls]);
2718
2719     },
2720
2721     getAutoCreate : function(){
2722
2723
2724         var bdy = {
2725                 cls : 'modal-body',
2726                 html : this.html || ''
2727         };
2728
2729         var title = {
2730             tag: 'h4',
2731             cls : 'modal-title',
2732             html : this.title
2733         };
2734
2735         if(this.specificTitle){
2736             title = this.title;
2737
2738         };
2739
2740         var header = [];
2741         if (this.allow_close) {
2742             header.push({
2743                 tag: 'button',
2744                 cls : 'close',
2745                 html : '&times'
2746             });
2747         }
2748
2749         header.push(title);
2750
2751         var size = '';
2752
2753         if(this.size.length){
2754             size = 'modal-' + this.size;
2755         }
2756
2757         var modal = {
2758             cls: "modal",
2759              cn : [
2760                 {
2761                     cls: "modal-dialog " + size,
2762                     cn : [
2763                         {
2764                             cls : "modal-content",
2765                             cn : [
2766                                 {
2767                                     cls : 'modal-header',
2768                                     cn : header
2769                                 },
2770                                 bdy,
2771                                 {
2772                                     cls : 'modal-footer',
2773                                     cn : [
2774                                         {
2775                                             tag: 'div',
2776                                             cls: 'btn-' + this.buttonPosition
2777                                         }
2778                                     ]
2779
2780                                 }
2781
2782
2783                             ]
2784
2785                         }
2786                     ]
2787
2788                 }
2789             ]
2790         };
2791
2792         if(this.animate){
2793             modal.cls += ' fade';
2794         }
2795
2796         return modal;
2797
2798     },
2799     getChildContainer : function() {
2800
2801          return this.bodyEl;
2802
2803     },
2804     getButtonContainer : function() {
2805          return this.el.select('.modal-footer div',true).first();
2806
2807     },
2808     initEvents : function()
2809     {
2810         if (this.allow_close) {
2811             this.closeEl.on('click', this.hide, this);
2812         }
2813         Roo.EventManager.onWindowResize(this.resize, this, true);
2814
2815
2816     },
2817
2818     resize : function()
2819     {
2820         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2821         if (this.fitwindow) {
2822             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2823             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2824             this.setSize(w,h);
2825         }
2826     },
2827
2828     setSize : function(w,h)
2829     {
2830         if (!w && !h) {
2831             return;
2832         }
2833         this.resizeTo(w,h);
2834     },
2835
2836     show : function() {
2837
2838         if (!this.rendered) {
2839             this.render();
2840         }
2841
2842         //this.el.setStyle('display', 'block');
2843         this.el.removeClass('hideing');        
2844         this.el.addClass('show');
2845  
2846         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2847             var _this = this;
2848             (function(){
2849                 this.el.addClass('in');
2850             }).defer(50, this);
2851         }else{
2852             this.el.addClass('in');
2853
2854         }
2855
2856         // not sure how we can show data in here..
2857         //if (this.tmpl) {
2858         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2859         //}
2860
2861         Roo.get(document.body).addClass("x-body-masked");
2862         
2863         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2864         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2865         this.maskEl.addClass('show');
2866         
2867         this.resize();
2868         
2869         this.fireEvent('show', this);
2870
2871         // set zindex here - otherwise it appears to be ignored...
2872         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2873
2874         (function () {
2875             this.items.forEach( function(e) {
2876                 e.layout ? e.layout() : false;
2877
2878             });
2879         }).defer(100,this);
2880
2881     },
2882     hide : function()
2883     {
2884         if(this.fireEvent("beforehide", this) !== false){
2885             this.maskEl.removeClass('show');
2886             Roo.get(document.body).removeClass("x-body-masked");
2887             this.el.removeClass('in');
2888             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2889
2890             if(this.animate){ // why
2891                 this.el.addClass('hideing');
2892                 (function(){
2893                     if (!this.el.hasClass('hideing')) {
2894                         return; // it's been shown again...
2895                     }
2896                     this.el.removeClass('show');
2897                     this.el.removeClass('hideing');
2898                 }).defer(150,this);
2899                 
2900             }else{
2901                  this.el.removeClass('show');
2902             }
2903             this.fireEvent('hide', this);
2904         }
2905     },
2906     isVisible : function()
2907     {
2908         
2909         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2910         
2911     },
2912
2913     addButton : function(str, cb)
2914     {
2915
2916
2917         var b = Roo.apply({}, { html : str } );
2918         b.xns = b.xns || Roo.bootstrap;
2919         b.xtype = b.xtype || 'Button';
2920         if (typeof(b.listeners) == 'undefined') {
2921             b.listeners = { click : cb.createDelegate(this)  };
2922         }
2923
2924         var btn = Roo.factory(b);
2925
2926         btn.render(this.el.select('.modal-footer div').first());
2927
2928         return btn;
2929
2930     },
2931
2932     setDefaultButton : function(btn)
2933     {
2934         //this.el.select('.modal-footer').()
2935     },
2936     diff : false,
2937
2938     resizeTo: function(w,h)
2939     {
2940         // skip.. ?? why??
2941
2942         this.dialogEl.setWidth(w);
2943         if (this.diff === false) {
2944             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2945         }
2946
2947         this.bodyEl.setHeight(h-this.diff);
2948
2949         this.fireEvent('resize', this);
2950
2951     },
2952     setContentSize  : function(w, h)
2953     {
2954
2955     },
2956     onButtonClick: function(btn,e)
2957     {
2958         //Roo.log([a,b,c]);
2959         this.fireEvent('btnclick', btn.name, e);
2960     },
2961      /**
2962      * Set the title of the Dialog
2963      * @param {String} str new Title
2964      */
2965     setTitle: function(str) {
2966         this.titleEl.dom.innerHTML = str;
2967     },
2968     /**
2969      * Set the body of the Dialog
2970      * @param {String} str new Title
2971      */
2972     setBody: function(str) {
2973         this.bodyEl.dom.innerHTML = str;
2974     },
2975     /**
2976      * Set the body of the Dialog using the template
2977      * @param {Obj} data - apply this data to the template and replace the body contents.
2978      */
2979     applyBody: function(obj)
2980     {
2981         if (!this.tmpl) {
2982             Roo.log("Error - using apply Body without a template");
2983             //code
2984         }
2985         this.tmpl.overwrite(this.bodyEl, obj);
2986     }
2987
2988 });
2989
2990
2991 Roo.apply(Roo.bootstrap.Modal,  {
2992     /**
2993          * Button config that displays a single OK button
2994          * @type Object
2995          */
2996         OK :  [{
2997             name : 'ok',
2998             weight : 'primary',
2999             html : 'OK'
3000         }],
3001         /**
3002          * Button config that displays Yes and No buttons
3003          * @type Object
3004          */
3005         YESNO : [
3006             {
3007                 name  : 'no',
3008                 html : 'No'
3009             },
3010             {
3011                 name  :'yes',
3012                 weight : 'primary',
3013                 html : 'Yes'
3014             }
3015         ],
3016
3017         /**
3018          * Button config that displays OK and Cancel buttons
3019          * @type Object
3020          */
3021         OKCANCEL : [
3022             {
3023                name : 'cancel',
3024                 html : 'Cancel'
3025             },
3026             {
3027                 name : 'ok',
3028                 weight : 'primary',
3029                 html : 'OK'
3030             }
3031         ],
3032         /**
3033          * Button config that displays Yes, No and Cancel buttons
3034          * @type Object
3035          */
3036         YESNOCANCEL : [
3037             {
3038                 name : 'yes',
3039                 weight : 'primary',
3040                 html : 'Yes'
3041             },
3042             {
3043                 name : 'no',
3044                 html : 'No'
3045             },
3046             {
3047                 name : 'cancel',
3048                 html : 'Cancel'
3049             }
3050         ],
3051         
3052         zIndex : 10001
3053 });
3054 /*
3055  * - LGPL
3056  *
3057  * messagebox - can be used as a replace
3058  * 
3059  */
3060 /**
3061  * @class Roo.MessageBox
3062  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3063  * Example usage:
3064  *<pre><code>
3065 // Basic alert:
3066 Roo.Msg.alert('Status', 'Changes saved successfully.');
3067
3068 // Prompt for user data:
3069 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3070     if (btn == 'ok'){
3071         // process text value...
3072     }
3073 });
3074
3075 // Show a dialog using config options:
3076 Roo.Msg.show({
3077    title:'Save Changes?',
3078    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3079    buttons: Roo.Msg.YESNOCANCEL,
3080    fn: processResult,
3081    animEl: 'elId'
3082 });
3083 </code></pre>
3084  * @singleton
3085  */
3086 Roo.bootstrap.MessageBox = function(){
3087     var dlg, opt, mask, waitTimer;
3088     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3089     var buttons, activeTextEl, bwidth;
3090
3091     
3092     // private
3093     var handleButton = function(button){
3094         dlg.hide();
3095         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3096     };
3097
3098     // private
3099     var handleHide = function(){
3100         if(opt && opt.cls){
3101             dlg.el.removeClass(opt.cls);
3102         }
3103         //if(waitTimer){
3104         //    Roo.TaskMgr.stop(waitTimer);
3105         //    waitTimer = null;
3106         //}
3107     };
3108
3109     // private
3110     var updateButtons = function(b){
3111         var width = 0;
3112         if(!b){
3113             buttons["ok"].hide();
3114             buttons["cancel"].hide();
3115             buttons["yes"].hide();
3116             buttons["no"].hide();
3117             //dlg.footer.dom.style.display = 'none';
3118             return width;
3119         }
3120         dlg.footerEl.dom.style.display = '';
3121         for(var k in buttons){
3122             if(typeof buttons[k] != "function"){
3123                 if(b[k]){
3124                     buttons[k].show();
3125                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3126                     width += buttons[k].el.getWidth()+15;
3127                 }else{
3128                     buttons[k].hide();
3129                 }
3130             }
3131         }
3132         return width;
3133     };
3134
3135     // private
3136     var handleEsc = function(d, k, e){
3137         if(opt && opt.closable !== false){
3138             dlg.hide();
3139         }
3140         if(e){
3141             e.stopEvent();
3142         }
3143     };
3144
3145     return {
3146         /**
3147          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3148          * @return {Roo.BasicDialog} The BasicDialog element
3149          */
3150         getDialog : function(){
3151            if(!dlg){
3152                 dlg = new Roo.bootstrap.Modal( {
3153                     //draggable: true,
3154                     //resizable:false,
3155                     //constraintoviewport:false,
3156                     //fixedcenter:true,
3157                     //collapsible : false,
3158                     //shim:true,
3159                     //modal: true,
3160                 //    width: 'auto',
3161                   //  height:100,
3162                     //buttonAlign:"center",
3163                     closeClick : function(){
3164                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3165                             handleButton("no");
3166                         }else{
3167                             handleButton("cancel");
3168                         }
3169                     }
3170                 });
3171                 dlg.render();
3172                 dlg.on("hide", handleHide);
3173                 mask = dlg.mask;
3174                 //dlg.addKeyListener(27, handleEsc);
3175                 buttons = {};
3176                 this.buttons = buttons;
3177                 var bt = this.buttonText;
3178                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3179                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3180                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3181                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3182                 //Roo.log(buttons);
3183                 bodyEl = dlg.bodyEl.createChild({
3184
3185                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3186                         '<textarea class="roo-mb-textarea"></textarea>' +
3187                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3188                 });
3189                 msgEl = bodyEl.dom.firstChild;
3190                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3191                 textboxEl.enableDisplayMode();
3192                 textboxEl.addKeyListener([10,13], function(){
3193                     if(dlg.isVisible() && opt && opt.buttons){
3194                         if(opt.buttons.ok){
3195                             handleButton("ok");
3196                         }else if(opt.buttons.yes){
3197                             handleButton("yes");
3198                         }
3199                     }
3200                 });
3201                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3202                 textareaEl.enableDisplayMode();
3203                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3204                 progressEl.enableDisplayMode();
3205                 
3206                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3207                 var pf = progressEl.dom.firstChild;
3208                 if (pf) {
3209                     pp = Roo.get(pf.firstChild);
3210                     pp.setHeight(pf.offsetHeight);
3211                 }
3212                 
3213             }
3214             return dlg;
3215         },
3216
3217         /**
3218          * Updates the message box body text
3219          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3220          * the XHTML-compliant non-breaking space character '&amp;#160;')
3221          * @return {Roo.MessageBox} This message box
3222          */
3223         updateText : function(text)
3224         {
3225             if(!dlg.isVisible() && !opt.width){
3226                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3227                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3228             }
3229             msgEl.innerHTML = text || '&#160;';
3230       
3231             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3232             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3233             var w = Math.max(
3234                     Math.min(opt.width || cw , this.maxWidth), 
3235                     Math.max(opt.minWidth || this.minWidth, bwidth)
3236             );
3237             if(opt.prompt){
3238                 activeTextEl.setWidth(w);
3239             }
3240             if(dlg.isVisible()){
3241                 dlg.fixedcenter = false;
3242             }
3243             // to big, make it scroll. = But as usual stupid IE does not support
3244             // !important..
3245             
3246             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3247                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3248                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3249             } else {
3250                 bodyEl.dom.style.height = '';
3251                 bodyEl.dom.style.overflowY = '';
3252             }
3253             if (cw > w) {
3254                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3255             } else {
3256                 bodyEl.dom.style.overflowX = '';
3257             }
3258             
3259             dlg.setContentSize(w, bodyEl.getHeight());
3260             if(dlg.isVisible()){
3261                 dlg.fixedcenter = true;
3262             }
3263             return this;
3264         },
3265
3266         /**
3267          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3268          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3269          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3270          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3271          * @return {Roo.MessageBox} This message box
3272          */
3273         updateProgress : function(value, text){
3274             if(text){
3275                 this.updateText(text);
3276             }
3277             
3278             if (pp) { // weird bug on my firefox - for some reason this is not defined
3279                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3280                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3281             }
3282             return this;
3283         },        
3284
3285         /**
3286          * Returns true if the message box is currently displayed
3287          * @return {Boolean} True if the message box is visible, else false
3288          */
3289         isVisible : function(){
3290             return dlg && dlg.isVisible();  
3291         },
3292
3293         /**
3294          * Hides the message box if it is displayed
3295          */
3296         hide : function(){
3297             if(this.isVisible()){
3298                 dlg.hide();
3299             }  
3300         },
3301
3302         /**
3303          * Displays a new message box, or reinitializes an existing message box, based on the config options
3304          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3305          * The following config object properties are supported:
3306          * <pre>
3307 Property    Type             Description
3308 ----------  ---------------  ------------------------------------------------------------------------------------
3309 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3310                                    closes (defaults to undefined)
3311 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3312                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3313 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3314                                    progress and wait dialogs will ignore this property and always hide the
3315                                    close button as they can only be closed programmatically.
3316 cls               String           A custom CSS class to apply to the message box element
3317 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3318                                    displayed (defaults to 75)
3319 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3320                                    function will be btn (the name of the button that was clicked, if applicable,
3321                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3322                                    Progress and wait dialogs will ignore this option since they do not respond to
3323                                    user actions and can only be closed programmatically, so any required function
3324                                    should be called by the same code after it closes the dialog.
3325 icon              String           A CSS class that provides a background image to be used as an icon for
3326                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3327 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3328 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3329 modal             Boolean          False to allow user interaction with the page while the message box is
3330                                    displayed (defaults to true)
3331 msg               String           A string that will replace the existing message box body text (defaults
3332                                    to the XHTML-compliant non-breaking space character '&#160;')
3333 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3334 progress          Boolean          True to display a progress bar (defaults to false)
3335 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3336 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3337 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3338 title             String           The title text
3339 value             String           The string value to set into the active textbox element if displayed
3340 wait              Boolean          True to display a progress bar (defaults to false)
3341 width             Number           The width of the dialog in pixels
3342 </pre>
3343          *
3344          * Example usage:
3345          * <pre><code>
3346 Roo.Msg.show({
3347    title: 'Address',
3348    msg: 'Please enter your address:',
3349    width: 300,
3350    buttons: Roo.MessageBox.OKCANCEL,
3351    multiline: true,
3352    fn: saveAddress,
3353    animEl: 'addAddressBtn'
3354 });
3355 </code></pre>
3356          * @param {Object} config Configuration options
3357          * @return {Roo.MessageBox} This message box
3358          */
3359         show : function(options)
3360         {
3361             
3362             // this causes nightmares if you show one dialog after another
3363             // especially on callbacks..
3364              
3365             if(this.isVisible()){
3366                 
3367                 this.hide();
3368                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3369                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3370                 Roo.log("New Dialog Message:" +  options.msg )
3371                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3372                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3373                 
3374             }
3375             var d = this.getDialog();
3376             opt = options;
3377             d.setTitle(opt.title || "&#160;");
3378             d.closeEl.setDisplayed(opt.closable !== false);
3379             activeTextEl = textboxEl;
3380             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3381             if(opt.prompt){
3382                 if(opt.multiline){
3383                     textboxEl.hide();
3384                     textareaEl.show();
3385                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3386                         opt.multiline : this.defaultTextHeight);
3387                     activeTextEl = textareaEl;
3388                 }else{
3389                     textboxEl.show();
3390                     textareaEl.hide();
3391                 }
3392             }else{
3393                 textboxEl.hide();
3394                 textareaEl.hide();
3395             }
3396             progressEl.setDisplayed(opt.progress === true);
3397             this.updateProgress(0);
3398             activeTextEl.dom.value = opt.value || "";
3399             if(opt.prompt){
3400                 dlg.setDefaultButton(activeTextEl);
3401             }else{
3402                 var bs = opt.buttons;
3403                 var db = null;
3404                 if(bs && bs.ok){
3405                     db = buttons["ok"];
3406                 }else if(bs && bs.yes){
3407                     db = buttons["yes"];
3408                 }
3409                 dlg.setDefaultButton(db);
3410             }
3411             bwidth = updateButtons(opt.buttons);
3412             this.updateText(opt.msg);
3413             if(opt.cls){
3414                 d.el.addClass(opt.cls);
3415             }
3416             d.proxyDrag = opt.proxyDrag === true;
3417             d.modal = opt.modal !== false;
3418             d.mask = opt.modal !== false ? mask : false;
3419             if(!d.isVisible()){
3420                 // force it to the end of the z-index stack so it gets a cursor in FF
3421                 document.body.appendChild(dlg.el.dom);
3422                 d.animateTarget = null;
3423                 d.show(options.animEl);
3424             }
3425             return this;
3426         },
3427
3428         /**
3429          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3430          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3431          * and closing the message box when the process is complete.
3432          * @param {String} title The title bar text
3433          * @param {String} msg The message box body text
3434          * @return {Roo.MessageBox} This message box
3435          */
3436         progress : function(title, msg){
3437             this.show({
3438                 title : title,
3439                 msg : msg,
3440                 buttons: false,
3441                 progress:true,
3442                 closable:false,
3443                 minWidth: this.minProgressWidth,
3444                 modal : true
3445             });
3446             return this;
3447         },
3448
3449         /**
3450          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3451          * If a callback function is passed it will be called after the user clicks the button, and the
3452          * id of the button that was clicked will be passed as the only parameter to the callback
3453          * (could also be the top-right close button).
3454          * @param {String} title The title bar text
3455          * @param {String} msg The message box body text
3456          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3457          * @param {Object} scope (optional) The scope of the callback function
3458          * @return {Roo.MessageBox} This message box
3459          */
3460         alert : function(title, msg, fn, scope)
3461         {
3462             this.show({
3463                 title : title,
3464                 msg : msg,
3465                 buttons: this.OK,
3466                 fn: fn,
3467                 closable : false,
3468                 scope : scope,
3469                 modal : true
3470             });
3471             return this;
3472         },
3473
3474         /**
3475          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3476          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3477          * You are responsible for closing the message box when the process is complete.
3478          * @param {String} msg The message box body text
3479          * @param {String} title (optional) The title bar text
3480          * @return {Roo.MessageBox} This message box
3481          */
3482         wait : function(msg, title){
3483             this.show({
3484                 title : title,
3485                 msg : msg,
3486                 buttons: false,
3487                 closable:false,
3488                 progress:true,
3489                 modal:true,
3490                 width:300,
3491                 wait:true
3492             });
3493             waitTimer = Roo.TaskMgr.start({
3494                 run: function(i){
3495                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3496                 },
3497                 interval: 1000
3498             });
3499             return this;
3500         },
3501
3502         /**
3503          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3504          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3505          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3506          * @param {String} title The title bar text
3507          * @param {String} msg The message box body text
3508          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3509          * @param {Object} scope (optional) The scope of the callback function
3510          * @return {Roo.MessageBox} This message box
3511          */
3512         confirm : function(title, msg, fn, scope){
3513             this.show({
3514                 title : title,
3515                 msg : msg,
3516                 buttons: this.YESNO,
3517                 fn: fn,
3518                 scope : scope,
3519                 modal : true
3520             });
3521             return this;
3522         },
3523
3524         /**
3525          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3526          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3527          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3528          * (could also be the top-right close button) and the text that was entered will be passed as the two
3529          * parameters to the callback.
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          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3535          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3536          * @return {Roo.MessageBox} This message box
3537          */
3538         prompt : function(title, msg, fn, scope, multiline){
3539             this.show({
3540                 title : title,
3541                 msg : msg,
3542                 buttons: this.OKCANCEL,
3543                 fn: fn,
3544                 minWidth:250,
3545                 scope : scope,
3546                 prompt:true,
3547                 multiline: multiline,
3548                 modal : true
3549             });
3550             return this;
3551         },
3552
3553         /**
3554          * Button config that displays a single OK button
3555          * @type Object
3556          */
3557         OK : {ok:true},
3558         /**
3559          * Button config that displays Yes and No buttons
3560          * @type Object
3561          */
3562         YESNO : {yes:true, no:true},
3563         /**
3564          * Button config that displays OK and Cancel buttons
3565          * @type Object
3566          */
3567         OKCANCEL : {ok:true, cancel:true},
3568         /**
3569          * Button config that displays Yes, No and Cancel buttons
3570          * @type Object
3571          */
3572         YESNOCANCEL : {yes:true, no:true, cancel:true},
3573
3574         /**
3575          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3576          * @type Number
3577          */
3578         defaultTextHeight : 75,
3579         /**
3580          * The maximum width in pixels of the message box (defaults to 600)
3581          * @type Number
3582          */
3583         maxWidth : 600,
3584         /**
3585          * The minimum width in pixels of the message box (defaults to 100)
3586          * @type Number
3587          */
3588         minWidth : 100,
3589         /**
3590          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3591          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3592          * @type Number
3593          */
3594         minProgressWidth : 250,
3595         /**
3596          * An object containing the default button text strings that can be overriden for localized language support.
3597          * Supported properties are: ok, cancel, yes and no.
3598          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3599          * @type Object
3600          */
3601         buttonText : {
3602             ok : "OK",
3603             cancel : "Cancel",
3604             yes : "Yes",
3605             no : "No"
3606         }
3607     };
3608 }();
3609
3610 /**
3611  * Shorthand for {@link Roo.MessageBox}
3612  */
3613 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3614 Roo.Msg = Roo.Msg || Roo.MessageBox;
3615 /*
3616  * - LGPL
3617  *
3618  * navbar
3619  * 
3620  */
3621
3622 /**
3623  * @class Roo.bootstrap.Navbar
3624  * @extends Roo.bootstrap.Component
3625  * Bootstrap Navbar class
3626
3627  * @constructor
3628  * Create a new Navbar
3629  * @param {Object} config The config object
3630  */
3631
3632
3633 Roo.bootstrap.Navbar = function(config){
3634     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3635     this.addEvents({
3636         // raw events
3637         /**
3638          * @event beforetoggle
3639          * Fire before toggle the menu
3640          * @param {Roo.EventObject} e
3641          */
3642         "beforetoggle" : true
3643     });
3644 };
3645
3646 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3647     
3648     
3649    
3650     // private
3651     navItems : false,
3652     loadMask : false,
3653     
3654     
3655     getAutoCreate : function(){
3656         
3657         
3658         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3659         
3660     },
3661     
3662     initEvents :function ()
3663     {
3664         //Roo.log(this.el.select('.navbar-toggle',true));
3665         this.el.select('.navbar-toggle',true).on('click', function() {
3666             if(this.fireEvent('beforetoggle', this) !== false){
3667                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3668             }
3669             
3670         }, this);
3671         
3672         var mark = {
3673             tag: "div",
3674             cls:"x-dlg-mask"
3675         };
3676         
3677         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3678         
3679         var size = this.el.getSize();
3680         this.maskEl.setSize(size.width, size.height);
3681         this.maskEl.enableDisplayMode("block");
3682         this.maskEl.hide();
3683         
3684         if(this.loadMask){
3685             this.maskEl.show();
3686         }
3687     },
3688     
3689     
3690     getChildContainer : function()
3691     {
3692         if (this.el.select('.collapse').getCount()) {
3693             return this.el.select('.collapse',true).first();
3694         }
3695         
3696         return this.el;
3697     },
3698     
3699     mask : function()
3700     {
3701         this.maskEl.show();
3702     },
3703     
3704     unmask : function()
3705     {
3706         this.maskEl.hide();
3707     } 
3708     
3709     
3710     
3711     
3712 });
3713
3714
3715
3716  
3717
3718  /*
3719  * - LGPL
3720  *
3721  * navbar
3722  * 
3723  */
3724
3725 /**
3726  * @class Roo.bootstrap.NavSimplebar
3727  * @extends Roo.bootstrap.Navbar
3728  * Bootstrap Sidebar class
3729  *
3730  * @cfg {Boolean} inverse is inverted color
3731  * 
3732  * @cfg {String} type (nav | pills | tabs)
3733  * @cfg {Boolean} arrangement stacked | justified
3734  * @cfg {String} align (left | right) alignment
3735  * 
3736  * @cfg {Boolean} main (true|false) main nav bar? default false
3737  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3738  * 
3739  * @cfg {String} tag (header|footer|nav|div) default is nav 
3740
3741  * 
3742  * 
3743  * 
3744  * @constructor
3745  * Create a new Sidebar
3746  * @param {Object} config The config object
3747  */
3748
3749
3750 Roo.bootstrap.NavSimplebar = function(config){
3751     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3752 };
3753
3754 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3755     
3756     inverse: false,
3757     
3758     type: false,
3759     arrangement: '',
3760     align : false,
3761     
3762     
3763     
3764     main : false,
3765     
3766     
3767     tag : false,
3768     
3769     
3770     getAutoCreate : function(){
3771         
3772         
3773         var cfg = {
3774             tag : this.tag || 'div',
3775             cls : 'navbar'
3776         };
3777           
3778         
3779         cfg.cn = [
3780             {
3781                 cls: 'nav',
3782                 tag : 'ul'
3783             }
3784         ];
3785         
3786          
3787         this.type = this.type || 'nav';
3788         if (['tabs','pills'].indexOf(this.type)!==-1) {
3789             cfg.cn[0].cls += ' nav-' + this.type
3790         
3791         
3792         } else {
3793             if (this.type!=='nav') {
3794                 Roo.log('nav type must be nav/tabs/pills')
3795             }
3796             cfg.cn[0].cls += ' navbar-nav'
3797         }
3798         
3799         
3800         
3801         
3802         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3803             cfg.cn[0].cls += ' nav-' + this.arrangement;
3804         }
3805         
3806         
3807         if (this.align === 'right') {
3808             cfg.cn[0].cls += ' navbar-right';
3809         }
3810         
3811         if (this.inverse) {
3812             cfg.cls += ' navbar-inverse';
3813             
3814         }
3815         
3816         
3817         return cfg;
3818     
3819         
3820     }
3821     
3822     
3823     
3824 });
3825
3826
3827
3828  
3829
3830  
3831        /*
3832  * - LGPL
3833  *
3834  * navbar
3835  * 
3836  */
3837
3838 /**
3839  * @class Roo.bootstrap.NavHeaderbar
3840  * @extends Roo.bootstrap.NavSimplebar
3841  * Bootstrap Sidebar class
3842  *
3843  * @cfg {String} brand what is brand
3844  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3845  * @cfg {String} brand_href href of the brand
3846  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3847  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3848  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3849  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3850  * 
3851  * @constructor
3852  * Create a new Sidebar
3853  * @param {Object} config The config object
3854  */
3855
3856
3857 Roo.bootstrap.NavHeaderbar = function(config){
3858     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3859       
3860 };
3861
3862 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3863     
3864     position: '',
3865     brand: '',
3866     brand_href: false,
3867     srButton : true,
3868     autohide : false,
3869     desktopCenter : false,
3870    
3871     
3872     getAutoCreate : function(){
3873         
3874         var   cfg = {
3875             tag: this.nav || 'nav',
3876             cls: 'navbar',
3877             role: 'navigation',
3878             cn: []
3879         };
3880         
3881         var cn = cfg.cn;
3882         if (this.desktopCenter) {
3883             cn.push({cls : 'container', cn : []});
3884             cn = cn[0].cn;
3885         }
3886         
3887         if(this.srButton){
3888             cn.push({
3889                 tag: 'div',
3890                 cls: 'navbar-header',
3891                 cn: [
3892                     {
3893                         tag: 'button',
3894                         type: 'button',
3895                         cls: 'navbar-toggle',
3896                         'data-toggle': 'collapse',
3897                         cn: [
3898                             {
3899                                 tag: 'span',
3900                                 cls: 'sr-only',
3901                                 html: 'Toggle navigation'
3902                             },
3903                             {
3904                                 tag: 'span',
3905                                 cls: 'icon-bar'
3906                             },
3907                             {
3908                                 tag: 'span',
3909                                 cls: 'icon-bar'
3910                             },
3911                             {
3912                                 tag: 'span',
3913                                 cls: 'icon-bar'
3914                             }
3915                         ]
3916                     }
3917                 ]
3918             });
3919         }
3920         
3921         cn.push({
3922             tag: 'div',
3923             cls: 'collapse navbar-collapse',
3924             cn : []
3925         });
3926         
3927         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3928         
3929         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3930             cfg.cls += ' navbar-' + this.position;
3931             
3932             // tag can override this..
3933             
3934             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3935         }
3936         
3937         if (this.brand !== '') {
3938             cn[0].cn.push({
3939                 tag: 'a',
3940                 href: this.brand_href ? this.brand_href : '#',
3941                 cls: 'navbar-brand',
3942                 cn: [
3943                 this.brand
3944                 ]
3945             });
3946         }
3947         
3948         if(this.main){
3949             cfg.cls += ' main-nav';
3950         }
3951         
3952         
3953         return cfg;
3954
3955         
3956     },
3957     getHeaderChildContainer : function()
3958     {
3959         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3960             return this.el.select('.navbar-header',true).first();
3961         }
3962         
3963         return this.getChildContainer();
3964     },
3965     
3966     
3967     initEvents : function()
3968     {
3969         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3970         
3971         if (this.autohide) {
3972             
3973             var prevScroll = 0;
3974             var ft = this.el;
3975             
3976             Roo.get(document).on('scroll',function(e) {
3977                 var ns = Roo.get(document).getScroll().top;
3978                 var os = prevScroll;
3979                 prevScroll = ns;
3980                 
3981                 if(ns > os){
3982                     ft.removeClass('slideDown');
3983                     ft.addClass('slideUp');
3984                     return;
3985                 }
3986                 ft.removeClass('slideUp');
3987                 ft.addClass('slideDown');
3988                  
3989               
3990           },this);
3991         }
3992     }    
3993     
3994 });
3995
3996
3997
3998  
3999
4000  /*
4001  * - LGPL
4002  *
4003  * navbar
4004  * 
4005  */
4006
4007 /**
4008  * @class Roo.bootstrap.NavSidebar
4009  * @extends Roo.bootstrap.Navbar
4010  * Bootstrap Sidebar class
4011  * 
4012  * @constructor
4013  * Create a new Sidebar
4014  * @param {Object} config The config object
4015  */
4016
4017
4018 Roo.bootstrap.NavSidebar = function(config){
4019     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4020 };
4021
4022 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4023     
4024     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4025     
4026     getAutoCreate : function(){
4027         
4028         
4029         return  {
4030             tag: 'div',
4031             cls: 'sidebar sidebar-nav'
4032         };
4033     
4034         
4035     }
4036     
4037     
4038     
4039 });
4040
4041
4042
4043  
4044
4045  /*
4046  * - LGPL
4047  *
4048  * nav group
4049  * 
4050  */
4051
4052 /**
4053  * @class Roo.bootstrap.NavGroup
4054  * @extends Roo.bootstrap.Component
4055  * Bootstrap NavGroup class
4056  * @cfg {String} align (left|right)
4057  * @cfg {Boolean} inverse
4058  * @cfg {String} type (nav|pills|tab) default nav
4059  * @cfg {String} navId - reference Id for navbar.
4060
4061  * 
4062  * @constructor
4063  * Create a new nav group
4064  * @param {Object} config The config object
4065  */
4066
4067 Roo.bootstrap.NavGroup = function(config){
4068     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4069     this.navItems = [];
4070    
4071     Roo.bootstrap.NavGroup.register(this);
4072      this.addEvents({
4073         /**
4074              * @event changed
4075              * Fires when the active item changes
4076              * @param {Roo.bootstrap.NavGroup} this
4077              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4078              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4079          */
4080         'changed': true
4081      });
4082     
4083 };
4084
4085 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4086     
4087     align: '',
4088     inverse: false,
4089     form: false,
4090     type: 'nav',
4091     navId : '',
4092     // private
4093     
4094     navItems : false, 
4095     
4096     getAutoCreate : function()
4097     {
4098         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4099         
4100         cfg = {
4101             tag : 'ul',
4102             cls: 'nav' 
4103         };
4104         
4105         if (['tabs','pills'].indexOf(this.type)!==-1) {
4106             cfg.cls += ' nav-' + this.type
4107         } else {
4108             if (this.type!=='nav') {
4109                 Roo.log('nav type must be nav/tabs/pills')
4110             }
4111             cfg.cls += ' navbar-nav'
4112         }
4113         
4114         if (this.parent() && this.parent().sidebar) {
4115             cfg = {
4116                 tag: 'ul',
4117                 cls: 'dashboard-menu sidebar-menu'
4118             };
4119             
4120             return cfg;
4121         }
4122         
4123         if (this.form === true) {
4124             cfg = {
4125                 tag: 'form',
4126                 cls: 'navbar-form'
4127             };
4128             
4129             if (this.align === 'right') {
4130                 cfg.cls += ' navbar-right';
4131             } else {
4132                 cfg.cls += ' navbar-left';
4133             }
4134         }
4135         
4136         if (this.align === 'right') {
4137             cfg.cls += ' navbar-right';
4138         }
4139         
4140         if (this.inverse) {
4141             cfg.cls += ' navbar-inverse';
4142             
4143         }
4144         
4145         
4146         return cfg;
4147     },
4148     /**
4149     * sets the active Navigation item
4150     * @param {Roo.bootstrap.NavItem} the new current navitem
4151     */
4152     setActiveItem : function(item)
4153     {
4154         var prev = false;
4155         Roo.each(this.navItems, function(v){
4156             if (v == item) {
4157                 return ;
4158             }
4159             if (v.isActive()) {
4160                 v.setActive(false, true);
4161                 prev = v;
4162                 
4163             }
4164             
4165         });
4166
4167         item.setActive(true, true);
4168         this.fireEvent('changed', this, item, prev);
4169         
4170         
4171     },
4172     /**
4173     * gets the active Navigation item
4174     * @return {Roo.bootstrap.NavItem} the current navitem
4175     */
4176     getActive : function()
4177     {
4178         
4179         var prev = false;
4180         Roo.each(this.navItems, function(v){
4181             
4182             if (v.isActive()) {
4183                 prev = v;
4184                 
4185             }
4186             
4187         });
4188         return prev;
4189     },
4190     
4191     indexOfNav : function()
4192     {
4193         
4194         var prev = false;
4195         Roo.each(this.navItems, function(v,i){
4196             
4197             if (v.isActive()) {
4198                 prev = i;
4199                 
4200             }
4201             
4202         });
4203         return prev;
4204     },
4205     /**
4206     * adds a Navigation item
4207     * @param {Roo.bootstrap.NavItem} the navitem to add
4208     */
4209     addItem : function(cfg)
4210     {
4211         var cn = new Roo.bootstrap.NavItem(cfg);
4212         this.register(cn);
4213         cn.parentId = this.id;
4214         cn.onRender(this.el, null);
4215         return cn;
4216     },
4217     /**
4218     * register a Navigation item
4219     * @param {Roo.bootstrap.NavItem} the navitem to add
4220     */
4221     register : function(item)
4222     {
4223         this.navItems.push( item);
4224         item.navId = this.navId;
4225     
4226     },
4227     
4228     /**
4229     * clear all the Navigation item
4230     */
4231    
4232     clearAll : function()
4233     {
4234         this.navItems = [];
4235         this.el.dom.innerHTML = '';
4236     },
4237     
4238     getNavItem: function(tabId)
4239     {
4240         var ret = false;
4241         Roo.each(this.navItems, function(e) {
4242             if (e.tabId == tabId) {
4243                ret =  e;
4244                return false;
4245             }
4246             return true;
4247             
4248         });
4249         return ret;
4250     },
4251     
4252     setActiveNext : function()
4253     {
4254         var i = this.indexOfNav(this.getActive());
4255         if (i > this.navItems.length) {
4256             return;
4257         }
4258         this.setActiveItem(this.navItems[i+1]);
4259     },
4260     setActivePrev : function()
4261     {
4262         var i = this.indexOfNav(this.getActive());
4263         if (i  < 1) {
4264             return;
4265         }
4266         this.setActiveItem(this.navItems[i-1]);
4267     },
4268     clearWasActive : function(except) {
4269         Roo.each(this.navItems, function(e) {
4270             if (e.tabId != except.tabId && e.was_active) {
4271                e.was_active = false;
4272                return false;
4273             }
4274             return true;
4275             
4276         });
4277     },
4278     getWasActive : function ()
4279     {
4280         var r = false;
4281         Roo.each(this.navItems, function(e) {
4282             if (e.was_active) {
4283                r = e;
4284                return false;
4285             }
4286             return true;
4287             
4288         });
4289         return r;
4290     }
4291     
4292     
4293 });
4294
4295  
4296 Roo.apply(Roo.bootstrap.NavGroup, {
4297     
4298     groups: {},
4299      /**
4300     * register a Navigation Group
4301     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4302     */
4303     register : function(navgrp)
4304     {
4305         this.groups[navgrp.navId] = navgrp;
4306         
4307     },
4308     /**
4309     * fetch a Navigation Group based on the navigation ID
4310     * @param {string} the navgroup to add
4311     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4312     */
4313     get: function(navId) {
4314         if (typeof(this.groups[navId]) == 'undefined') {
4315             return false;
4316             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4317         }
4318         return this.groups[navId] ;
4319     }
4320     
4321     
4322     
4323 });
4324
4325  /*
4326  * - LGPL
4327  *
4328  * row
4329  * 
4330  */
4331
4332 /**
4333  * @class Roo.bootstrap.NavItem
4334  * @extends Roo.bootstrap.Component
4335  * Bootstrap Navbar.NavItem class
4336  * @cfg {String} href  link to
4337  * @cfg {String} html content of button
4338  * @cfg {String} badge text inside badge
4339  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4340  * @cfg {String} glyphicon name of glyphicon
4341  * @cfg {String} icon name of font awesome icon
4342  * @cfg {Boolean} active Is item active
4343  * @cfg {Boolean} disabled Is item disabled
4344  
4345  * @cfg {Boolean} preventDefault (true | false) default false
4346  * @cfg {String} tabId the tab that this item activates.
4347  * @cfg {String} tagtype (a|span) render as a href or span?
4348  * @cfg {Boolean} animateRef (true|false) link to element default false  
4349   
4350  * @constructor
4351  * Create a new Navbar Item
4352  * @param {Object} config The config object
4353  */
4354 Roo.bootstrap.NavItem = function(config){
4355     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4356     this.addEvents({
4357         // raw events
4358         /**
4359          * @event click
4360          * The raw click event for the entire grid.
4361          * @param {Roo.EventObject} e
4362          */
4363         "click" : true,
4364          /**
4365             * @event changed
4366             * Fires when the active item active state changes
4367             * @param {Roo.bootstrap.NavItem} this
4368             * @param {boolean} state the new state
4369              
4370          */
4371         'changed': true,
4372         /**
4373             * @event scrollto
4374             * Fires when scroll to element
4375             * @param {Roo.bootstrap.NavItem} this
4376             * @param {Object} options
4377             * @param {Roo.EventObject} e
4378              
4379          */
4380         'scrollto': true
4381     });
4382    
4383 };
4384
4385 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4386     
4387     href: false,
4388     html: '',
4389     badge: '',
4390     icon: false,
4391     glyphicon: false,
4392     active: false,
4393     preventDefault : false,
4394     tabId : false,
4395     tagtype : 'a',
4396     disabled : false,
4397     animateRef : false,
4398     was_active : false,
4399     
4400     getAutoCreate : function(){
4401          
4402         var cfg = {
4403             tag: 'li',
4404             cls: 'nav-item'
4405             
4406         };
4407         
4408         if (this.active) {
4409             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4410         }
4411         if (this.disabled) {
4412             cfg.cls += ' disabled';
4413         }
4414         
4415         if (this.href || this.html || this.glyphicon || this.icon) {
4416             cfg.cn = [
4417                 {
4418                     tag: this.tagtype,
4419                     href : this.href || "#",
4420                     html: this.html || ''
4421                 }
4422             ];
4423             
4424             if (this.icon) {
4425                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4426             }
4427
4428             if(this.glyphicon) {
4429                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4430             }
4431             
4432             if (this.menu) {
4433                 
4434                 cfg.cn[0].html += " <span class='caret'></span>";
4435              
4436             }
4437             
4438             if (this.badge !== '') {
4439                  
4440                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4441             }
4442         }
4443         
4444         
4445         
4446         return cfg;
4447     },
4448     initEvents: function() 
4449     {
4450         if (typeof (this.menu) != 'undefined') {
4451             this.menu.parentType = this.xtype;
4452             this.menu.triggerEl = this.el;
4453             this.menu = this.addxtype(Roo.apply({}, this.menu));
4454         }
4455         
4456         this.el.select('a',true).on('click', this.onClick, this);
4457         
4458         if(this.tagtype == 'span'){
4459             this.el.select('span',true).on('click', this.onClick, this);
4460         }
4461        
4462         // at this point parent should be available..
4463         this.parent().register(this);
4464     },
4465     
4466     onClick : function(e)
4467     {
4468         if (e.getTarget('.dropdown-menu-item')) {
4469             // did you click on a menu itemm.... - then don't trigger onclick..
4470             return;
4471         }
4472         
4473         if(
4474                 this.preventDefault || 
4475                 this.href == '#' 
4476         ){
4477             Roo.log("NavItem - prevent Default?");
4478             e.preventDefault();
4479         }
4480         
4481         if (this.disabled) {
4482             return;
4483         }
4484         
4485         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4486         if (tg && tg.transition) {
4487             Roo.log("waiting for the transitionend");
4488             return;
4489         }
4490         
4491         
4492         
4493         //Roo.log("fire event clicked");
4494         if(this.fireEvent('click', this, e) === false){
4495             return;
4496         };
4497         
4498         if(this.tagtype == 'span'){
4499             return;
4500         }
4501         
4502         //Roo.log(this.href);
4503         var ael = this.el.select('a',true).first();
4504         //Roo.log(ael);
4505         
4506         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4507             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4508             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4509                 return; // ignore... - it's a 'hash' to another page.
4510             }
4511             Roo.log("NavItem - prevent Default?");
4512             e.preventDefault();
4513             this.scrollToElement(e);
4514         }
4515         
4516         
4517         var p =  this.parent();
4518    
4519         if (['tabs','pills'].indexOf(p.type)!==-1) {
4520             if (typeof(p.setActiveItem) !== 'undefined') {
4521                 p.setActiveItem(this);
4522             }
4523         }
4524         
4525         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4526         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4527             // remove the collapsed menu expand...
4528             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4529         }
4530     },
4531     
4532     isActive: function () {
4533         return this.active
4534     },
4535     setActive : function(state, fire, is_was_active)
4536     {
4537         if (this.active && !state && this.navId) {
4538             this.was_active = true;
4539             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4540             if (nv) {
4541                 nv.clearWasActive(this);
4542             }
4543             
4544         }
4545         this.active = state;
4546         
4547         if (!state ) {
4548             this.el.removeClass('active');
4549         } else if (!this.el.hasClass('active')) {
4550             this.el.addClass('active');
4551         }
4552         if (fire) {
4553             this.fireEvent('changed', this, state);
4554         }
4555         
4556         // show a panel if it's registered and related..
4557         
4558         if (!this.navId || !this.tabId || !state || is_was_active) {
4559             return;
4560         }
4561         
4562         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4563         if (!tg) {
4564             return;
4565         }
4566         var pan = tg.getPanelByName(this.tabId);
4567         if (!pan) {
4568             return;
4569         }
4570         // if we can not flip to new panel - go back to old nav highlight..
4571         if (false == tg.showPanel(pan)) {
4572             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4573             if (nv) {
4574                 var onav = nv.getWasActive();
4575                 if (onav) {
4576                     onav.setActive(true, false, true);
4577                 }
4578             }
4579             
4580         }
4581         
4582         
4583         
4584     },
4585      // this should not be here...
4586     setDisabled : function(state)
4587     {
4588         this.disabled = state;
4589         if (!state ) {
4590             this.el.removeClass('disabled');
4591         } else if (!this.el.hasClass('disabled')) {
4592             this.el.addClass('disabled');
4593         }
4594         
4595     },
4596     
4597     /**
4598      * Fetch the element to display the tooltip on.
4599      * @return {Roo.Element} defaults to this.el
4600      */
4601     tooltipEl : function()
4602     {
4603         return this.el.select('' + this.tagtype + '', true).first();
4604     },
4605     
4606     scrollToElement : function(e)
4607     {
4608         var c = document.body;
4609         
4610         /*
4611          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4612          */
4613         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4614             c = document.documentElement;
4615         }
4616         
4617         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4618         
4619         if(!target){
4620             return;
4621         }
4622
4623         var o = target.calcOffsetsTo(c);
4624         
4625         var options = {
4626             target : target,
4627             value : o[1]
4628         };
4629         
4630         this.fireEvent('scrollto', this, options, e);
4631         
4632         Roo.get(c).scrollTo('top', options.value, true);
4633         
4634         return;
4635     }
4636 });
4637  
4638
4639  /*
4640  * - LGPL
4641  *
4642  * sidebar item
4643  *
4644  *  li
4645  *    <span> icon </span>
4646  *    <span> text </span>
4647  *    <span>badge </span>
4648  */
4649
4650 /**
4651  * @class Roo.bootstrap.NavSidebarItem
4652  * @extends Roo.bootstrap.NavItem
4653  * Bootstrap Navbar.NavSidebarItem class
4654  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4655  * {Boolean} open is the menu open
4656  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4657  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4658  * {String} buttonSize (sm|md|lg)the extra classes for the button
4659  * {Boolean} showArrow show arrow next to the text (default true)
4660  * @constructor
4661  * Create a new Navbar Button
4662  * @param {Object} config The config object
4663  */
4664 Roo.bootstrap.NavSidebarItem = function(config){
4665     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4666     this.addEvents({
4667         // raw events
4668         /**
4669          * @event click
4670          * The raw click event for the entire grid.
4671          * @param {Roo.EventObject} e
4672          */
4673         "click" : true,
4674          /**
4675             * @event changed
4676             * Fires when the active item active state changes
4677             * @param {Roo.bootstrap.NavSidebarItem} this
4678             * @param {boolean} state the new state
4679              
4680          */
4681         'changed': true
4682     });
4683    
4684 };
4685
4686 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4687     
4688     badgeWeight : 'default',
4689     
4690     open: false,
4691     
4692     buttonView : false,
4693     
4694     buttonWeight : 'default',
4695     
4696     buttonSize : 'md',
4697     
4698     showArrow : true,
4699     
4700     getAutoCreate : function(){
4701         
4702         
4703         var a = {
4704                 tag: 'a',
4705                 href : this.href || '#',
4706                 cls: '',
4707                 html : '',
4708                 cn : []
4709         };
4710         
4711         if(this.buttonView){
4712             a = {
4713                 tag: 'button',
4714                 href : this.href || '#',
4715                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4716                 html : this.html,
4717                 cn : []
4718             };
4719         }
4720         
4721         var cfg = {
4722             tag: 'li',
4723             cls: '',
4724             cn: [ a ]
4725         };
4726         
4727         if (this.active) {
4728             cfg.cls += ' active';
4729         }
4730         
4731         if (this.disabled) {
4732             cfg.cls += ' disabled';
4733         }
4734         if (this.open) {
4735             cfg.cls += ' open x-open';
4736         }
4737         // left icon..
4738         if (this.glyphicon || this.icon) {
4739             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4740             a.cn.push({ tag : 'i', cls : c }) ;
4741         }
4742         
4743         if(!this.buttonView){
4744             var span = {
4745                 tag: 'span',
4746                 html : this.html || ''
4747             };
4748
4749             a.cn.push(span);
4750             
4751         }
4752         
4753         if (this.badge !== '') {
4754             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4755         }
4756         
4757         if (this.menu) {
4758             
4759             if(this.showArrow){
4760                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4761             }
4762             
4763             a.cls += ' dropdown-toggle treeview' ;
4764         }
4765         
4766         return cfg;
4767     },
4768     
4769     initEvents : function()
4770     { 
4771         if (typeof (this.menu) != 'undefined') {
4772             this.menu.parentType = this.xtype;
4773             this.menu.triggerEl = this.el;
4774             this.menu = this.addxtype(Roo.apply({}, this.menu));
4775         }
4776         
4777         this.el.on('click', this.onClick, this);
4778         
4779         if(this.badge !== ''){
4780             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4781         }
4782         
4783     },
4784     
4785     onClick : function(e)
4786     {
4787         if(this.disabled){
4788             e.preventDefault();
4789             return;
4790         }
4791         
4792         if(this.preventDefault){
4793             e.preventDefault();
4794         }
4795         
4796         this.fireEvent('click', this);
4797     },
4798     
4799     disable : function()
4800     {
4801         this.setDisabled(true);
4802     },
4803     
4804     enable : function()
4805     {
4806         this.setDisabled(false);
4807     },
4808     
4809     setDisabled : function(state)
4810     {
4811         if(this.disabled == state){
4812             return;
4813         }
4814         
4815         this.disabled = state;
4816         
4817         if (state) {
4818             this.el.addClass('disabled');
4819             return;
4820         }
4821         
4822         this.el.removeClass('disabled');
4823         
4824         return;
4825     },
4826     
4827     setActive : function(state)
4828     {
4829         if(this.active == state){
4830             return;
4831         }
4832         
4833         this.active = state;
4834         
4835         if (state) {
4836             this.el.addClass('active');
4837             return;
4838         }
4839         
4840         this.el.removeClass('active');
4841         
4842         return;
4843     },
4844     
4845     isActive: function () 
4846     {
4847         return this.active;
4848     },
4849     
4850     setBadge : function(str)
4851     {
4852         if(!this.badgeEl){
4853             return;
4854         }
4855         
4856         this.badgeEl.dom.innerHTML = str;
4857     }
4858     
4859    
4860      
4861  
4862 });
4863  
4864
4865  /*
4866  * - LGPL
4867  *
4868  * row
4869  * 
4870  */
4871
4872 /**
4873  * @class Roo.bootstrap.Row
4874  * @extends Roo.bootstrap.Component
4875  * Bootstrap Row class (contains columns...)
4876  * 
4877  * @constructor
4878  * Create a new Row
4879  * @param {Object} config The config object
4880  */
4881
4882 Roo.bootstrap.Row = function(config){
4883     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4884 };
4885
4886 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4887     
4888     getAutoCreate : function(){
4889        return {
4890             cls: 'row clearfix'
4891        };
4892     }
4893     
4894     
4895 });
4896
4897  
4898
4899  /*
4900  * - LGPL
4901  *
4902  * element
4903  * 
4904  */
4905
4906 /**
4907  * @class Roo.bootstrap.Element
4908  * @extends Roo.bootstrap.Component
4909  * Bootstrap Element class
4910  * @cfg {String} html contents of the element
4911  * @cfg {String} tag tag of the element
4912  * @cfg {String} cls class of the element
4913  * @cfg {Boolean} preventDefault (true|false) default false
4914  * @cfg {Boolean} clickable (true|false) default false
4915  * 
4916  * @constructor
4917  * Create a new Element
4918  * @param {Object} config The config object
4919  */
4920
4921 Roo.bootstrap.Element = function(config){
4922     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4923     
4924     this.addEvents({
4925         // raw events
4926         /**
4927          * @event click
4928          * When a element is chick
4929          * @param {Roo.bootstrap.Element} this
4930          * @param {Roo.EventObject} e
4931          */
4932         "click" : true
4933     });
4934 };
4935
4936 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4937     
4938     tag: 'div',
4939     cls: '',
4940     html: '',
4941     preventDefault: false, 
4942     clickable: false,
4943     
4944     getAutoCreate : function(){
4945         
4946         var cfg = {
4947             tag: this.tag,
4948             // cls: this.cls, double assign in parent class Component.js :: onRender
4949             html: this.html
4950         };
4951         
4952         return cfg;
4953     },
4954     
4955     initEvents: function() 
4956     {
4957         Roo.bootstrap.Element.superclass.initEvents.call(this);
4958         
4959         if(this.clickable){
4960             this.el.on('click', this.onClick, this);
4961         }
4962         
4963     },
4964     
4965     onClick : function(e)
4966     {
4967         if(this.preventDefault){
4968             e.preventDefault();
4969         }
4970         
4971         this.fireEvent('click', this, e);
4972     },
4973     
4974     getValue : function()
4975     {
4976         return this.el.dom.innerHTML;
4977     },
4978     
4979     setValue : function(value)
4980     {
4981         this.el.dom.innerHTML = value;
4982     }
4983    
4984 });
4985
4986  
4987
4988  /*
4989  * - LGPL
4990  *
4991  * pagination
4992  * 
4993  */
4994
4995 /**
4996  * @class Roo.bootstrap.Pagination
4997  * @extends Roo.bootstrap.Component
4998  * Bootstrap Pagination class
4999  * @cfg {String} size xs | sm | md | lg
5000  * @cfg {Boolean} inverse false | true
5001  * 
5002  * @constructor
5003  * Create a new Pagination
5004  * @param {Object} config The config object
5005  */
5006
5007 Roo.bootstrap.Pagination = function(config){
5008     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5009 };
5010
5011 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5012     
5013     cls: false,
5014     size: false,
5015     inverse: false,
5016     
5017     getAutoCreate : function(){
5018         var cfg = {
5019             tag: 'ul',
5020                 cls: 'pagination'
5021         };
5022         if (this.inverse) {
5023             cfg.cls += ' inverse';
5024         }
5025         if (this.html) {
5026             cfg.html=this.html;
5027         }
5028         if (this.cls) {
5029             cfg.cls += " " + this.cls;
5030         }
5031         return cfg;
5032     }
5033    
5034 });
5035
5036  
5037
5038  /*
5039  * - LGPL
5040  *
5041  * Pagination item
5042  * 
5043  */
5044
5045
5046 /**
5047  * @class Roo.bootstrap.PaginationItem
5048  * @extends Roo.bootstrap.Component
5049  * Bootstrap PaginationItem class
5050  * @cfg {String} html text
5051  * @cfg {String} href the link
5052  * @cfg {Boolean} preventDefault (true | false) default true
5053  * @cfg {Boolean} active (true | false) default false
5054  * @cfg {Boolean} disabled default false
5055  * 
5056  * 
5057  * @constructor
5058  * Create a new PaginationItem
5059  * @param {Object} config The config object
5060  */
5061
5062
5063 Roo.bootstrap.PaginationItem = function(config){
5064     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5065     this.addEvents({
5066         // raw events
5067         /**
5068          * @event click
5069          * The raw click event for the entire grid.
5070          * @param {Roo.EventObject} e
5071          */
5072         "click" : true
5073     });
5074 };
5075
5076 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5077     
5078     href : false,
5079     html : false,
5080     preventDefault: true,
5081     active : false,
5082     cls : false,
5083     disabled: false,
5084     
5085     getAutoCreate : function(){
5086         var cfg= {
5087             tag: 'li',
5088             cn: [
5089                 {
5090                     tag : 'a',
5091                     href : this.href ? this.href : '#',
5092                     html : this.html ? this.html : ''
5093                 }
5094             ]
5095         };
5096         
5097         if(this.cls){
5098             cfg.cls = this.cls;
5099         }
5100         
5101         if(this.disabled){
5102             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5103         }
5104         
5105         if(this.active){
5106             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5107         }
5108         
5109         return cfg;
5110     },
5111     
5112     initEvents: function() {
5113         
5114         this.el.on('click', this.onClick, this);
5115         
5116     },
5117     onClick : function(e)
5118     {
5119         Roo.log('PaginationItem on click ');
5120         if(this.preventDefault){
5121             e.preventDefault();
5122         }
5123         
5124         if(this.disabled){
5125             return;
5126         }
5127         
5128         this.fireEvent('click', this, e);
5129     }
5130    
5131 });
5132
5133  
5134
5135  /*
5136  * - LGPL
5137  *
5138  * slider
5139  * 
5140  */
5141
5142
5143 /**
5144  * @class Roo.bootstrap.Slider
5145  * @extends Roo.bootstrap.Component
5146  * Bootstrap Slider class
5147  *    
5148  * @constructor
5149  * Create a new Slider
5150  * @param {Object} config The config object
5151  */
5152
5153 Roo.bootstrap.Slider = function(config){
5154     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5155 };
5156
5157 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5158     
5159     getAutoCreate : function(){
5160         
5161         var cfg = {
5162             tag: 'div',
5163             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5164             cn: [
5165                 {
5166                     tag: 'a',
5167                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5168                 }
5169             ]
5170         };
5171         
5172         return cfg;
5173     }
5174    
5175 });
5176
5177  /*
5178  * Based on:
5179  * Ext JS Library 1.1.1
5180  * Copyright(c) 2006-2007, Ext JS, LLC.
5181  *
5182  * Originally Released Under LGPL - original licence link has changed is not relivant.
5183  *
5184  * Fork - LGPL
5185  * <script type="text/javascript">
5186  */
5187  
5188
5189 /**
5190  * @class Roo.grid.ColumnModel
5191  * @extends Roo.util.Observable
5192  * This is the default implementation of a ColumnModel used by the Grid. It defines
5193  * the columns in the grid.
5194  * <br>Usage:<br>
5195  <pre><code>
5196  var colModel = new Roo.grid.ColumnModel([
5197         {header: "Ticker", width: 60, sortable: true, locked: true},
5198         {header: "Company Name", width: 150, sortable: true},
5199         {header: "Market Cap.", width: 100, sortable: true},
5200         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5201         {header: "Employees", width: 100, sortable: true, resizable: false}
5202  ]);
5203  </code></pre>
5204  * <p>
5205  
5206  * The config options listed for this class are options which may appear in each
5207  * individual column definition.
5208  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5209  * @constructor
5210  * @param {Object} config An Array of column config objects. See this class's
5211  * config objects for details.
5212 */
5213 Roo.grid.ColumnModel = function(config){
5214         /**
5215      * The config passed into the constructor
5216      */
5217     this.config = config;
5218     this.lookup = {};
5219
5220     // if no id, create one
5221     // if the column does not have a dataIndex mapping,
5222     // map it to the order it is in the config
5223     for(var i = 0, len = config.length; i < len; i++){
5224         var c = config[i];
5225         if(typeof c.dataIndex == "undefined"){
5226             c.dataIndex = i;
5227         }
5228         if(typeof c.renderer == "string"){
5229             c.renderer = Roo.util.Format[c.renderer];
5230         }
5231         if(typeof c.id == "undefined"){
5232             c.id = Roo.id();
5233         }
5234         if(c.editor && c.editor.xtype){
5235             c.editor  = Roo.factory(c.editor, Roo.grid);
5236         }
5237         if(c.editor && c.editor.isFormField){
5238             c.editor = new Roo.grid.GridEditor(c.editor);
5239         }
5240         this.lookup[c.id] = c;
5241     }
5242
5243     /**
5244      * The width of columns which have no width specified (defaults to 100)
5245      * @type Number
5246      */
5247     this.defaultWidth = 100;
5248
5249     /**
5250      * Default sortable of columns which have no sortable specified (defaults to false)
5251      * @type Boolean
5252      */
5253     this.defaultSortable = false;
5254
5255     this.addEvents({
5256         /**
5257              * @event widthchange
5258              * Fires when the width of a column changes.
5259              * @param {ColumnModel} this
5260              * @param {Number} columnIndex The column index
5261              * @param {Number} newWidth The new width
5262              */
5263             "widthchange": true,
5264         /**
5265              * @event headerchange
5266              * Fires when the text of a header changes.
5267              * @param {ColumnModel} this
5268              * @param {Number} columnIndex The column index
5269              * @param {Number} newText The new header text
5270              */
5271             "headerchange": true,
5272         /**
5273              * @event hiddenchange
5274              * Fires when a column is hidden or "unhidden".
5275              * @param {ColumnModel} this
5276              * @param {Number} columnIndex The column index
5277              * @param {Boolean} hidden true if hidden, false otherwise
5278              */
5279             "hiddenchange": true,
5280             /**
5281          * @event columnmoved
5282          * Fires when a column is moved.
5283          * @param {ColumnModel} this
5284          * @param {Number} oldIndex
5285          * @param {Number} newIndex
5286          */
5287         "columnmoved" : true,
5288         /**
5289          * @event columlockchange
5290          * Fires when a column's locked state is changed
5291          * @param {ColumnModel} this
5292          * @param {Number} colIndex
5293          * @param {Boolean} locked true if locked
5294          */
5295         "columnlockchange" : true
5296     });
5297     Roo.grid.ColumnModel.superclass.constructor.call(this);
5298 };
5299 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5300     /**
5301      * @cfg {String} header The header text to display in the Grid view.
5302      */
5303     /**
5304      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5305      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5306      * specified, the column's index is used as an index into the Record's data Array.
5307      */
5308     /**
5309      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5310      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5311      */
5312     /**
5313      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5314      * Defaults to the value of the {@link #defaultSortable} property.
5315      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5316      */
5317     /**
5318      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5319      */
5320     /**
5321      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5322      */
5323     /**
5324      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5325      */
5326     /**
5327      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5328      */
5329     /**
5330      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5331      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5332      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5333      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5334      */
5335        /**
5336      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5337      */
5338     /**
5339      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5340      */
5341     /**
5342      * @cfg {String} cursor (Optional)
5343      */
5344     /**
5345      * @cfg {String} tooltip (Optional)
5346      */
5347     /**
5348      * @cfg {Number} xs (Optional)
5349      */
5350     /**
5351      * @cfg {Number} sm (Optional)
5352      */
5353     /**
5354      * @cfg {Number} md (Optional)
5355      */
5356     /**
5357      * @cfg {Number} lg (Optional)
5358      */
5359     /**
5360      * Returns the id of the column at the specified index.
5361      * @param {Number} index The column index
5362      * @return {String} the id
5363      */
5364     getColumnId : function(index){
5365         return this.config[index].id;
5366     },
5367
5368     /**
5369      * Returns the column for a specified id.
5370      * @param {String} id The column id
5371      * @return {Object} the column
5372      */
5373     getColumnById : function(id){
5374         return this.lookup[id];
5375     },
5376
5377     
5378     /**
5379      * Returns the column for a specified dataIndex.
5380      * @param {String} dataIndex The column dataIndex
5381      * @return {Object|Boolean} the column or false if not found
5382      */
5383     getColumnByDataIndex: function(dataIndex){
5384         var index = this.findColumnIndex(dataIndex);
5385         return index > -1 ? this.config[index] : false;
5386     },
5387     
5388     /**
5389      * Returns the index for a specified column id.
5390      * @param {String} id The column id
5391      * @return {Number} the index, or -1 if not found
5392      */
5393     getIndexById : function(id){
5394         for(var i = 0, len = this.config.length; i < len; i++){
5395             if(this.config[i].id == id){
5396                 return i;
5397             }
5398         }
5399         return -1;
5400     },
5401     
5402     /**
5403      * Returns the index for a specified column dataIndex.
5404      * @param {String} dataIndex The column dataIndex
5405      * @return {Number} the index, or -1 if not found
5406      */
5407     
5408     findColumnIndex : function(dataIndex){
5409         for(var i = 0, len = this.config.length; i < len; i++){
5410             if(this.config[i].dataIndex == dataIndex){
5411                 return i;
5412             }
5413         }
5414         return -1;
5415     },
5416     
5417     
5418     moveColumn : function(oldIndex, newIndex){
5419         var c = this.config[oldIndex];
5420         this.config.splice(oldIndex, 1);
5421         this.config.splice(newIndex, 0, c);
5422         this.dataMap = null;
5423         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5424     },
5425
5426     isLocked : function(colIndex){
5427         return this.config[colIndex].locked === true;
5428     },
5429
5430     setLocked : function(colIndex, value, suppressEvent){
5431         if(this.isLocked(colIndex) == value){
5432             return;
5433         }
5434         this.config[colIndex].locked = value;
5435         if(!suppressEvent){
5436             this.fireEvent("columnlockchange", this, colIndex, value);
5437         }
5438     },
5439
5440     getTotalLockedWidth : function(){
5441         var totalWidth = 0;
5442         for(var i = 0; i < this.config.length; i++){
5443             if(this.isLocked(i) && !this.isHidden(i)){
5444                 this.totalWidth += this.getColumnWidth(i);
5445             }
5446         }
5447         return totalWidth;
5448     },
5449
5450     getLockedCount : function(){
5451         for(var i = 0, len = this.config.length; i < len; i++){
5452             if(!this.isLocked(i)){
5453                 return i;
5454             }
5455         }
5456         
5457         return this.config.length;
5458     },
5459
5460     /**
5461      * Returns the number of columns.
5462      * @return {Number}
5463      */
5464     getColumnCount : function(visibleOnly){
5465         if(visibleOnly === true){
5466             var c = 0;
5467             for(var i = 0, len = this.config.length; i < len; i++){
5468                 if(!this.isHidden(i)){
5469                     c++;
5470                 }
5471             }
5472             return c;
5473         }
5474         return this.config.length;
5475     },
5476
5477     /**
5478      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5479      * @param {Function} fn
5480      * @param {Object} scope (optional)
5481      * @return {Array} result
5482      */
5483     getColumnsBy : function(fn, scope){
5484         var r = [];
5485         for(var i = 0, len = this.config.length; i < len; i++){
5486             var c = this.config[i];
5487             if(fn.call(scope||this, c, i) === true){
5488                 r[r.length] = c;
5489             }
5490         }
5491         return r;
5492     },
5493
5494     /**
5495      * Returns true if the specified column is sortable.
5496      * @param {Number} col The column index
5497      * @return {Boolean}
5498      */
5499     isSortable : function(col){
5500         if(typeof this.config[col].sortable == "undefined"){
5501             return this.defaultSortable;
5502         }
5503         return this.config[col].sortable;
5504     },
5505
5506     /**
5507      * Returns the rendering (formatting) function defined for the column.
5508      * @param {Number} col The column index.
5509      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5510      */
5511     getRenderer : function(col){
5512         if(!this.config[col].renderer){
5513             return Roo.grid.ColumnModel.defaultRenderer;
5514         }
5515         return this.config[col].renderer;
5516     },
5517
5518     /**
5519      * Sets the rendering (formatting) function for a column.
5520      * @param {Number} col The column index
5521      * @param {Function} fn The function to use to process the cell's raw data
5522      * to return HTML markup for the grid view. The render function is called with
5523      * the following parameters:<ul>
5524      * <li>Data value.</li>
5525      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5526      * <li>css A CSS style string to apply to the table cell.</li>
5527      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5528      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5529      * <li>Row index</li>
5530      * <li>Column index</li>
5531      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5532      */
5533     setRenderer : function(col, fn){
5534         this.config[col].renderer = fn;
5535     },
5536
5537     /**
5538      * Returns the width for the specified column.
5539      * @param {Number} col The column index
5540      * @return {Number}
5541      */
5542     getColumnWidth : function(col){
5543         return this.config[col].width * 1 || this.defaultWidth;
5544     },
5545
5546     /**
5547      * Sets the width for a column.
5548      * @param {Number} col The column index
5549      * @param {Number} width The new width
5550      */
5551     setColumnWidth : function(col, width, suppressEvent){
5552         this.config[col].width = width;
5553         this.totalWidth = null;
5554         if(!suppressEvent){
5555              this.fireEvent("widthchange", this, col, width);
5556         }
5557     },
5558
5559     /**
5560      * Returns the total width of all columns.
5561      * @param {Boolean} includeHidden True to include hidden column widths
5562      * @return {Number}
5563      */
5564     getTotalWidth : function(includeHidden){
5565         if(!this.totalWidth){
5566             this.totalWidth = 0;
5567             for(var i = 0, len = this.config.length; i < len; i++){
5568                 if(includeHidden || !this.isHidden(i)){
5569                     this.totalWidth += this.getColumnWidth(i);
5570                 }
5571             }
5572         }
5573         return this.totalWidth;
5574     },
5575
5576     /**
5577      * Returns the header for the specified column.
5578      * @param {Number} col The column index
5579      * @return {String}
5580      */
5581     getColumnHeader : function(col){
5582         return this.config[col].header;
5583     },
5584
5585     /**
5586      * Sets the header for a column.
5587      * @param {Number} col The column index
5588      * @param {String} header The new header
5589      */
5590     setColumnHeader : function(col, header){
5591         this.config[col].header = header;
5592         this.fireEvent("headerchange", this, col, header);
5593     },
5594
5595     /**
5596      * Returns the tooltip for the specified column.
5597      * @param {Number} col The column index
5598      * @return {String}
5599      */
5600     getColumnTooltip : function(col){
5601             return this.config[col].tooltip;
5602     },
5603     /**
5604      * Sets the tooltip for a column.
5605      * @param {Number} col The column index
5606      * @param {String} tooltip The new tooltip
5607      */
5608     setColumnTooltip : function(col, tooltip){
5609             this.config[col].tooltip = tooltip;
5610     },
5611
5612     /**
5613      * Returns the dataIndex for the specified column.
5614      * @param {Number} col The column index
5615      * @return {Number}
5616      */
5617     getDataIndex : function(col){
5618         return this.config[col].dataIndex;
5619     },
5620
5621     /**
5622      * Sets the dataIndex for a column.
5623      * @param {Number} col The column index
5624      * @param {Number} dataIndex The new dataIndex
5625      */
5626     setDataIndex : function(col, dataIndex){
5627         this.config[col].dataIndex = dataIndex;
5628     },
5629
5630     
5631     
5632     /**
5633      * Returns true if the cell is editable.
5634      * @param {Number} colIndex The column index
5635      * @param {Number} rowIndex The row index - this is nto actually used..?
5636      * @return {Boolean}
5637      */
5638     isCellEditable : function(colIndex, rowIndex){
5639         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5640     },
5641
5642     /**
5643      * Returns the editor defined for the cell/column.
5644      * return false or null to disable editing.
5645      * @param {Number} colIndex The column index
5646      * @param {Number} rowIndex The row index
5647      * @return {Object}
5648      */
5649     getCellEditor : function(colIndex, rowIndex){
5650         return this.config[colIndex].editor;
5651     },
5652
5653     /**
5654      * Sets if a column is editable.
5655      * @param {Number} col The column index
5656      * @param {Boolean} editable True if the column is editable
5657      */
5658     setEditable : function(col, editable){
5659         this.config[col].editable = editable;
5660     },
5661
5662
5663     /**
5664      * Returns true if the column is hidden.
5665      * @param {Number} colIndex The column index
5666      * @return {Boolean}
5667      */
5668     isHidden : function(colIndex){
5669         return this.config[colIndex].hidden;
5670     },
5671
5672
5673     /**
5674      * Returns true if the column width cannot be changed
5675      */
5676     isFixed : function(colIndex){
5677         return this.config[colIndex].fixed;
5678     },
5679
5680     /**
5681      * Returns true if the column can be resized
5682      * @return {Boolean}
5683      */
5684     isResizable : function(colIndex){
5685         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5686     },
5687     /**
5688      * Sets if a column is hidden.
5689      * @param {Number} colIndex The column index
5690      * @param {Boolean} hidden True if the column is hidden
5691      */
5692     setHidden : function(colIndex, hidden){
5693         this.config[colIndex].hidden = hidden;
5694         this.totalWidth = null;
5695         this.fireEvent("hiddenchange", this, colIndex, hidden);
5696     },
5697
5698     /**
5699      * Sets the editor for a column.
5700      * @param {Number} col The column index
5701      * @param {Object} editor The editor object
5702      */
5703     setEditor : function(col, editor){
5704         this.config[col].editor = editor;
5705     }
5706 });
5707
5708 Roo.grid.ColumnModel.defaultRenderer = function(value)
5709 {
5710     if(typeof value == "object") {
5711         return value;
5712     }
5713         if(typeof value == "string" && value.length < 1){
5714             return "&#160;";
5715         }
5716     
5717         return String.format("{0}", value);
5718 };
5719
5720 // Alias for backwards compatibility
5721 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5722 /*
5723  * Based on:
5724  * Ext JS Library 1.1.1
5725  * Copyright(c) 2006-2007, Ext JS, LLC.
5726  *
5727  * Originally Released Under LGPL - original licence link has changed is not relivant.
5728  *
5729  * Fork - LGPL
5730  * <script type="text/javascript">
5731  */
5732  
5733 /**
5734  * @class Roo.LoadMask
5735  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5736  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5737  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5738  * element's UpdateManager load indicator and will be destroyed after the initial load.
5739  * @constructor
5740  * Create a new LoadMask
5741  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5742  * @param {Object} config The config object
5743  */
5744 Roo.LoadMask = function(el, config){
5745     this.el = Roo.get(el);
5746     Roo.apply(this, config);
5747     if(this.store){
5748         this.store.on('beforeload', this.onBeforeLoad, this);
5749         this.store.on('load', this.onLoad, this);
5750         this.store.on('loadexception', this.onLoadException, this);
5751         this.removeMask = false;
5752     }else{
5753         var um = this.el.getUpdateManager();
5754         um.showLoadIndicator = false; // disable the default indicator
5755         um.on('beforeupdate', this.onBeforeLoad, this);
5756         um.on('update', this.onLoad, this);
5757         um.on('failure', this.onLoad, this);
5758         this.removeMask = true;
5759     }
5760 };
5761
5762 Roo.LoadMask.prototype = {
5763     /**
5764      * @cfg {Boolean} removeMask
5765      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5766      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5767      */
5768     /**
5769      * @cfg {String} msg
5770      * The text to display in a centered loading message box (defaults to 'Loading...')
5771      */
5772     msg : 'Loading...',
5773     /**
5774      * @cfg {String} msgCls
5775      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5776      */
5777     msgCls : 'x-mask-loading',
5778
5779     /**
5780      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5781      * @type Boolean
5782      */
5783     disabled: false,
5784
5785     /**
5786      * Disables the mask to prevent it from being displayed
5787      */
5788     disable : function(){
5789        this.disabled = true;
5790     },
5791
5792     /**
5793      * Enables the mask so that it can be displayed
5794      */
5795     enable : function(){
5796         this.disabled = false;
5797     },
5798     
5799     onLoadException : function()
5800     {
5801         Roo.log(arguments);
5802         
5803         if (typeof(arguments[3]) != 'undefined') {
5804             Roo.MessageBox.alert("Error loading",arguments[3]);
5805         } 
5806         /*
5807         try {
5808             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5809                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5810             }   
5811         } catch(e) {
5812             
5813         }
5814         */
5815     
5816         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5817     },
5818     // private
5819     onLoad : function()
5820     {
5821         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5822     },
5823
5824     // private
5825     onBeforeLoad : function(){
5826         if(!this.disabled){
5827             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5828         }
5829     },
5830
5831     // private
5832     destroy : function(){
5833         if(this.store){
5834             this.store.un('beforeload', this.onBeforeLoad, this);
5835             this.store.un('load', this.onLoad, this);
5836             this.store.un('loadexception', this.onLoadException, this);
5837         }else{
5838             var um = this.el.getUpdateManager();
5839             um.un('beforeupdate', this.onBeforeLoad, this);
5840             um.un('update', this.onLoad, this);
5841             um.un('failure', this.onLoad, this);
5842         }
5843     }
5844 };/*
5845  * - LGPL
5846  *
5847  * table
5848  * 
5849  */
5850
5851 /**
5852  * @class Roo.bootstrap.Table
5853  * @extends Roo.bootstrap.Component
5854  * Bootstrap Table class
5855  * @cfg {String} cls table class
5856  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5857  * @cfg {String} bgcolor Specifies the background color for a table
5858  * @cfg {Number} border Specifies whether the table cells should have borders or not
5859  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5860  * @cfg {Number} cellspacing Specifies the space between cells
5861  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5862  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5863  * @cfg {String} sortable Specifies that the table should be sortable
5864  * @cfg {String} summary Specifies a summary of the content of a table
5865  * @cfg {Number} width Specifies the width of a table
5866  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5867  * 
5868  * @cfg {boolean} striped Should the rows be alternative striped
5869  * @cfg {boolean} bordered Add borders to the table
5870  * @cfg {boolean} hover Add hover highlighting
5871  * @cfg {boolean} condensed Format condensed
5872  * @cfg {boolean} responsive Format condensed
5873  * @cfg {Boolean} loadMask (true|false) default false
5874  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5875  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5876  * @cfg {Boolean} rowSelection (true|false) default false
5877  * @cfg {Boolean} cellSelection (true|false) default false
5878  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5879  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5880  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5881  
5882  * 
5883  * @constructor
5884  * Create a new Table
5885  * @param {Object} config The config object
5886  */
5887
5888 Roo.bootstrap.Table = function(config){
5889     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5890     
5891   
5892     
5893     // BC...
5894     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5895     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5896     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5897     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5898     
5899     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5900     if (this.sm) {
5901         this.sm.grid = this;
5902         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5903         this.sm = this.selModel;
5904         this.sm.xmodule = this.xmodule || false;
5905     }
5906     
5907     if (this.cm && typeof(this.cm.config) == 'undefined') {
5908         this.colModel = new Roo.grid.ColumnModel(this.cm);
5909         this.cm = this.colModel;
5910         this.cm.xmodule = this.xmodule || false;
5911     }
5912     if (this.store) {
5913         this.store= Roo.factory(this.store, Roo.data);
5914         this.ds = this.store;
5915         this.ds.xmodule = this.xmodule || false;
5916          
5917     }
5918     if (this.footer && this.store) {
5919         this.footer.dataSource = this.ds;
5920         this.footer = Roo.factory(this.footer);
5921     }
5922     
5923     /** @private */
5924     this.addEvents({
5925         /**
5926          * @event cellclick
5927          * Fires when a cell is clicked
5928          * @param {Roo.bootstrap.Table} this
5929          * @param {Roo.Element} el
5930          * @param {Number} rowIndex
5931          * @param {Number} columnIndex
5932          * @param {Roo.EventObject} e
5933          */
5934         "cellclick" : true,
5935         /**
5936          * @event celldblclick
5937          * Fires when a cell is double clicked
5938          * @param {Roo.bootstrap.Table} this
5939          * @param {Roo.Element} el
5940          * @param {Number} rowIndex
5941          * @param {Number} columnIndex
5942          * @param {Roo.EventObject} e
5943          */
5944         "celldblclick" : true,
5945         /**
5946          * @event rowclick
5947          * Fires when a row is clicked
5948          * @param {Roo.bootstrap.Table} this
5949          * @param {Roo.Element} el
5950          * @param {Number} rowIndex
5951          * @param {Roo.EventObject} e
5952          */
5953         "rowclick" : true,
5954         /**
5955          * @event rowdblclick
5956          * Fires when a row is double clicked
5957          * @param {Roo.bootstrap.Table} this
5958          * @param {Roo.Element} el
5959          * @param {Number} rowIndex
5960          * @param {Roo.EventObject} e
5961          */
5962         "rowdblclick" : true,
5963         /**
5964          * @event mouseover
5965          * Fires when a mouseover occur
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         "mouseover" : true,
5973         /**
5974          * @event mouseout
5975          * Fires when a mouseout occur
5976          * @param {Roo.bootstrap.Table} this
5977          * @param {Roo.Element} el
5978          * @param {Number} rowIndex
5979          * @param {Number} columnIndex
5980          * @param {Roo.EventObject} e
5981          */
5982         "mouseout" : true,
5983         /**
5984          * @event rowclass
5985          * Fires when a row is rendered, so you can change add a style to it.
5986          * @param {Roo.bootstrap.Table} this
5987          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5988          */
5989         'rowclass' : true,
5990           /**
5991          * @event rowsrendered
5992          * Fires when all the  rows have been rendered
5993          * @param {Roo.bootstrap.Table} this
5994          */
5995         'rowsrendered' : true,
5996         /**
5997          * @event contextmenu
5998          * The raw contextmenu event for the entire grid.
5999          * @param {Roo.EventObject} e
6000          */
6001         "contextmenu" : true,
6002         /**
6003          * @event rowcontextmenu
6004          * Fires when a row is right clicked
6005          * @param {Roo.bootstrap.Table} this
6006          * @param {Number} rowIndex
6007          * @param {Roo.EventObject} e
6008          */
6009         "rowcontextmenu" : true,
6010         /**
6011          * @event cellcontextmenu
6012          * Fires when a cell is right clicked
6013          * @param {Roo.bootstrap.Table} this
6014          * @param {Number} rowIndex
6015          * @param {Number} cellIndex
6016          * @param {Roo.EventObject} e
6017          */
6018          "cellcontextmenu" : true,
6019          /**
6020          * @event headercontextmenu
6021          * Fires when a header is right clicked
6022          * @param {Roo.bootstrap.Table} this
6023          * @param {Number} columnIndex
6024          * @param {Roo.EventObject} e
6025          */
6026         "headercontextmenu" : true
6027     });
6028 };
6029
6030 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6031     
6032     cls: false,
6033     align: false,
6034     bgcolor: false,
6035     border: false,
6036     cellpadding: false,
6037     cellspacing: false,
6038     frame: false,
6039     rules: false,
6040     sortable: false,
6041     summary: false,
6042     width: false,
6043     striped : false,
6044     scrollBody : false,
6045     bordered: false,
6046     hover:  false,
6047     condensed : false,
6048     responsive : false,
6049     sm : false,
6050     cm : false,
6051     store : false,
6052     loadMask : false,
6053     footerShow : true,
6054     headerShow : true,
6055   
6056     rowSelection : false,
6057     cellSelection : false,
6058     layout : false,
6059     
6060     // Roo.Element - the tbody
6061     mainBody: false,
6062     // Roo.Element - thead element
6063     mainHead: false,
6064     
6065     container: false, // used by gridpanel...
6066     
6067     lazyLoad : false,
6068     
6069     CSS : Roo.util.CSS,
6070     
6071     getAutoCreate : function()
6072     {
6073         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6074         
6075         cfg = {
6076             tag: 'table',
6077             cls : 'table',
6078             cn : []
6079         };
6080         if (this.scrollBody) {
6081             cfg.cls += ' table-body-fixed';
6082         }    
6083         if (this.striped) {
6084             cfg.cls += ' table-striped';
6085         }
6086         
6087         if (this.hover) {
6088             cfg.cls += ' table-hover';
6089         }
6090         if (this.bordered) {
6091             cfg.cls += ' table-bordered';
6092         }
6093         if (this.condensed) {
6094             cfg.cls += ' table-condensed';
6095         }
6096         if (this.responsive) {
6097             cfg.cls += ' table-responsive';
6098         }
6099         
6100         if (this.cls) {
6101             cfg.cls+=  ' ' +this.cls;
6102         }
6103         
6104         // this lot should be simplifed...
6105         
6106         if (this.align) {
6107             cfg.align=this.align;
6108         }
6109         if (this.bgcolor) {
6110             cfg.bgcolor=this.bgcolor;
6111         }
6112         if (this.border) {
6113             cfg.border=this.border;
6114         }
6115         if (this.cellpadding) {
6116             cfg.cellpadding=this.cellpadding;
6117         }
6118         if (this.cellspacing) {
6119             cfg.cellspacing=this.cellspacing;
6120         }
6121         if (this.frame) {
6122             cfg.frame=this.frame;
6123         }
6124         if (this.rules) {
6125             cfg.rules=this.rules;
6126         }
6127         if (this.sortable) {
6128             cfg.sortable=this.sortable;
6129         }
6130         if (this.summary) {
6131             cfg.summary=this.summary;
6132         }
6133         if (this.width) {
6134             cfg.width=this.width;
6135         }
6136         if (this.layout) {
6137             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6138         }
6139         
6140         if(this.store || this.cm){
6141             if(this.headerShow){
6142                 cfg.cn.push(this.renderHeader());
6143             }
6144             
6145             cfg.cn.push(this.renderBody());
6146             
6147             if(this.footerShow){
6148                 cfg.cn.push(this.renderFooter());
6149             }
6150             // where does this come from?
6151             //cfg.cls+=  ' TableGrid';
6152         }
6153         
6154         return { cn : [ cfg ] };
6155     },
6156     
6157     initEvents : function()
6158     {   
6159         if(!this.store || !this.cm){
6160             return;
6161         }
6162         if (this.selModel) {
6163             this.selModel.initEvents();
6164         }
6165         
6166         
6167         //Roo.log('initEvents with ds!!!!');
6168         
6169         this.mainBody = this.el.select('tbody', true).first();
6170         this.mainHead = this.el.select('thead', true).first();
6171         
6172         
6173         
6174         
6175         var _this = this;
6176         
6177         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6178             e.on('click', _this.sort, _this);
6179         });
6180         
6181         this.mainBody.on("click", this.onClick, this);
6182         this.mainBody.on("dblclick", this.onDblClick, this);
6183         
6184         // why is this done????? = it breaks dialogs??
6185         //this.parent().el.setStyle('position', 'relative');
6186         
6187         
6188         if (this.footer) {
6189             this.footer.parentId = this.id;
6190             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6191             
6192             if(this.lazyLoad){
6193                 this.el.select('tfoot tr td').first().addClass('hide');
6194             }
6195         } 
6196         
6197         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6198         
6199         this.store.on('load', this.onLoad, this);
6200         this.store.on('beforeload', this.onBeforeLoad, this);
6201         this.store.on('update', this.onUpdate, this);
6202         this.store.on('add', this.onAdd, this);
6203         this.store.on("clear", this.clear, this);
6204         
6205         this.el.on("contextmenu", this.onContextMenu, this);
6206         
6207         this.mainBody.on('scroll', this.onBodyScroll, this);
6208         
6209         this.cm.on("headerchange", this.onHeaderChange, this);
6210         
6211         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6212         
6213     },
6214     
6215     onContextMenu : function(e, t)
6216     {
6217         this.processEvent("contextmenu", e);
6218     },
6219     
6220     processEvent : function(name, e)
6221     {
6222         if (name != 'touchstart' ) {
6223             this.fireEvent(name, e);    
6224         }
6225         
6226         var t = e.getTarget();
6227         
6228         var cell = Roo.get(t);
6229         
6230         if(!cell){
6231             return;
6232         }
6233         
6234         if(cell.findParent('tfoot', false, true)){
6235             return;
6236         }
6237         
6238         if(cell.findParent('thead', false, true)){
6239             
6240             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6241                 cell = Roo.get(t).findParent('th', false, true);
6242                 if (!cell) {
6243                     Roo.log("failed to find th in thead?");
6244                     Roo.log(e.getTarget());
6245                     return;
6246                 }
6247             }
6248             
6249             var cellIndex = cell.dom.cellIndex;
6250             
6251             var ename = name == 'touchstart' ? 'click' : name;
6252             this.fireEvent("header" + ename, this, cellIndex, e);
6253             
6254             return;
6255         }
6256         
6257         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6258             cell = Roo.get(t).findParent('td', false, true);
6259             if (!cell) {
6260                 Roo.log("failed to find th in tbody?");
6261                 Roo.log(e.getTarget());
6262                 return;
6263             }
6264         }
6265         
6266         var row = cell.findParent('tr', false, true);
6267         var cellIndex = cell.dom.cellIndex;
6268         var rowIndex = row.dom.rowIndex - 1;
6269         
6270         if(row !== false){
6271             
6272             this.fireEvent("row" + name, this, rowIndex, e);
6273             
6274             if(cell !== false){
6275             
6276                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6277             }
6278         }
6279         
6280     },
6281     
6282     onMouseover : function(e, el)
6283     {
6284         var cell = Roo.get(el);
6285         
6286         if(!cell){
6287             return;
6288         }
6289         
6290         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6291             cell = cell.findParent('td', false, true);
6292         }
6293         
6294         var row = cell.findParent('tr', false, true);
6295         var cellIndex = cell.dom.cellIndex;
6296         var rowIndex = row.dom.rowIndex - 1; // start from 0
6297         
6298         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6299         
6300     },
6301     
6302     onMouseout : 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('mouseout', this, cell, rowIndex, cellIndex, e);
6319         
6320     },
6321     
6322     onClick : function(e, el)
6323     {
6324         var cell = Roo.get(el);
6325         
6326         if(!cell || (!this.cellSelection && !this.rowSelection)){
6327             return;
6328         }
6329         
6330         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6331             cell = cell.findParent('td', false, true);
6332         }
6333         
6334         if(!cell || typeof(cell) == 'undefined'){
6335             return;
6336         }
6337         
6338         var row = cell.findParent('tr', false, true);
6339         
6340         if(!row || typeof(row) == 'undefined'){
6341             return;
6342         }
6343         
6344         var cellIndex = cell.dom.cellIndex;
6345         var rowIndex = this.getRowIndex(row);
6346         
6347         // why??? - should these not be based on SelectionModel?
6348         if(this.cellSelection){
6349             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6350         }
6351         
6352         if(this.rowSelection){
6353             this.fireEvent('rowclick', this, row, rowIndex, e);
6354         }
6355         
6356         
6357     },
6358         
6359     onDblClick : function(e,el)
6360     {
6361         var cell = Roo.get(el);
6362         
6363         if(!cell || (!this.cellSelection && !this.rowSelection)){
6364             return;
6365         }
6366         
6367         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6368             cell = cell.findParent('td', false, true);
6369         }
6370         
6371         if(!cell || typeof(cell) == 'undefined'){
6372             return;
6373         }
6374         
6375         var row = cell.findParent('tr', false, true);
6376         
6377         if(!row || typeof(row) == 'undefined'){
6378             return;
6379         }
6380         
6381         var cellIndex = cell.dom.cellIndex;
6382         var rowIndex = this.getRowIndex(row);
6383         
6384         if(this.cellSelection){
6385             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6386         }
6387         
6388         if(this.rowSelection){
6389             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6390         }
6391     },
6392     
6393     sort : function(e,el)
6394     {
6395         var col = Roo.get(el);
6396         
6397         if(!col.hasClass('sortable')){
6398             return;
6399         }
6400         
6401         var sort = col.attr('sort');
6402         var dir = 'ASC';
6403         
6404         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6405             dir = 'DESC';
6406         }
6407         
6408         this.store.sortInfo = {field : sort, direction : dir};
6409         
6410         if (this.footer) {
6411             Roo.log("calling footer first");
6412             this.footer.onClick('first');
6413         } else {
6414         
6415             this.store.load({ params : { start : 0 } });
6416         }
6417     },
6418     
6419     renderHeader : function()
6420     {
6421         var header = {
6422             tag: 'thead',
6423             cn : []
6424         };
6425         
6426         var cm = this.cm;
6427         this.totalWidth = 0;
6428         
6429         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6430             
6431             var config = cm.config[i];
6432             
6433             var c = {
6434                 tag: 'th',
6435                 cls : 'x-hcol-' + i,
6436                 style : '',
6437                 html: cm.getColumnHeader(i)
6438             };
6439             
6440             var hh = '';
6441             
6442             if(typeof(config.sortable) != 'undefined' && config.sortable){
6443                 c.cls = 'sortable';
6444                 c.html = '<i class="glyphicon"></i>' + c.html;
6445             }
6446             
6447             if(typeof(config.lgHeader) != 'undefined'){
6448                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6449             }
6450             
6451             if(typeof(config.mdHeader) != 'undefined'){
6452                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6453             }
6454             
6455             if(typeof(config.smHeader) != 'undefined'){
6456                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6457             }
6458             
6459             if(typeof(config.xsHeader) != 'undefined'){
6460                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6461             }
6462             
6463             if(hh.length){
6464                 c.html = hh;
6465             }
6466             
6467             if(typeof(config.tooltip) != 'undefined'){
6468                 c.tooltip = config.tooltip;
6469             }
6470             
6471             if(typeof(config.colspan) != 'undefined'){
6472                 c.colspan = config.colspan;
6473             }
6474             
6475             if(typeof(config.hidden) != 'undefined' && config.hidden){
6476                 c.style += ' display:none;';
6477             }
6478             
6479             if(typeof(config.dataIndex) != 'undefined'){
6480                 c.sort = config.dataIndex;
6481             }
6482             
6483            
6484             
6485             if(typeof(config.align) != 'undefined' && config.align.length){
6486                 c.style += ' text-align:' + config.align + ';';
6487             }
6488             
6489             if(typeof(config.width) != 'undefined'){
6490                 c.style += ' width:' + config.width + 'px;';
6491                 this.totalWidth += config.width;
6492             } else {
6493                 this.totalWidth += 100; // assume minimum of 100 per column?
6494             }
6495             
6496             if(typeof(config.cls) != 'undefined'){
6497                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6498             }
6499             
6500             ['xs','sm','md','lg'].map(function(size){
6501                 
6502                 if(typeof(config[size]) == 'undefined'){
6503                     return;
6504                 }
6505                 
6506                 if (!config[size]) { // 0 = hidden
6507                     c.cls += ' hidden-' + size;
6508                     return;
6509                 }
6510                 
6511                 c.cls += ' col-' + size + '-' + config[size];
6512
6513             });
6514             
6515             header.cn.push(c)
6516         }
6517         
6518         return header;
6519     },
6520     
6521     renderBody : function()
6522     {
6523         var body = {
6524             tag: 'tbody',
6525             cn : [
6526                 {
6527                     tag: 'tr',
6528                     cn : [
6529                         {
6530                             tag : 'td',
6531                             colspan :  this.cm.getColumnCount()
6532                         }
6533                     ]
6534                 }
6535             ]
6536         };
6537         
6538         return body;
6539     },
6540     
6541     renderFooter : function()
6542     {
6543         var footer = {
6544             tag: 'tfoot',
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 footer;
6559     },
6560     
6561     
6562     
6563     onLoad : function()
6564     {
6565 //        Roo.log('ds onload');
6566         this.clear();
6567         
6568         var _this = this;
6569         var cm = this.cm;
6570         var ds = this.store;
6571         
6572         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6573             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6574             if (_this.store.sortInfo) {
6575                     
6576                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6577                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6578                 }
6579                 
6580                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6581                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6582                 }
6583             }
6584         });
6585         
6586         var tbody =  this.mainBody;
6587               
6588         if(ds.getCount() > 0){
6589             ds.data.each(function(d,rowIndex){
6590                 var row =  this.renderRow(cm, ds, rowIndex);
6591                 
6592                 tbody.createChild(row);
6593                 
6594                 var _this = this;
6595                 
6596                 if(row.cellObjects.length){
6597                     Roo.each(row.cellObjects, function(r){
6598                         _this.renderCellObject(r);
6599                     })
6600                 }
6601                 
6602             }, this);
6603         }
6604         
6605         Roo.each(this.el.select('tbody td', true).elements, function(e){
6606             e.on('mouseover', _this.onMouseover, _this);
6607         });
6608         
6609         Roo.each(this.el.select('tbody td', true).elements, function(e){
6610             e.on('mouseout', _this.onMouseout, _this);
6611         });
6612         this.fireEvent('rowsrendered', this);
6613         //if(this.loadMask){
6614         //    this.maskEl.hide();
6615         //}
6616         
6617         this.autoSize();
6618     },
6619     
6620     
6621     onUpdate : function(ds,record)
6622     {
6623         this.refreshRow(record);
6624         this.autoSize();
6625     },
6626     
6627     onRemove : function(ds, record, index, isUpdate){
6628         if(isUpdate !== true){
6629             this.fireEvent("beforerowremoved", this, index, record);
6630         }
6631         var bt = this.mainBody.dom;
6632         
6633         var rows = this.el.select('tbody > tr', true).elements;
6634         
6635         if(typeof(rows[index]) != 'undefined'){
6636             bt.removeChild(rows[index].dom);
6637         }
6638         
6639 //        if(bt.rows[index]){
6640 //            bt.removeChild(bt.rows[index]);
6641 //        }
6642         
6643         if(isUpdate !== true){
6644             //this.stripeRows(index);
6645             //this.syncRowHeights(index, index);
6646             //this.layout();
6647             this.fireEvent("rowremoved", this, index, record);
6648         }
6649     },
6650     
6651     onAdd : function(ds, records, rowIndex)
6652     {
6653         //Roo.log('on Add called');
6654         // - note this does not handle multiple adding very well..
6655         var bt = this.mainBody.dom;
6656         for (var i =0 ; i < records.length;i++) {
6657             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6658             //Roo.log(records[i]);
6659             //Roo.log(this.store.getAt(rowIndex+i));
6660             this.insertRow(this.store, rowIndex + i, false);
6661             return;
6662         }
6663         
6664     },
6665     
6666     
6667     refreshRow : function(record){
6668         var ds = this.store, index;
6669         if(typeof record == 'number'){
6670             index = record;
6671             record = ds.getAt(index);
6672         }else{
6673             index = ds.indexOf(record);
6674         }
6675         this.insertRow(ds, index, true);
6676         this.autoSize();
6677         this.onRemove(ds, record, index+1, true);
6678         this.autoSize();
6679         //this.syncRowHeights(index, index);
6680         //this.layout();
6681         this.fireEvent("rowupdated", this, index, record);
6682     },
6683     
6684     insertRow : function(dm, rowIndex, isUpdate){
6685         
6686         if(!isUpdate){
6687             this.fireEvent("beforerowsinserted", this, rowIndex);
6688         }
6689             //var s = this.getScrollState();
6690         var row = this.renderRow(this.cm, this.store, rowIndex);
6691         // insert before rowIndex..
6692         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6693         
6694         var _this = this;
6695                 
6696         if(row.cellObjects.length){
6697             Roo.each(row.cellObjects, function(r){
6698                 _this.renderCellObject(r);
6699             })
6700         }
6701             
6702         if(!isUpdate){
6703             this.fireEvent("rowsinserted", this, rowIndex);
6704             //this.syncRowHeights(firstRow, lastRow);
6705             //this.stripeRows(firstRow);
6706             //this.layout();
6707         }
6708         
6709     },
6710     
6711     
6712     getRowDom : function(rowIndex)
6713     {
6714         var rows = this.el.select('tbody > tr', true).elements;
6715         
6716         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6717         
6718     },
6719     // returns the object tree for a tr..
6720   
6721     
6722     renderRow : function(cm, ds, rowIndex) 
6723     {
6724         var d = ds.getAt(rowIndex);
6725         
6726         var row = {
6727             tag : 'tr',
6728             cls : 'x-row-' + rowIndex,
6729             cn : []
6730         };
6731             
6732         var cellObjects = [];
6733         
6734         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6735             var config = cm.config[i];
6736             
6737             var renderer = cm.getRenderer(i);
6738             var value = '';
6739             var id = false;
6740             
6741             if(typeof(renderer) !== 'undefined'){
6742                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6743             }
6744             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6745             // and are rendered into the cells after the row is rendered - using the id for the element.
6746             
6747             if(typeof(value) === 'object'){
6748                 id = Roo.id();
6749                 cellObjects.push({
6750                     container : id,
6751                     cfg : value 
6752                 })
6753             }
6754             
6755             var rowcfg = {
6756                 record: d,
6757                 rowIndex : rowIndex,
6758                 colIndex : i,
6759                 rowClass : ''
6760             };
6761
6762             this.fireEvent('rowclass', this, rowcfg);
6763             
6764             var td = {
6765                 tag: 'td',
6766                 cls : rowcfg.rowClass + ' x-col-' + i,
6767                 style: '',
6768                 html: (typeof(value) === 'object') ? '' : value
6769             };
6770             
6771             if (id) {
6772                 td.id = id;
6773             }
6774             
6775             if(typeof(config.colspan) != 'undefined'){
6776                 td.colspan = config.colspan;
6777             }
6778             
6779             if(typeof(config.hidden) != 'undefined' && config.hidden){
6780                 td.style += ' display:none;';
6781             }
6782             
6783             if(typeof(config.align) != 'undefined' && config.align.length){
6784                 td.style += ' text-align:' + config.align + ';';
6785             }
6786             
6787             if(typeof(config.width) != 'undefined'){
6788                 td.style += ' width:' +  config.width + 'px;';
6789             }
6790             
6791             if(typeof(config.cursor) != 'undefined'){
6792                 td.style += ' cursor:' +  config.cursor + ';';
6793             }
6794             
6795             if(typeof(config.cls) != 'undefined'){
6796                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6797             }
6798             
6799             ['xs','sm','md','lg'].map(function(size){
6800                 
6801                 if(typeof(config[size]) == 'undefined'){
6802                     return;
6803                 }
6804                 
6805                 if (!config[size]) { // 0 = hidden
6806                     td.cls += ' hidden-' + size;
6807                     return;
6808                 }
6809                 
6810                 td.cls += ' col-' + size + '-' + config[size];
6811
6812             });
6813             
6814             row.cn.push(td);
6815            
6816         }
6817         
6818         row.cellObjects = cellObjects;
6819         
6820         return row;
6821           
6822     },
6823     
6824     
6825     
6826     onBeforeLoad : function()
6827     {
6828         //Roo.log('ds onBeforeLoad');
6829         
6830         //this.clear();
6831         
6832         //if(this.loadMask){
6833         //    this.maskEl.show();
6834         //}
6835     },
6836      /**
6837      * Remove all rows
6838      */
6839     clear : function()
6840     {
6841         this.el.select('tbody', true).first().dom.innerHTML = '';
6842     },
6843     /**
6844      * Show or hide a row.
6845      * @param {Number} rowIndex to show or hide
6846      * @param {Boolean} state hide
6847      */
6848     setRowVisibility : function(rowIndex, state)
6849     {
6850         var bt = this.mainBody.dom;
6851         
6852         var rows = this.el.select('tbody > tr', true).elements;
6853         
6854         if(typeof(rows[rowIndex]) == 'undefined'){
6855             return;
6856         }
6857         rows[rowIndex].dom.style.display = state ? '' : 'none';
6858     },
6859     
6860     
6861     getSelectionModel : function(){
6862         if(!this.selModel){
6863             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6864         }
6865         return this.selModel;
6866     },
6867     /*
6868      * Render the Roo.bootstrap object from renderder
6869      */
6870     renderCellObject : function(r)
6871     {
6872         var _this = this;
6873         
6874         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6875         
6876         var t = r.cfg.render(r.container);
6877         
6878         if(r.cfg.cn){
6879             Roo.each(r.cfg.cn, function(c){
6880                 var child = {
6881                     container: t.getChildContainer(),
6882                     cfg: c
6883                 };
6884                 _this.renderCellObject(child);
6885             })
6886         }
6887     },
6888     
6889     getRowIndex : function(row)
6890     {
6891         var rowIndex = -1;
6892         
6893         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6894             if(el != row){
6895                 return;
6896             }
6897             
6898             rowIndex = index;
6899         });
6900         
6901         return rowIndex;
6902     },
6903      /**
6904      * Returns the grid's underlying element = used by panel.Grid
6905      * @return {Element} The element
6906      */
6907     getGridEl : function(){
6908         return this.el;
6909     },
6910      /**
6911      * Forces a resize - used by panel.Grid
6912      * @return {Element} The element
6913      */
6914     autoSize : function()
6915     {
6916         //var ctr = Roo.get(this.container.dom.parentElement);
6917         var ctr = Roo.get(this.el.dom);
6918         
6919         var thd = this.getGridEl().select('thead',true).first();
6920         var tbd = this.getGridEl().select('tbody', true).first();
6921         var tfd = this.getGridEl().select('tfoot', true).first();
6922         
6923         var cw = ctr.getWidth();
6924         
6925         if (tbd) {
6926             
6927             tbd.setSize(ctr.getWidth(),
6928                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6929             );
6930             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6931             cw -= barsize;
6932         }
6933         cw = Math.max(cw, this.totalWidth);
6934         this.getGridEl().select('tr',true).setWidth(cw);
6935         // resize 'expandable coloumn?
6936         
6937         return; // we doe not have a view in this design..
6938         
6939     },
6940     onBodyScroll: function()
6941     {
6942         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6943         if(this.mainHead){
6944             this.mainHead.setStyle({
6945                 'position' : 'relative',
6946                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6947             });
6948         }
6949         
6950         if(this.lazyLoad){
6951             
6952             var scrollHeight = this.mainBody.dom.scrollHeight;
6953             
6954             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6955             
6956             var height = this.mainBody.getHeight();
6957             
6958             if(scrollHeight - height == scrollTop) {
6959                 
6960                 var total = this.ds.getTotalCount();
6961                 
6962                 if(this.footer.cursor + this.footer.pageSize < total){
6963                     
6964                     this.footer.ds.load({
6965                         params : {
6966                             start : this.footer.cursor + this.footer.pageSize,
6967                             limit : this.footer.pageSize
6968                         },
6969                         add : true
6970                     });
6971                 }
6972             }
6973             
6974         }
6975     },
6976     
6977     onHeaderChange : function()
6978     {
6979         var header = this.renderHeader();
6980         var table = this.el.select('table', true).first();
6981         
6982         this.mainHead.remove();
6983         this.mainHead = table.createChild(header, this.mainBody, false);
6984     },
6985     
6986     onHiddenChange : function(colModel, colIndex, hidden)
6987     {
6988         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6989         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6990         
6991         this.CSS.updateRule(thSelector, "display", "");
6992         this.CSS.updateRule(tdSelector, "display", "");
6993         
6994         if(hidden){
6995             this.CSS.updateRule(thSelector, "display", "none");
6996             this.CSS.updateRule(tdSelector, "display", "none");
6997         }
6998         
6999         this.onHeaderChange();
7000         this.onLoad();
7001         
7002     }
7003     
7004 });
7005
7006  
7007
7008  /*
7009  * - LGPL
7010  *
7011  * table cell
7012  * 
7013  */
7014
7015 /**
7016  * @class Roo.bootstrap.TableCell
7017  * @extends Roo.bootstrap.Component
7018  * Bootstrap TableCell class
7019  * @cfg {String} html cell contain text
7020  * @cfg {String} cls cell class
7021  * @cfg {String} tag cell tag (td|th) default td
7022  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7023  * @cfg {String} align Aligns the content in a cell
7024  * @cfg {String} axis Categorizes cells
7025  * @cfg {String} bgcolor Specifies the background color of a cell
7026  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7027  * @cfg {Number} colspan Specifies the number of columns a cell should span
7028  * @cfg {String} headers Specifies one or more header cells a cell is related to
7029  * @cfg {Number} height Sets the height of a cell
7030  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7031  * @cfg {Number} rowspan Sets the number of rows a cell should span
7032  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7033  * @cfg {String} valign Vertical aligns the content in a cell
7034  * @cfg {Number} width Specifies the width of a cell
7035  * 
7036  * @constructor
7037  * Create a new TableCell
7038  * @param {Object} config The config object
7039  */
7040
7041 Roo.bootstrap.TableCell = function(config){
7042     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7043 };
7044
7045 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7046     
7047     html: false,
7048     cls: false,
7049     tag: false,
7050     abbr: false,
7051     align: false,
7052     axis: false,
7053     bgcolor: false,
7054     charoff: false,
7055     colspan: false,
7056     headers: false,
7057     height: false,
7058     nowrap: false,
7059     rowspan: false,
7060     scope: false,
7061     valign: false,
7062     width: false,
7063     
7064     
7065     getAutoCreate : function(){
7066         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7067         
7068         cfg = {
7069             tag: 'td'
7070         };
7071         
7072         if(this.tag){
7073             cfg.tag = this.tag;
7074         }
7075         
7076         if (this.html) {
7077             cfg.html=this.html
7078         }
7079         if (this.cls) {
7080             cfg.cls=this.cls
7081         }
7082         if (this.abbr) {
7083             cfg.abbr=this.abbr
7084         }
7085         if (this.align) {
7086             cfg.align=this.align
7087         }
7088         if (this.axis) {
7089             cfg.axis=this.axis
7090         }
7091         if (this.bgcolor) {
7092             cfg.bgcolor=this.bgcolor
7093         }
7094         if (this.charoff) {
7095             cfg.charoff=this.charoff
7096         }
7097         if (this.colspan) {
7098             cfg.colspan=this.colspan
7099         }
7100         if (this.headers) {
7101             cfg.headers=this.headers
7102         }
7103         if (this.height) {
7104             cfg.height=this.height
7105         }
7106         if (this.nowrap) {
7107             cfg.nowrap=this.nowrap
7108         }
7109         if (this.rowspan) {
7110             cfg.rowspan=this.rowspan
7111         }
7112         if (this.scope) {
7113             cfg.scope=this.scope
7114         }
7115         if (this.valign) {
7116             cfg.valign=this.valign
7117         }
7118         if (this.width) {
7119             cfg.width=this.width
7120         }
7121         
7122         
7123         return cfg;
7124     }
7125    
7126 });
7127
7128  
7129
7130  /*
7131  * - LGPL
7132  *
7133  * table row
7134  * 
7135  */
7136
7137 /**
7138  * @class Roo.bootstrap.TableRow
7139  * @extends Roo.bootstrap.Component
7140  * Bootstrap TableRow class
7141  * @cfg {String} cls row class
7142  * @cfg {String} align Aligns the content in a table row
7143  * @cfg {String} bgcolor Specifies a background color for a table row
7144  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7145  * @cfg {String} valign Vertical aligns the content in a table row
7146  * 
7147  * @constructor
7148  * Create a new TableRow
7149  * @param {Object} config The config object
7150  */
7151
7152 Roo.bootstrap.TableRow = function(config){
7153     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7154 };
7155
7156 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7157     
7158     cls: false,
7159     align: false,
7160     bgcolor: false,
7161     charoff: false,
7162     valign: false,
7163     
7164     getAutoCreate : function(){
7165         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7166         
7167         cfg = {
7168             tag: 'tr'
7169         };
7170             
7171         if(this.cls){
7172             cfg.cls = this.cls;
7173         }
7174         if(this.align){
7175             cfg.align = this.align;
7176         }
7177         if(this.bgcolor){
7178             cfg.bgcolor = this.bgcolor;
7179         }
7180         if(this.charoff){
7181             cfg.charoff = this.charoff;
7182         }
7183         if(this.valign){
7184             cfg.valign = this.valign;
7185         }
7186         
7187         return cfg;
7188     }
7189    
7190 });
7191
7192  
7193
7194  /*
7195  * - LGPL
7196  *
7197  * table body
7198  * 
7199  */
7200
7201 /**
7202  * @class Roo.bootstrap.TableBody
7203  * @extends Roo.bootstrap.Component
7204  * Bootstrap TableBody class
7205  * @cfg {String} cls element class
7206  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7207  * @cfg {String} align Aligns the content inside the element
7208  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7209  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7210  * 
7211  * @constructor
7212  * Create a new TableBody
7213  * @param {Object} config The config object
7214  */
7215
7216 Roo.bootstrap.TableBody = function(config){
7217     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7218 };
7219
7220 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7221     
7222     cls: false,
7223     tag: false,
7224     align: false,
7225     charoff: false,
7226     valign: false,
7227     
7228     getAutoCreate : function(){
7229         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7230         
7231         cfg = {
7232             tag: 'tbody'
7233         };
7234             
7235         if (this.cls) {
7236             cfg.cls=this.cls
7237         }
7238         if(this.tag){
7239             cfg.tag = this.tag;
7240         }
7241         
7242         if(this.align){
7243             cfg.align = this.align;
7244         }
7245         if(this.charoff){
7246             cfg.charoff = this.charoff;
7247         }
7248         if(this.valign){
7249             cfg.valign = this.valign;
7250         }
7251         
7252         return cfg;
7253     }
7254     
7255     
7256 //    initEvents : function()
7257 //    {
7258 //        
7259 //        if(!this.store){
7260 //            return;
7261 //        }
7262 //        
7263 //        this.store = Roo.factory(this.store, Roo.data);
7264 //        this.store.on('load', this.onLoad, this);
7265 //        
7266 //        this.store.load();
7267 //        
7268 //    },
7269 //    
7270 //    onLoad: function () 
7271 //    {   
7272 //        this.fireEvent('load', this);
7273 //    }
7274 //    
7275 //   
7276 });
7277
7278  
7279
7280  /*
7281  * Based on:
7282  * Ext JS Library 1.1.1
7283  * Copyright(c) 2006-2007, Ext JS, LLC.
7284  *
7285  * Originally Released Under LGPL - original licence link has changed is not relivant.
7286  *
7287  * Fork - LGPL
7288  * <script type="text/javascript">
7289  */
7290
7291 // as we use this in bootstrap.
7292 Roo.namespace('Roo.form');
7293  /**
7294  * @class Roo.form.Action
7295  * Internal Class used to handle form actions
7296  * @constructor
7297  * @param {Roo.form.BasicForm} el The form element or its id
7298  * @param {Object} config Configuration options
7299  */
7300
7301  
7302  
7303 // define the action interface
7304 Roo.form.Action = function(form, options){
7305     this.form = form;
7306     this.options = options || {};
7307 };
7308 /**
7309  * Client Validation Failed
7310  * @const 
7311  */
7312 Roo.form.Action.CLIENT_INVALID = 'client';
7313 /**
7314  * Server Validation Failed
7315  * @const 
7316  */
7317 Roo.form.Action.SERVER_INVALID = 'server';
7318  /**
7319  * Connect to Server Failed
7320  * @const 
7321  */
7322 Roo.form.Action.CONNECT_FAILURE = 'connect';
7323 /**
7324  * Reading Data from Server Failed
7325  * @const 
7326  */
7327 Roo.form.Action.LOAD_FAILURE = 'load';
7328
7329 Roo.form.Action.prototype = {
7330     type : 'default',
7331     failureType : undefined,
7332     response : undefined,
7333     result : undefined,
7334
7335     // interface method
7336     run : function(options){
7337
7338     },
7339
7340     // interface method
7341     success : function(response){
7342
7343     },
7344
7345     // interface method
7346     handleResponse : function(response){
7347
7348     },
7349
7350     // default connection failure
7351     failure : function(response){
7352         
7353         this.response = response;
7354         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7355         this.form.afterAction(this, false);
7356     },
7357
7358     processResponse : function(response){
7359         this.response = response;
7360         if(!response.responseText){
7361             return true;
7362         }
7363         this.result = this.handleResponse(response);
7364         return this.result;
7365     },
7366
7367     // utility functions used internally
7368     getUrl : function(appendParams){
7369         var url = this.options.url || this.form.url || this.form.el.dom.action;
7370         if(appendParams){
7371             var p = this.getParams();
7372             if(p){
7373                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7374             }
7375         }
7376         return url;
7377     },
7378
7379     getMethod : function(){
7380         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7381     },
7382
7383     getParams : function(){
7384         var bp = this.form.baseParams;
7385         var p = this.options.params;
7386         if(p){
7387             if(typeof p == "object"){
7388                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7389             }else if(typeof p == 'string' && bp){
7390                 p += '&' + Roo.urlEncode(bp);
7391             }
7392         }else if(bp){
7393             p = Roo.urlEncode(bp);
7394         }
7395         return p;
7396     },
7397
7398     createCallback : function(){
7399         return {
7400             success: this.success,
7401             failure: this.failure,
7402             scope: this,
7403             timeout: (this.form.timeout*1000),
7404             upload: this.form.fileUpload ? this.success : undefined
7405         };
7406     }
7407 };
7408
7409 Roo.form.Action.Submit = function(form, options){
7410     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7411 };
7412
7413 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7414     type : 'submit',
7415
7416     haveProgress : false,
7417     uploadComplete : false,
7418     
7419     // uploadProgress indicator.
7420     uploadProgress : function()
7421     {
7422         if (!this.form.progressUrl) {
7423             return;
7424         }
7425         
7426         if (!this.haveProgress) {
7427             Roo.MessageBox.progress("Uploading", "Uploading");
7428         }
7429         if (this.uploadComplete) {
7430            Roo.MessageBox.hide();
7431            return;
7432         }
7433         
7434         this.haveProgress = true;
7435    
7436         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7437         
7438         var c = new Roo.data.Connection();
7439         c.request({
7440             url : this.form.progressUrl,
7441             params: {
7442                 id : uid
7443             },
7444             method: 'GET',
7445             success : function(req){
7446                //console.log(data);
7447                 var rdata = false;
7448                 var edata;
7449                 try  {
7450                    rdata = Roo.decode(req.responseText)
7451                 } catch (e) {
7452                     Roo.log("Invalid data from server..");
7453                     Roo.log(edata);
7454                     return;
7455                 }
7456                 if (!rdata || !rdata.success) {
7457                     Roo.log(rdata);
7458                     Roo.MessageBox.alert(Roo.encode(rdata));
7459                     return;
7460                 }
7461                 var data = rdata.data;
7462                 
7463                 if (this.uploadComplete) {
7464                    Roo.MessageBox.hide();
7465                    return;
7466                 }
7467                    
7468                 if (data){
7469                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7470                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7471                     );
7472                 }
7473                 this.uploadProgress.defer(2000,this);
7474             },
7475        
7476             failure: function(data) {
7477                 Roo.log('progress url failed ');
7478                 Roo.log(data);
7479             },
7480             scope : this
7481         });
7482            
7483     },
7484     
7485     
7486     run : function()
7487     {
7488         // run get Values on the form, so it syncs any secondary forms.
7489         this.form.getValues();
7490         
7491         var o = this.options;
7492         var method = this.getMethod();
7493         var isPost = method == 'POST';
7494         if(o.clientValidation === false || this.form.isValid()){
7495             
7496             if (this.form.progressUrl) {
7497                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7498                     (new Date() * 1) + '' + Math.random());
7499                     
7500             } 
7501             
7502             
7503             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7504                 form:this.form.el.dom,
7505                 url:this.getUrl(!isPost),
7506                 method: method,
7507                 params:isPost ? this.getParams() : null,
7508                 isUpload: this.form.fileUpload
7509             }));
7510             
7511             this.uploadProgress();
7512
7513         }else if (o.clientValidation !== false){ // client validation failed
7514             this.failureType = Roo.form.Action.CLIENT_INVALID;
7515             this.form.afterAction(this, false);
7516         }
7517     },
7518
7519     success : function(response)
7520     {
7521         this.uploadComplete= true;
7522         if (this.haveProgress) {
7523             Roo.MessageBox.hide();
7524         }
7525         
7526         
7527         var result = this.processResponse(response);
7528         if(result === true || result.success){
7529             this.form.afterAction(this, true);
7530             return;
7531         }
7532         if(result.errors){
7533             this.form.markInvalid(result.errors);
7534             this.failureType = Roo.form.Action.SERVER_INVALID;
7535         }
7536         this.form.afterAction(this, false);
7537     },
7538     failure : function(response)
7539     {
7540         this.uploadComplete= true;
7541         if (this.haveProgress) {
7542             Roo.MessageBox.hide();
7543         }
7544         
7545         this.response = response;
7546         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7547         this.form.afterAction(this, false);
7548     },
7549     
7550     handleResponse : function(response){
7551         if(this.form.errorReader){
7552             var rs = this.form.errorReader.read(response);
7553             var errors = [];
7554             if(rs.records){
7555                 for(var i = 0, len = rs.records.length; i < len; i++) {
7556                     var r = rs.records[i];
7557                     errors[i] = r.data;
7558                 }
7559             }
7560             if(errors.length < 1){
7561                 errors = null;
7562             }
7563             return {
7564                 success : rs.success,
7565                 errors : errors
7566             };
7567         }
7568         var ret = false;
7569         try {
7570             ret = Roo.decode(response.responseText);
7571         } catch (e) {
7572             ret = {
7573                 success: false,
7574                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7575                 errors : []
7576             };
7577         }
7578         return ret;
7579         
7580     }
7581 });
7582
7583
7584 Roo.form.Action.Load = function(form, options){
7585     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7586     this.reader = this.form.reader;
7587 };
7588
7589 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7590     type : 'load',
7591
7592     run : function(){
7593         
7594         Roo.Ajax.request(Roo.apply(
7595                 this.createCallback(), {
7596                     method:this.getMethod(),
7597                     url:this.getUrl(false),
7598                     params:this.getParams()
7599         }));
7600     },
7601
7602     success : function(response){
7603         
7604         var result = this.processResponse(response);
7605         if(result === true || !result.success || !result.data){
7606             this.failureType = Roo.form.Action.LOAD_FAILURE;
7607             this.form.afterAction(this, false);
7608             return;
7609         }
7610         this.form.clearInvalid();
7611         this.form.setValues(result.data);
7612         this.form.afterAction(this, true);
7613     },
7614
7615     handleResponse : function(response){
7616         if(this.form.reader){
7617             var rs = this.form.reader.read(response);
7618             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7619             return {
7620                 success : rs.success,
7621                 data : data
7622             };
7623         }
7624         return Roo.decode(response.responseText);
7625     }
7626 });
7627
7628 Roo.form.Action.ACTION_TYPES = {
7629     'load' : Roo.form.Action.Load,
7630     'submit' : Roo.form.Action.Submit
7631 };/*
7632  * - LGPL
7633  *
7634  * form
7635  *
7636  */
7637
7638 /**
7639  * @class Roo.bootstrap.Form
7640  * @extends Roo.bootstrap.Component
7641  * Bootstrap Form class
7642  * @cfg {String} method  GET | POST (default POST)
7643  * @cfg {String} labelAlign top | left (default top)
7644  * @cfg {String} align left  | right - for navbars
7645  * @cfg {Boolean} loadMask load mask when submit (default true)
7646
7647  *
7648  * @constructor
7649  * Create a new Form
7650  * @param {Object} config The config object
7651  */
7652
7653
7654 Roo.bootstrap.Form = function(config){
7655     
7656     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7657     
7658     Roo.bootstrap.Form.popover.apply();
7659     
7660     this.addEvents({
7661         /**
7662          * @event clientvalidation
7663          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7664          * @param {Form} this
7665          * @param {Boolean} valid true if the form has passed client-side validation
7666          */
7667         clientvalidation: true,
7668         /**
7669          * @event beforeaction
7670          * Fires before any action is performed. Return false to cancel the action.
7671          * @param {Form} this
7672          * @param {Action} action The action to be performed
7673          */
7674         beforeaction: true,
7675         /**
7676          * @event actionfailed
7677          * Fires when an action fails.
7678          * @param {Form} this
7679          * @param {Action} action The action that failed
7680          */
7681         actionfailed : true,
7682         /**
7683          * @event actioncomplete
7684          * Fires when an action is completed.
7685          * @param {Form} this
7686          * @param {Action} action The action that completed
7687          */
7688         actioncomplete : true
7689     });
7690 };
7691
7692 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7693
7694      /**
7695      * @cfg {String} method
7696      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7697      */
7698     method : 'POST',
7699     /**
7700      * @cfg {String} url
7701      * The URL to use for form actions if one isn't supplied in the action options.
7702      */
7703     /**
7704      * @cfg {Boolean} fileUpload
7705      * Set to true if this form is a file upload.
7706      */
7707
7708     /**
7709      * @cfg {Object} baseParams
7710      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7711      */
7712
7713     /**
7714      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7715      */
7716     timeout: 30,
7717     /**
7718      * @cfg {Sting} align (left|right) for navbar forms
7719      */
7720     align : 'left',
7721
7722     // private
7723     activeAction : null,
7724
7725     /**
7726      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7727      * element by passing it or its id or mask the form itself by passing in true.
7728      * @type Mixed
7729      */
7730     waitMsgTarget : false,
7731
7732     loadMask : true,
7733     
7734     /**
7735      * @cfg {Boolean} errorMask (true|false) default false
7736      */
7737     errorMask : false,
7738     
7739     /**
7740      * @cfg {Number} maskOffset Default 100
7741      */
7742     maskOffset : 100,
7743     
7744     /**
7745      * @cfg {Boolean} maskBody
7746      */
7747     maskBody : false,
7748
7749     getAutoCreate : function(){
7750
7751         var cfg = {
7752             tag: 'form',
7753             method : this.method || 'POST',
7754             id : this.id || Roo.id(),
7755             cls : ''
7756         };
7757         if (this.parent().xtype.match(/^Nav/)) {
7758             cfg.cls = 'navbar-form navbar-' + this.align;
7759
7760         }
7761
7762         if (this.labelAlign == 'left' ) {
7763             cfg.cls += ' form-horizontal';
7764         }
7765
7766
7767         return cfg;
7768     },
7769     initEvents : function()
7770     {
7771         this.el.on('submit', this.onSubmit, this);
7772         // this was added as random key presses on the form where triggering form submit.
7773         this.el.on('keypress', function(e) {
7774             if (e.getCharCode() != 13) {
7775                 return true;
7776             }
7777             // we might need to allow it for textareas.. and some other items.
7778             // check e.getTarget().
7779
7780             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7781                 return true;
7782             }
7783
7784             Roo.log("keypress blocked");
7785
7786             e.preventDefault();
7787             return false;
7788         });
7789         
7790     },
7791     // private
7792     onSubmit : function(e){
7793         e.stopEvent();
7794     },
7795
7796      /**
7797      * Returns true if client-side validation on the form is successful.
7798      * @return Boolean
7799      */
7800     isValid : function(){
7801         var items = this.getItems();
7802         var valid = true;
7803         var target = false;
7804         
7805         items.each(function(f){
7806             
7807             if(f.validate()){
7808                 return;
7809             }
7810             valid = false;
7811
7812             if(!target && f.el.isVisible(true)){
7813                 target = f;
7814             }
7815            
7816         });
7817         
7818         if(this.errorMask && !valid){
7819             Roo.bootstrap.Form.popover.mask(this, target);
7820         }
7821         
7822         return valid;
7823     },
7824     
7825     /**
7826      * Returns true if any fields in this form have changed since their original load.
7827      * @return Boolean
7828      */
7829     isDirty : function(){
7830         var dirty = false;
7831         var items = this.getItems();
7832         items.each(function(f){
7833            if(f.isDirty()){
7834                dirty = true;
7835                return false;
7836            }
7837            return true;
7838         });
7839         return dirty;
7840     },
7841      /**
7842      * Performs a predefined action (submit or load) or custom actions you define on this form.
7843      * @param {String} actionName The name of the action type
7844      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7845      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7846      * accept other config options):
7847      * <pre>
7848 Property          Type             Description
7849 ----------------  ---------------  ----------------------------------------------------------------------------------
7850 url               String           The url for the action (defaults to the form's url)
7851 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7852 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7853 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7854                                    validate the form on the client (defaults to false)
7855      * </pre>
7856      * @return {BasicForm} this
7857      */
7858     doAction : function(action, options){
7859         if(typeof action == 'string'){
7860             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7861         }
7862         if(this.fireEvent('beforeaction', this, action) !== false){
7863             this.beforeAction(action);
7864             action.run.defer(100, action);
7865         }
7866         return this;
7867     },
7868
7869     // private
7870     beforeAction : function(action){
7871         var o = action.options;
7872         
7873         if(this.loadMask){
7874             
7875             if(this.maskBody){
7876                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7877             } else {
7878                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7879             }
7880         }
7881         // not really supported yet.. ??
7882
7883         //if(this.waitMsgTarget === true){
7884         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7885         //}else if(this.waitMsgTarget){
7886         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7887         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7888         //}else {
7889         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7890        // }
7891
7892     },
7893
7894     // private
7895     afterAction : function(action, success){
7896         this.activeAction = null;
7897         var o = action.options;
7898
7899         if(this.loadMask){
7900             
7901             if(this.maskBody){
7902                 Roo.get(document.body).unmask();
7903             } else {
7904                 this.el.unmask();
7905             }
7906         }
7907         
7908         //if(this.waitMsgTarget === true){
7909 //            this.el.unmask();
7910         //}else if(this.waitMsgTarget){
7911         //    this.waitMsgTarget.unmask();
7912         //}else{
7913         //    Roo.MessageBox.updateProgress(1);
7914         //    Roo.MessageBox.hide();
7915        // }
7916         //
7917         if(success){
7918             if(o.reset){
7919                 this.reset();
7920             }
7921             Roo.callback(o.success, o.scope, [this, action]);
7922             this.fireEvent('actioncomplete', this, action);
7923
7924         }else{
7925
7926             // failure condition..
7927             // we have a scenario where updates need confirming.
7928             // eg. if a locking scenario exists..
7929             // we look for { errors : { needs_confirm : true }} in the response.
7930             if (
7931                 (typeof(action.result) != 'undefined')  &&
7932                 (typeof(action.result.errors) != 'undefined')  &&
7933                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7934            ){
7935                 var _t = this;
7936                 Roo.log("not supported yet");
7937                  /*
7938
7939                 Roo.MessageBox.confirm(
7940                     "Change requires confirmation",
7941                     action.result.errorMsg,
7942                     function(r) {
7943                         if (r != 'yes') {
7944                             return;
7945                         }
7946                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7947                     }
7948
7949                 );
7950                 */
7951
7952
7953                 return;
7954             }
7955
7956             Roo.callback(o.failure, o.scope, [this, action]);
7957             // show an error message if no failed handler is set..
7958             if (!this.hasListener('actionfailed')) {
7959                 Roo.log("need to add dialog support");
7960                 /*
7961                 Roo.MessageBox.alert("Error",
7962                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7963                         action.result.errorMsg :
7964                         "Saving Failed, please check your entries or try again"
7965                 );
7966                 */
7967             }
7968
7969             this.fireEvent('actionfailed', this, action);
7970         }
7971
7972     },
7973     /**
7974      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7975      * @param {String} id The value to search for
7976      * @return Field
7977      */
7978     findField : function(id){
7979         var items = this.getItems();
7980         var field = items.get(id);
7981         if(!field){
7982              items.each(function(f){
7983                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7984                     field = f;
7985                     return false;
7986                 }
7987                 return true;
7988             });
7989         }
7990         return field || null;
7991     },
7992      /**
7993      * Mark fields in this form invalid in bulk.
7994      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7995      * @return {BasicForm} this
7996      */
7997     markInvalid : function(errors){
7998         if(errors instanceof Array){
7999             for(var i = 0, len = errors.length; i < len; i++){
8000                 var fieldError = errors[i];
8001                 var f = this.findField(fieldError.id);
8002                 if(f){
8003                     f.markInvalid(fieldError.msg);
8004                 }
8005             }
8006         }else{
8007             var field, id;
8008             for(id in errors){
8009                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8010                     field.markInvalid(errors[id]);
8011                 }
8012             }
8013         }
8014         //Roo.each(this.childForms || [], function (f) {
8015         //    f.markInvalid(errors);
8016         //});
8017
8018         return this;
8019     },
8020
8021     /**
8022      * Set values for fields in this form in bulk.
8023      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8024      * @return {BasicForm} this
8025      */
8026     setValues : function(values){
8027         if(values instanceof Array){ // array of objects
8028             for(var i = 0, len = values.length; i < len; i++){
8029                 var v = values[i];
8030                 var f = this.findField(v.id);
8031                 if(f){
8032                     f.setValue(v.value);
8033                     if(this.trackResetOnLoad){
8034                         f.originalValue = f.getValue();
8035                     }
8036                 }
8037             }
8038         }else{ // object hash
8039             var field, id;
8040             for(id in values){
8041                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8042
8043                     if (field.setFromData &&
8044                         field.valueField &&
8045                         field.displayField &&
8046                         // combos' with local stores can
8047                         // be queried via setValue()
8048                         // to set their value..
8049                         (field.store && !field.store.isLocal)
8050                         ) {
8051                         // it's a combo
8052                         var sd = { };
8053                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8054                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8055                         field.setFromData(sd);
8056
8057                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8058                         
8059                         field.setFromData(values);
8060                         
8061                     } else {
8062                         field.setValue(values[id]);
8063                     }
8064
8065
8066                     if(this.trackResetOnLoad){
8067                         field.originalValue = field.getValue();
8068                     }
8069                 }
8070             }
8071         }
8072
8073         //Roo.each(this.childForms || [], function (f) {
8074         //    f.setValues(values);
8075         //});
8076
8077         return this;
8078     },
8079
8080     /**
8081      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8082      * they are returned as an array.
8083      * @param {Boolean} asString
8084      * @return {Object}
8085      */
8086     getValues : function(asString){
8087         //if (this.childForms) {
8088             // copy values from the child forms
8089         //    Roo.each(this.childForms, function (f) {
8090         //        this.setValues(f.getValues());
8091         //    }, this);
8092         //}
8093
8094
8095
8096         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8097         if(asString === true){
8098             return fs;
8099         }
8100         return Roo.urlDecode(fs);
8101     },
8102
8103     /**
8104      * Returns the fields in this form as an object with key/value pairs.
8105      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8106      * @return {Object}
8107      */
8108     getFieldValues : function(with_hidden)
8109     {
8110         var items = this.getItems();
8111         var ret = {};
8112         items.each(function(f){
8113             
8114             if (!f.getName()) {
8115                 return;
8116             }
8117             
8118             var v = f.getValue();
8119             
8120             if (f.inputType =='radio') {
8121                 if (typeof(ret[f.getName()]) == 'undefined') {
8122                     ret[f.getName()] = ''; // empty..
8123                 }
8124
8125                 if (!f.el.dom.checked) {
8126                     return;
8127
8128                 }
8129                 v = f.el.dom.value;
8130
8131             }
8132             
8133             if(f.xtype == 'MoneyField'){
8134                 ret[f.currencyName] = f.getCurrency();
8135             }
8136
8137             // not sure if this supported any more..
8138             if ((typeof(v) == 'object') && f.getRawValue) {
8139                 v = f.getRawValue() ; // dates..
8140             }
8141             // combo boxes where name != hiddenName...
8142             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8143                 ret[f.name] = f.getRawValue();
8144             }
8145             ret[f.getName()] = v;
8146         });
8147
8148         return ret;
8149     },
8150
8151     /**
8152      * Clears all invalid messages in this form.
8153      * @return {BasicForm} this
8154      */
8155     clearInvalid : function(){
8156         var items = this.getItems();
8157
8158         items.each(function(f){
8159            f.clearInvalid();
8160         });
8161
8162         return this;
8163     },
8164
8165     /**
8166      * Resets this form.
8167      * @return {BasicForm} this
8168      */
8169     reset : function(){
8170         var items = this.getItems();
8171         items.each(function(f){
8172             f.reset();
8173         });
8174
8175         Roo.each(this.childForms || [], function (f) {
8176             f.reset();
8177         });
8178
8179
8180         return this;
8181     },
8182     
8183     getItems : function()
8184     {
8185         var r=new Roo.util.MixedCollection(false, function(o){
8186             return o.id || (o.id = Roo.id());
8187         });
8188         var iter = function(el) {
8189             if (el.inputEl) {
8190                 r.add(el);
8191             }
8192             if (!el.items) {
8193                 return;
8194             }
8195             Roo.each(el.items,function(e) {
8196                 iter(e);
8197             });
8198         };
8199
8200         iter(this);
8201         return r;
8202     },
8203     
8204     hideFields : function(items)
8205     {
8206         Roo.each(items, function(i){
8207             
8208             var f = this.findField(i);
8209             
8210             if(!f){
8211                 return;
8212             }
8213             
8214             if(f.xtype == 'DateField'){
8215                 f.setVisible(false);
8216                 return;
8217             }
8218             
8219             f.hide();
8220             
8221         }, this);
8222     },
8223     
8224     showFields : function(items)
8225     {
8226         Roo.each(items, function(i){
8227             
8228             var f = this.findField(i);
8229             
8230             if(!f){
8231                 return;
8232             }
8233             
8234             if(f.xtype == 'DateField'){
8235                 f.setVisible(true);
8236                 return;
8237             }
8238             
8239             f.show();
8240             
8241         }, this);
8242     }
8243
8244 });
8245
8246 Roo.apply(Roo.bootstrap.Form, {
8247     
8248     popover : {
8249         
8250         padding : 5,
8251         
8252         isApplied : false,
8253         
8254         isMasked : false,
8255         
8256         form : false,
8257         
8258         target : false,
8259         
8260         toolTip : false,
8261         
8262         intervalID : false,
8263         
8264         maskEl : false,
8265         
8266         apply : function()
8267         {
8268             if(this.isApplied){
8269                 return;
8270             }
8271             
8272             this.maskEl = {
8273                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8274                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8275                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8276                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8277             };
8278             
8279             this.maskEl.top.enableDisplayMode("block");
8280             this.maskEl.left.enableDisplayMode("block");
8281             this.maskEl.bottom.enableDisplayMode("block");
8282             this.maskEl.right.enableDisplayMode("block");
8283             
8284             this.toolTip = new Roo.bootstrap.Tooltip({
8285                 cls : 'roo-form-error-popover',
8286                 alignment : {
8287                     'left' : ['r-l', [-2,0], 'right'],
8288                     'right' : ['l-r', [2,0], 'left'],
8289                     'bottom' : ['tl-bl', [0,2], 'top'],
8290                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8291                 }
8292             });
8293             
8294             this.toolTip.render(Roo.get(document.body));
8295
8296             this.toolTip.el.enableDisplayMode("block");
8297             
8298             Roo.get(document.body).on('click', function(){
8299                 this.unmask();
8300             }, this);
8301             
8302             Roo.get(document.body).on('touchstart', function(){
8303                 this.unmask();
8304             }, this);
8305             
8306             this.isApplied = true
8307         },
8308         
8309         mask : function(form, target)
8310         {
8311             this.form = form;
8312             
8313             this.target = target;
8314             
8315             if(!this.form.errorMask || !target.el){
8316                 return;
8317             }
8318             
8319             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8320             
8321             Roo.log(scrollable);
8322             
8323             var ot = this.target.el.calcOffsetsTo(scrollable);
8324             
8325             var scrollTo = ot[1] - this.form.maskOffset;
8326             
8327             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8328             
8329             scrollable.scrollTo('top', scrollTo);
8330             
8331             var box = this.target.el.getBox();
8332             Roo.log(box);
8333             var zIndex = Roo.bootstrap.Modal.zIndex++;
8334
8335             
8336             this.maskEl.top.setStyle('position', 'absolute');
8337             this.maskEl.top.setStyle('z-index', zIndex);
8338             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8339             this.maskEl.top.setLeft(0);
8340             this.maskEl.top.setTop(0);
8341             this.maskEl.top.show();
8342             
8343             this.maskEl.left.setStyle('position', 'absolute');
8344             this.maskEl.left.setStyle('z-index', zIndex);
8345             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8346             this.maskEl.left.setLeft(0);
8347             this.maskEl.left.setTop(box.y - this.padding);
8348             this.maskEl.left.show();
8349
8350             this.maskEl.bottom.setStyle('position', 'absolute');
8351             this.maskEl.bottom.setStyle('z-index', zIndex);
8352             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8353             this.maskEl.bottom.setLeft(0);
8354             this.maskEl.bottom.setTop(box.bottom + this.padding);
8355             this.maskEl.bottom.show();
8356
8357             this.maskEl.right.setStyle('position', 'absolute');
8358             this.maskEl.right.setStyle('z-index', zIndex);
8359             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8360             this.maskEl.right.setLeft(box.right + this.padding);
8361             this.maskEl.right.setTop(box.y - this.padding);
8362             this.maskEl.right.show();
8363
8364             this.toolTip.bindEl = this.target.el;
8365
8366             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8367
8368             var tip = this.target.blankText;
8369
8370             if(this.target.getValue() !== '' ) {
8371                 
8372                 if (this.target.invalidText.length) {
8373                     tip = this.target.invalidText;
8374                 } else if (this.target.regexText.length){
8375                     tip = this.target.regexText;
8376                 }
8377             }
8378
8379             this.toolTip.show(tip);
8380
8381             this.intervalID = window.setInterval(function() {
8382                 Roo.bootstrap.Form.popover.unmask();
8383             }, 10000);
8384
8385             window.onwheel = function(){ return false;};
8386             
8387             (function(){ this.isMasked = true; }).defer(500, this);
8388             
8389         },
8390         
8391         unmask : function()
8392         {
8393             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8394                 return;
8395             }
8396             
8397             this.maskEl.top.setStyle('position', 'absolute');
8398             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8399             this.maskEl.top.hide();
8400
8401             this.maskEl.left.setStyle('position', 'absolute');
8402             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8403             this.maskEl.left.hide();
8404
8405             this.maskEl.bottom.setStyle('position', 'absolute');
8406             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8407             this.maskEl.bottom.hide();
8408
8409             this.maskEl.right.setStyle('position', 'absolute');
8410             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8411             this.maskEl.right.hide();
8412             
8413             this.toolTip.hide();
8414             
8415             this.toolTip.el.hide();
8416             
8417             window.onwheel = function(){ return true;};
8418             
8419             if(this.intervalID){
8420                 window.clearInterval(this.intervalID);
8421                 this.intervalID = false;
8422             }
8423             
8424             this.isMasked = false;
8425             
8426         }
8427         
8428     }
8429     
8430 });
8431
8432 /*
8433  * Based on:
8434  * Ext JS Library 1.1.1
8435  * Copyright(c) 2006-2007, Ext JS, LLC.
8436  *
8437  * Originally Released Under LGPL - original licence link has changed is not relivant.
8438  *
8439  * Fork - LGPL
8440  * <script type="text/javascript">
8441  */
8442 /**
8443  * @class Roo.form.VTypes
8444  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8445  * @singleton
8446  */
8447 Roo.form.VTypes = function(){
8448     // closure these in so they are only created once.
8449     var alpha = /^[a-zA-Z_]+$/;
8450     var alphanum = /^[a-zA-Z0-9_]+$/;
8451     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8452     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8453
8454     // All these messages and functions are configurable
8455     return {
8456         /**
8457          * The function used to validate email addresses
8458          * @param {String} value The email address
8459          */
8460         'email' : function(v){
8461             return email.test(v);
8462         },
8463         /**
8464          * The error text to display when the email validation function returns false
8465          * @type String
8466          */
8467         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8468         /**
8469          * The keystroke filter mask to be applied on email input
8470          * @type RegExp
8471          */
8472         'emailMask' : /[a-z0-9_\.\-@]/i,
8473
8474         /**
8475          * The function used to validate URLs
8476          * @param {String} value The URL
8477          */
8478         'url' : function(v){
8479             return url.test(v);
8480         },
8481         /**
8482          * The error text to display when the url validation function returns false
8483          * @type String
8484          */
8485         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8486         
8487         /**
8488          * The function used to validate alpha values
8489          * @param {String} value The value
8490          */
8491         'alpha' : function(v){
8492             return alpha.test(v);
8493         },
8494         /**
8495          * The error text to display when the alpha validation function returns false
8496          * @type String
8497          */
8498         'alphaText' : 'This field should only contain letters and _',
8499         /**
8500          * The keystroke filter mask to be applied on alpha input
8501          * @type RegExp
8502          */
8503         'alphaMask' : /[a-z_]/i,
8504
8505         /**
8506          * The function used to validate alphanumeric values
8507          * @param {String} value The value
8508          */
8509         'alphanum' : function(v){
8510             return alphanum.test(v);
8511         },
8512         /**
8513          * The error text to display when the alphanumeric validation function returns false
8514          * @type String
8515          */
8516         'alphanumText' : 'This field should only contain letters, numbers and _',
8517         /**
8518          * The keystroke filter mask to be applied on alphanumeric input
8519          * @type RegExp
8520          */
8521         'alphanumMask' : /[a-z0-9_]/i
8522     };
8523 }();/*
8524  * - LGPL
8525  *
8526  * Input
8527  * 
8528  */
8529
8530 /**
8531  * @class Roo.bootstrap.Input
8532  * @extends Roo.bootstrap.Component
8533  * Bootstrap Input class
8534  * @cfg {Boolean} disabled is it disabled
8535  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8536  * @cfg {String} name name of the input
8537  * @cfg {string} fieldLabel - the label associated
8538  * @cfg {string} placeholder - placeholder to put in text.
8539  * @cfg {string}  before - input group add on before
8540  * @cfg {string} after - input group add on after
8541  * @cfg {string} size - (lg|sm) or leave empty..
8542  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8543  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8544  * @cfg {Number} md colspan out of 12 for computer-sized screens
8545  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8546  * @cfg {string} value default value of the input
8547  * @cfg {Number} labelWidth set the width of label 
8548  * @cfg {Number} labellg set the width of label (1-12)
8549  * @cfg {Number} labelmd set the width of label (1-12)
8550  * @cfg {Number} labelsm set the width of label (1-12)
8551  * @cfg {Number} labelxs set the width of label (1-12)
8552  * @cfg {String} labelAlign (top|left)
8553  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8554  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8555  * @cfg {String} indicatorpos (left|right) default left
8556  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8557  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8558
8559  * @cfg {String} align (left|center|right) Default left
8560  * @cfg {Boolean} forceFeedback (true|false) Default false
8561  * 
8562  * @constructor
8563  * Create a new Input
8564  * @param {Object} config The config object
8565  */
8566
8567 Roo.bootstrap.Input = function(config){
8568     
8569     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8570     
8571     this.addEvents({
8572         /**
8573          * @event focus
8574          * Fires when this field receives input focus.
8575          * @param {Roo.form.Field} this
8576          */
8577         focus : true,
8578         /**
8579          * @event blur
8580          * Fires when this field loses input focus.
8581          * @param {Roo.form.Field} this
8582          */
8583         blur : true,
8584         /**
8585          * @event specialkey
8586          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8587          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8588          * @param {Roo.form.Field} this
8589          * @param {Roo.EventObject} e The event object
8590          */
8591         specialkey : true,
8592         /**
8593          * @event change
8594          * Fires just before the field blurs if the field value has changed.
8595          * @param {Roo.form.Field} this
8596          * @param {Mixed} newValue The new value
8597          * @param {Mixed} oldValue The original value
8598          */
8599         change : true,
8600         /**
8601          * @event invalid
8602          * Fires after the field has been marked as invalid.
8603          * @param {Roo.form.Field} this
8604          * @param {String} msg The validation message
8605          */
8606         invalid : true,
8607         /**
8608          * @event valid
8609          * Fires after the field has been validated with no errors.
8610          * @param {Roo.form.Field} this
8611          */
8612         valid : true,
8613          /**
8614          * @event keyup
8615          * Fires after the key up
8616          * @param {Roo.form.Field} this
8617          * @param {Roo.EventObject}  e The event Object
8618          */
8619         keyup : true
8620     });
8621 };
8622
8623 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8624      /**
8625      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8626       automatic validation (defaults to "keyup").
8627      */
8628     validationEvent : "keyup",
8629      /**
8630      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8631      */
8632     validateOnBlur : true,
8633     /**
8634      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8635      */
8636     validationDelay : 250,
8637      /**
8638      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8639      */
8640     focusClass : "x-form-focus",  // not needed???
8641     
8642        
8643     /**
8644      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8645      */
8646     invalidClass : "has-warning",
8647     
8648     /**
8649      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8650      */
8651     validClass : "has-success",
8652     
8653     /**
8654      * @cfg {Boolean} hasFeedback (true|false) default true
8655      */
8656     hasFeedback : true,
8657     
8658     /**
8659      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8660      */
8661     invalidFeedbackClass : "glyphicon-warning-sign",
8662     
8663     /**
8664      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8665      */
8666     validFeedbackClass : "glyphicon-ok",
8667     
8668     /**
8669      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8670      */
8671     selectOnFocus : false,
8672     
8673      /**
8674      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8675      */
8676     maskRe : null,
8677        /**
8678      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8679      */
8680     vtype : null,
8681     
8682       /**
8683      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8684      */
8685     disableKeyFilter : false,
8686     
8687        /**
8688      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8689      */
8690     disabled : false,
8691      /**
8692      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8693      */
8694     allowBlank : true,
8695     /**
8696      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8697      */
8698     blankText : "Please complete this mandatory field",
8699     
8700      /**
8701      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8702      */
8703     minLength : 0,
8704     /**
8705      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8706      */
8707     maxLength : Number.MAX_VALUE,
8708     /**
8709      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8710      */
8711     minLengthText : "The minimum length for this field is {0}",
8712     /**
8713      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8714      */
8715     maxLengthText : "The maximum length for this field is {0}",
8716   
8717     
8718     /**
8719      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8720      * If available, this function will be called only after the basic validators all return true, and will be passed the
8721      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8722      */
8723     validator : null,
8724     /**
8725      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8726      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8727      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8728      */
8729     regex : null,
8730     /**
8731      * @cfg {String} regexText -- Depricated - use Invalid Text
8732      */
8733     regexText : "",
8734     
8735     /**
8736      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8737      */
8738     invalidText : "",
8739     
8740     
8741     
8742     autocomplete: false,
8743     
8744     
8745     fieldLabel : '',
8746     inputType : 'text',
8747     
8748     name : false,
8749     placeholder: false,
8750     before : false,
8751     after : false,
8752     size : false,
8753     hasFocus : false,
8754     preventMark: false,
8755     isFormField : true,
8756     value : '',
8757     labelWidth : 2,
8758     labelAlign : false,
8759     readOnly : false,
8760     align : false,
8761     formatedValue : false,
8762     forceFeedback : false,
8763     
8764     indicatorpos : 'left',
8765     
8766     labellg : 0,
8767     labelmd : 0,
8768     labelsm : 0,
8769     labelxs : 0,
8770     
8771     capture : '',
8772     accept : '',
8773     
8774     parentLabelAlign : function()
8775     {
8776         var parent = this;
8777         while (parent.parent()) {
8778             parent = parent.parent();
8779             if (typeof(parent.labelAlign) !='undefined') {
8780                 return parent.labelAlign;
8781             }
8782         }
8783         return 'left';
8784         
8785     },
8786     
8787     getAutoCreate : function()
8788     {
8789         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8790         
8791         var id = Roo.id();
8792         
8793         var cfg = {};
8794         
8795         if(this.inputType != 'hidden'){
8796             cfg.cls = 'form-group' //input-group
8797         }
8798         
8799         var input =  {
8800             tag: 'input',
8801             id : id,
8802             type : this.inputType,
8803             value : this.value,
8804             cls : 'form-control',
8805             placeholder : this.placeholder || '',
8806             autocomplete : this.autocomplete || 'new-password'
8807         };
8808         
8809         if(this.capture.length){
8810             input.capture = this.capture;
8811         }
8812         
8813         if(this.accept.length){
8814             input.accept = this.accept + "/*";
8815         }
8816         
8817         if(this.align){
8818             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8819         }
8820         
8821         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8822             input.maxLength = this.maxLength;
8823         }
8824         
8825         if (this.disabled) {
8826             input.disabled=true;
8827         }
8828         
8829         if (this.readOnly) {
8830             input.readonly=true;
8831         }
8832         
8833         if (this.name) {
8834             input.name = this.name;
8835         }
8836         
8837         if (this.size) {
8838             input.cls += ' input-' + this.size;
8839         }
8840         
8841         var settings=this;
8842         ['xs','sm','md','lg'].map(function(size){
8843             if (settings[size]) {
8844                 cfg.cls += ' col-' + size + '-' + settings[size];
8845             }
8846         });
8847         
8848         var inputblock = input;
8849         
8850         var feedback = {
8851             tag: 'span',
8852             cls: 'glyphicon form-control-feedback'
8853         };
8854             
8855         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8856             
8857             inputblock = {
8858                 cls : 'has-feedback',
8859                 cn :  [
8860                     input,
8861                     feedback
8862                 ] 
8863             };  
8864         }
8865         
8866         if (this.before || this.after) {
8867             
8868             inputblock = {
8869                 cls : 'input-group',
8870                 cn :  [] 
8871             };
8872             
8873             if (this.before && typeof(this.before) == 'string') {
8874                 
8875                 inputblock.cn.push({
8876                     tag :'span',
8877                     cls : 'roo-input-before input-group-addon',
8878                     html : this.before
8879                 });
8880             }
8881             if (this.before && typeof(this.before) == 'object') {
8882                 this.before = Roo.factory(this.before);
8883                 
8884                 inputblock.cn.push({
8885                     tag :'span',
8886                     cls : 'roo-input-before input-group-' +
8887                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8888                 });
8889             }
8890             
8891             inputblock.cn.push(input);
8892             
8893             if (this.after && typeof(this.after) == 'string') {
8894                 inputblock.cn.push({
8895                     tag :'span',
8896                     cls : 'roo-input-after input-group-addon',
8897                     html : this.after
8898                 });
8899             }
8900             if (this.after && typeof(this.after) == 'object') {
8901                 this.after = Roo.factory(this.after);
8902                 
8903                 inputblock.cn.push({
8904                     tag :'span',
8905                     cls : 'roo-input-after input-group-' +
8906                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8907                 });
8908             }
8909             
8910             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8911                 inputblock.cls += ' has-feedback';
8912                 inputblock.cn.push(feedback);
8913             }
8914         };
8915         
8916         if (align ==='left' && this.fieldLabel.length) {
8917             
8918             cfg.cls += ' roo-form-group-label-left';
8919             
8920             cfg.cn = [
8921                 {
8922                     tag : 'i',
8923                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8924                     tooltip : 'This field is required'
8925                 },
8926                 {
8927                     tag: 'label',
8928                     'for' :  id,
8929                     cls : 'control-label',
8930                     html : this.fieldLabel
8931
8932                 },
8933                 {
8934                     cls : "", 
8935                     cn: [
8936                         inputblock
8937                     ]
8938                 }
8939             ];
8940             
8941             var labelCfg = cfg.cn[1];
8942             var contentCfg = cfg.cn[2];
8943             
8944             if(this.indicatorpos == 'right'){
8945                 cfg.cn = [
8946                     {
8947                         tag: 'label',
8948                         'for' :  id,
8949                         cls : 'control-label',
8950                         cn : [
8951                             {
8952                                 tag : 'span',
8953                                 html : this.fieldLabel
8954                             },
8955                             {
8956                                 tag : 'i',
8957                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8958                                 tooltip : 'This field is required'
8959                             }
8960                         ]
8961                     },
8962                     {
8963                         cls : "",
8964                         cn: [
8965                             inputblock
8966                         ]
8967                     }
8968
8969                 ];
8970                 
8971                 labelCfg = cfg.cn[0];
8972                 contentCfg = cfg.cn[1];
8973             
8974             }
8975             
8976             if(this.labelWidth > 12){
8977                 labelCfg.style = "width: " + this.labelWidth + 'px';
8978             }
8979             
8980             if(this.labelWidth < 13 && this.labelmd == 0){
8981                 this.labelmd = this.labelWidth;
8982             }
8983             
8984             if(this.labellg > 0){
8985                 labelCfg.cls += ' col-lg-' + this.labellg;
8986                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8987             }
8988             
8989             if(this.labelmd > 0){
8990                 labelCfg.cls += ' col-md-' + this.labelmd;
8991                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8992             }
8993             
8994             if(this.labelsm > 0){
8995                 labelCfg.cls += ' col-sm-' + this.labelsm;
8996                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8997             }
8998             
8999             if(this.labelxs > 0){
9000                 labelCfg.cls += ' col-xs-' + this.labelxs;
9001                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9002             }
9003             
9004             
9005         } else if ( this.fieldLabel.length) {
9006                 
9007             cfg.cn = [
9008                 {
9009                     tag : 'i',
9010                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9011                     tooltip : 'This field is required'
9012                 },
9013                 {
9014                     tag: 'label',
9015                    //cls : 'input-group-addon',
9016                     html : this.fieldLabel
9017
9018                 },
9019
9020                inputblock
9021
9022            ];
9023            
9024            if(this.indicatorpos == 'right'){
9025                 
9026                 cfg.cn = [
9027                     {
9028                         tag: 'label',
9029                        //cls : 'input-group-addon',
9030                         html : this.fieldLabel
9031
9032                     },
9033                     {
9034                         tag : 'i',
9035                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9036                         tooltip : 'This field is required'
9037                     },
9038
9039                    inputblock
9040
9041                ];
9042
9043             }
9044
9045         } else {
9046             
9047             cfg.cn = [
9048
9049                     inputblock
9050
9051             ];
9052                 
9053                 
9054         };
9055         
9056         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9057            cfg.cls += ' navbar-form';
9058         }
9059         
9060         if (this.parentType === 'NavGroup') {
9061            cfg.cls += ' navbar-form';
9062            cfg.tag = 'li';
9063         }
9064         
9065         return cfg;
9066         
9067     },
9068     /**
9069      * return the real input element.
9070      */
9071     inputEl: function ()
9072     {
9073         return this.el.select('input.form-control',true).first();
9074     },
9075     
9076     tooltipEl : function()
9077     {
9078         return this.inputEl();
9079     },
9080     
9081     indicatorEl : function()
9082     {
9083         var indicator = this.el.select('i.roo-required-indicator',true).first();
9084         
9085         if(!indicator){
9086             return false;
9087         }
9088         
9089         return indicator;
9090         
9091     },
9092     
9093     setDisabled : function(v)
9094     {
9095         var i  = this.inputEl().dom;
9096         if (!v) {
9097             i.removeAttribute('disabled');
9098             return;
9099             
9100         }
9101         i.setAttribute('disabled','true');
9102     },
9103     initEvents : function()
9104     {
9105           
9106         this.inputEl().on("keydown" , this.fireKey,  this);
9107         this.inputEl().on("focus", this.onFocus,  this);
9108         this.inputEl().on("blur", this.onBlur,  this);
9109         
9110         this.inputEl().relayEvent('keyup', this);
9111         
9112         this.indicator = this.indicatorEl();
9113         
9114         if(this.indicator){
9115             this.indicator.addClass('invisible');
9116         }
9117  
9118         // reference to original value for reset
9119         this.originalValue = this.getValue();
9120         //Roo.form.TextField.superclass.initEvents.call(this);
9121         if(this.validationEvent == 'keyup'){
9122             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9123             this.inputEl().on('keyup', this.filterValidation, this);
9124         }
9125         else if(this.validationEvent !== false){
9126             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9127         }
9128         
9129         if(this.selectOnFocus){
9130             this.on("focus", this.preFocus, this);
9131             
9132         }
9133         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9134             this.inputEl().on("keypress", this.filterKeys, this);
9135         } else {
9136             this.inputEl().relayEvent('keypress', this);
9137         }
9138        /* if(this.grow){
9139             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9140             this.el.on("click", this.autoSize,  this);
9141         }
9142         */
9143         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9144             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9145         }
9146         
9147         if (typeof(this.before) == 'object') {
9148             this.before.render(this.el.select('.roo-input-before',true).first());
9149         }
9150         if (typeof(this.after) == 'object') {
9151             this.after.render(this.el.select('.roo-input-after',true).first());
9152         }
9153         
9154         this.inputEl().on('change', this.onChange, this);
9155         
9156     },
9157     filterValidation : function(e){
9158         if(!e.isNavKeyPress()){
9159             this.validationTask.delay(this.validationDelay);
9160         }
9161     },
9162      /**
9163      * Validates the field value
9164      * @return {Boolean} True if the value is valid, else false
9165      */
9166     validate : function(){
9167         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9168         if(this.disabled || this.validateValue(this.getRawValue())){
9169             this.markValid();
9170             return true;
9171         }
9172         
9173         this.markInvalid();
9174         return false;
9175     },
9176     
9177     
9178     /**
9179      * Validates a value according to the field's validation rules and marks the field as invalid
9180      * if the validation fails
9181      * @param {Mixed} value The value to validate
9182      * @return {Boolean} True if the value is valid, else false
9183      */
9184     validateValue : function(value)
9185     {
9186         if(this.getVisibilityEl().hasClass('hidden')){
9187             return true;
9188         }
9189         
9190         if(value.length < 1)  { // if it's blank
9191             if(this.allowBlank){
9192                 return true;
9193             }
9194             return false;
9195         }
9196         
9197         if(value.length < this.minLength){
9198             return false;
9199         }
9200         if(value.length > this.maxLength){
9201             return false;
9202         }
9203         if(this.vtype){
9204             var vt = Roo.form.VTypes;
9205             if(!vt[this.vtype](value, this)){
9206                 return false;
9207             }
9208         }
9209         if(typeof this.validator == "function"){
9210             var msg = this.validator(value);
9211             if(msg !== true){
9212                 return false;
9213             }
9214             if (typeof(msg) == 'string') {
9215                 this.invalidText = msg;
9216             }
9217         }
9218         
9219         if(this.regex && !this.regex.test(value)){
9220             return false;
9221         }
9222         
9223         return true;
9224     },
9225     
9226      // private
9227     fireKey : function(e){
9228         //Roo.log('field ' + e.getKey());
9229         if(e.isNavKeyPress()){
9230             this.fireEvent("specialkey", this, e);
9231         }
9232     },
9233     focus : function (selectText){
9234         if(this.rendered){
9235             this.inputEl().focus();
9236             if(selectText === true){
9237                 this.inputEl().dom.select();
9238             }
9239         }
9240         return this;
9241     } ,
9242     
9243     onFocus : function(){
9244         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9245            // this.el.addClass(this.focusClass);
9246         }
9247         if(!this.hasFocus){
9248             this.hasFocus = true;
9249             this.startValue = this.getValue();
9250             this.fireEvent("focus", this);
9251         }
9252     },
9253     
9254     beforeBlur : Roo.emptyFn,
9255
9256     
9257     // private
9258     onBlur : function(){
9259         this.beforeBlur();
9260         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9261             //this.el.removeClass(this.focusClass);
9262         }
9263         this.hasFocus = false;
9264         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9265             this.validate();
9266         }
9267         var v = this.getValue();
9268         if(String(v) !== String(this.startValue)){
9269             this.fireEvent('change', this, v, this.startValue);
9270         }
9271         this.fireEvent("blur", this);
9272     },
9273     
9274     onChange : function(e)
9275     {
9276         var v = this.getValue();
9277         if(String(v) !== String(this.startValue)){
9278             this.fireEvent('change', this, v, this.startValue);
9279         }
9280         
9281     },
9282     
9283     /**
9284      * Resets the current field value to the originally loaded value and clears any validation messages
9285      */
9286     reset : function(){
9287         this.setValue(this.originalValue);
9288         this.validate();
9289     },
9290      /**
9291      * Returns the name of the field
9292      * @return {Mixed} name The name field
9293      */
9294     getName: function(){
9295         return this.name;
9296     },
9297      /**
9298      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9299      * @return {Mixed} value The field value
9300      */
9301     getValue : function(){
9302         
9303         var v = this.inputEl().getValue();
9304         
9305         return v;
9306     },
9307     /**
9308      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9309      * @return {Mixed} value The field value
9310      */
9311     getRawValue : function(){
9312         var v = this.inputEl().getValue();
9313         
9314         return v;
9315     },
9316     
9317     /**
9318      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9319      * @param {Mixed} value The value to set
9320      */
9321     setRawValue : function(v){
9322         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9323     },
9324     
9325     selectText : function(start, end){
9326         var v = this.getRawValue();
9327         if(v.length > 0){
9328             start = start === undefined ? 0 : start;
9329             end = end === undefined ? v.length : end;
9330             var d = this.inputEl().dom;
9331             if(d.setSelectionRange){
9332                 d.setSelectionRange(start, end);
9333             }else if(d.createTextRange){
9334                 var range = d.createTextRange();
9335                 range.moveStart("character", start);
9336                 range.moveEnd("character", v.length-end);
9337                 range.select();
9338             }
9339         }
9340     },
9341     
9342     /**
9343      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9344      * @param {Mixed} value The value to set
9345      */
9346     setValue : function(v){
9347         this.value = v;
9348         if(this.rendered){
9349             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9350             this.validate();
9351         }
9352     },
9353     
9354     /*
9355     processValue : function(value){
9356         if(this.stripCharsRe){
9357             var newValue = value.replace(this.stripCharsRe, '');
9358             if(newValue !== value){
9359                 this.setRawValue(newValue);
9360                 return newValue;
9361             }
9362         }
9363         return value;
9364     },
9365   */
9366     preFocus : function(){
9367         
9368         if(this.selectOnFocus){
9369             this.inputEl().dom.select();
9370         }
9371     },
9372     filterKeys : function(e){
9373         var k = e.getKey();
9374         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9375             return;
9376         }
9377         var c = e.getCharCode(), cc = String.fromCharCode(c);
9378         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9379             return;
9380         }
9381         if(!this.maskRe.test(cc)){
9382             e.stopEvent();
9383         }
9384     },
9385      /**
9386      * Clear any invalid styles/messages for this field
9387      */
9388     clearInvalid : function(){
9389         
9390         if(!this.el || this.preventMark){ // not rendered
9391             return;
9392         }
9393         
9394      
9395         this.el.removeClass(this.invalidClass);
9396         
9397         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9398             
9399             var feedback = this.el.select('.form-control-feedback', true).first();
9400             
9401             if(feedback){
9402                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9403             }
9404             
9405         }
9406         
9407         this.fireEvent('valid', this);
9408     },
9409     
9410      /**
9411      * Mark this field as valid
9412      */
9413     markValid : function()
9414     {
9415         if(!this.el  || this.preventMark){ // not rendered...
9416             return;
9417         }
9418         
9419         this.el.removeClass([this.invalidClass, this.validClass]);
9420         
9421         var feedback = this.el.select('.form-control-feedback', true).first();
9422             
9423         if(feedback){
9424             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9425         }
9426         
9427         if(this.indicator){
9428             this.indicator.removeClass('visible');
9429             this.indicator.addClass('invisible');
9430         }
9431         
9432         if(this.disabled){
9433             return;
9434         }
9435         
9436         if(this.allowBlank && !this.getRawValue().length){
9437             return;
9438         }
9439         
9440         this.el.addClass(this.validClass);
9441         
9442         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9443             
9444             var feedback = this.el.select('.form-control-feedback', true).first();
9445             
9446             if(feedback){
9447                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9448                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9449             }
9450             
9451         }
9452         
9453         this.fireEvent('valid', this);
9454     },
9455     
9456      /**
9457      * Mark this field as invalid
9458      * @param {String} msg The validation message
9459      */
9460     markInvalid : function(msg)
9461     {
9462         if(!this.el  || this.preventMark){ // not rendered
9463             return;
9464         }
9465         
9466         this.el.removeClass([this.invalidClass, this.validClass]);
9467         
9468         var feedback = this.el.select('.form-control-feedback', true).first();
9469             
9470         if(feedback){
9471             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9472         }
9473
9474         if(this.disabled){
9475             return;
9476         }
9477         
9478         if(this.allowBlank && !this.getRawValue().length){
9479             return;
9480         }
9481         
9482         if(this.indicator){
9483             this.indicator.removeClass('invisible');
9484             this.indicator.addClass('visible');
9485         }
9486         
9487         this.el.addClass(this.invalidClass);
9488         
9489         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9490             
9491             var feedback = this.el.select('.form-control-feedback', true).first();
9492             
9493             if(feedback){
9494                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9495                 
9496                 if(this.getValue().length || this.forceFeedback){
9497                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9498                 }
9499                 
9500             }
9501             
9502         }
9503         
9504         this.fireEvent('invalid', this, msg);
9505     },
9506     // private
9507     SafariOnKeyDown : function(event)
9508     {
9509         // this is a workaround for a password hang bug on chrome/ webkit.
9510         if (this.inputEl().dom.type != 'password') {
9511             return;
9512         }
9513         
9514         var isSelectAll = false;
9515         
9516         if(this.inputEl().dom.selectionEnd > 0){
9517             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9518         }
9519         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9520             event.preventDefault();
9521             this.setValue('');
9522             return;
9523         }
9524         
9525         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9526             
9527             event.preventDefault();
9528             // this is very hacky as keydown always get's upper case.
9529             //
9530             var cc = String.fromCharCode(event.getCharCode());
9531             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9532             
9533         }
9534     },
9535     adjustWidth : function(tag, w){
9536         tag = tag.toLowerCase();
9537         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9538             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9539                 if(tag == 'input'){
9540                     return w + 2;
9541                 }
9542                 if(tag == 'textarea'){
9543                     return w-2;
9544                 }
9545             }else if(Roo.isOpera){
9546                 if(tag == 'input'){
9547                     return w + 2;
9548                 }
9549                 if(tag == 'textarea'){
9550                     return w-2;
9551                 }
9552             }
9553         }
9554         return w;
9555     },
9556     
9557     setFieldLabel : function(v)
9558     {
9559         if(!this.rendered){
9560             return;
9561         }
9562         
9563         if(this.indicator){
9564             var ar = this.el.select('label > span',true);
9565             
9566             if (ar.elements.length) {
9567                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9568                 this.fieldLabel = v;
9569                 return;
9570             }
9571             
9572             var br = this.el.select('label',true);
9573             
9574             if(br.elements.length) {
9575                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9576                 this.fieldLabel = v;
9577                 return;
9578             }
9579             
9580             Roo.log('Cannot Found any of label > span || label in input');
9581             return;
9582         }
9583         
9584         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9585         this.fieldLabel = v;
9586         
9587         
9588     }
9589 });
9590
9591  
9592 /*
9593  * - LGPL
9594  *
9595  * Input
9596  * 
9597  */
9598
9599 /**
9600  * @class Roo.bootstrap.TextArea
9601  * @extends Roo.bootstrap.Input
9602  * Bootstrap TextArea class
9603  * @cfg {Number} cols Specifies the visible width of a text area
9604  * @cfg {Number} rows Specifies the visible number of lines in a text area
9605  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9606  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9607  * @cfg {string} html text
9608  * 
9609  * @constructor
9610  * Create a new TextArea
9611  * @param {Object} config The config object
9612  */
9613
9614 Roo.bootstrap.TextArea = function(config){
9615     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9616    
9617 };
9618
9619 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9620      
9621     cols : false,
9622     rows : 5,
9623     readOnly : false,
9624     warp : 'soft',
9625     resize : false,
9626     value: false,
9627     html: false,
9628     
9629     getAutoCreate : function(){
9630         
9631         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9632         
9633         var id = Roo.id();
9634         
9635         var cfg = {};
9636         
9637         if(this.inputType != 'hidden'){
9638             cfg.cls = 'form-group' //input-group
9639         }
9640         
9641         var input =  {
9642             tag: 'textarea',
9643             id : id,
9644             warp : this.warp,
9645             rows : this.rows,
9646             value : this.value || '',
9647             html: this.html || '',
9648             cls : 'form-control',
9649             placeholder : this.placeholder || '' 
9650             
9651         };
9652         
9653         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9654             input.maxLength = this.maxLength;
9655         }
9656         
9657         if(this.resize){
9658             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9659         }
9660         
9661         if(this.cols){
9662             input.cols = this.cols;
9663         }
9664         
9665         if (this.readOnly) {
9666             input.readonly = true;
9667         }
9668         
9669         if (this.name) {
9670             input.name = this.name;
9671         }
9672         
9673         if (this.size) {
9674             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9675         }
9676         
9677         var settings=this;
9678         ['xs','sm','md','lg'].map(function(size){
9679             if (settings[size]) {
9680                 cfg.cls += ' col-' + size + '-' + settings[size];
9681             }
9682         });
9683         
9684         var inputblock = input;
9685         
9686         if(this.hasFeedback && !this.allowBlank){
9687             
9688             var feedback = {
9689                 tag: 'span',
9690                 cls: 'glyphicon form-control-feedback'
9691             };
9692
9693             inputblock = {
9694                 cls : 'has-feedback',
9695                 cn :  [
9696                     input,
9697                     feedback
9698                 ] 
9699             };  
9700         }
9701         
9702         
9703         if (this.before || this.after) {
9704             
9705             inputblock = {
9706                 cls : 'input-group',
9707                 cn :  [] 
9708             };
9709             if (this.before) {
9710                 inputblock.cn.push({
9711                     tag :'span',
9712                     cls : 'input-group-addon',
9713                     html : this.before
9714                 });
9715             }
9716             
9717             inputblock.cn.push(input);
9718             
9719             if(this.hasFeedback && !this.allowBlank){
9720                 inputblock.cls += ' has-feedback';
9721                 inputblock.cn.push(feedback);
9722             }
9723             
9724             if (this.after) {
9725                 inputblock.cn.push({
9726                     tag :'span',
9727                     cls : 'input-group-addon',
9728                     html : this.after
9729                 });
9730             }
9731             
9732         }
9733         
9734         if (align ==='left' && this.fieldLabel.length) {
9735             cfg.cn = [
9736                 {
9737                     tag: 'label',
9738                     'for' :  id,
9739                     cls : 'control-label',
9740                     html : this.fieldLabel
9741                 },
9742                 {
9743                     cls : "",
9744                     cn: [
9745                         inputblock
9746                     ]
9747                 }
9748
9749             ];
9750             
9751             if(this.labelWidth > 12){
9752                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9753             }
9754
9755             if(this.labelWidth < 13 && this.labelmd == 0){
9756                 this.labelmd = this.labelWidth;
9757             }
9758
9759             if(this.labellg > 0){
9760                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9761                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9762             }
9763
9764             if(this.labelmd > 0){
9765                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9766                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9767             }
9768
9769             if(this.labelsm > 0){
9770                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9771                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9772             }
9773
9774             if(this.labelxs > 0){
9775                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9776                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9777             }
9778             
9779         } else if ( this.fieldLabel.length) {
9780             cfg.cn = [
9781
9782                {
9783                    tag: 'label',
9784                    //cls : 'input-group-addon',
9785                    html : this.fieldLabel
9786
9787                },
9788
9789                inputblock
9790
9791            ];
9792
9793         } else {
9794
9795             cfg.cn = [
9796
9797                 inputblock
9798
9799             ];
9800                 
9801         }
9802         
9803         if (this.disabled) {
9804             input.disabled=true;
9805         }
9806         
9807         return cfg;
9808         
9809     },
9810     /**
9811      * return the real textarea element.
9812      */
9813     inputEl: function ()
9814     {
9815         return this.el.select('textarea.form-control',true).first();
9816     },
9817     
9818     /**
9819      * Clear any invalid styles/messages for this field
9820      */
9821     clearInvalid : function()
9822     {
9823         
9824         if(!this.el || this.preventMark){ // not rendered
9825             return;
9826         }
9827         
9828         var label = this.el.select('label', true).first();
9829         var icon = this.el.select('i.fa-star', true).first();
9830         
9831         if(label && icon){
9832             icon.remove();
9833         }
9834         
9835         this.el.removeClass(this.invalidClass);
9836         
9837         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9838             
9839             var feedback = this.el.select('.form-control-feedback', true).first();
9840             
9841             if(feedback){
9842                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9843             }
9844             
9845         }
9846         
9847         this.fireEvent('valid', this);
9848     },
9849     
9850      /**
9851      * Mark this field as valid
9852      */
9853     markValid : function()
9854     {
9855         if(!this.el  || this.preventMark){ // not rendered
9856             return;
9857         }
9858         
9859         this.el.removeClass([this.invalidClass, this.validClass]);
9860         
9861         var feedback = this.el.select('.form-control-feedback', true).first();
9862             
9863         if(feedback){
9864             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9865         }
9866
9867         if(this.disabled || this.allowBlank){
9868             return;
9869         }
9870         
9871         var label = this.el.select('label', true).first();
9872         var icon = this.el.select('i.fa-star', true).first();
9873         
9874         if(label && icon){
9875             icon.remove();
9876         }
9877         
9878         this.el.addClass(this.validClass);
9879         
9880         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9881             
9882             var feedback = this.el.select('.form-control-feedback', true).first();
9883             
9884             if(feedback){
9885                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9886                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9887             }
9888             
9889         }
9890         
9891         this.fireEvent('valid', this);
9892     },
9893     
9894      /**
9895      * Mark this field as invalid
9896      * @param {String} msg The validation message
9897      */
9898     markInvalid : function(msg)
9899     {
9900         if(!this.el  || this.preventMark){ // not rendered
9901             return;
9902         }
9903         
9904         this.el.removeClass([this.invalidClass, this.validClass]);
9905         
9906         var feedback = this.el.select('.form-control-feedback', true).first();
9907             
9908         if(feedback){
9909             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9910         }
9911
9912         if(this.disabled || this.allowBlank){
9913             return;
9914         }
9915         
9916         var label = this.el.select('label', true).first();
9917         var icon = this.el.select('i.fa-star', true).first();
9918         
9919         if(!this.getValue().length && label && !icon){
9920             this.el.createChild({
9921                 tag : 'i',
9922                 cls : 'text-danger fa fa-lg fa-star',
9923                 tooltip : 'This field is required',
9924                 style : 'margin-right:5px;'
9925             }, label, true);
9926         }
9927
9928         this.el.addClass(this.invalidClass);
9929         
9930         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9931             
9932             var feedback = this.el.select('.form-control-feedback', true).first();
9933             
9934             if(feedback){
9935                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9936                 
9937                 if(this.getValue().length || this.forceFeedback){
9938                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9939                 }
9940                 
9941             }
9942             
9943         }
9944         
9945         this.fireEvent('invalid', this, msg);
9946     }
9947 });
9948
9949  
9950 /*
9951  * - LGPL
9952  *
9953  * trigger field - base class for combo..
9954  * 
9955  */
9956  
9957 /**
9958  * @class Roo.bootstrap.TriggerField
9959  * @extends Roo.bootstrap.Input
9960  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9961  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9962  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9963  * for which you can provide a custom implementation.  For example:
9964  * <pre><code>
9965 var trigger = new Roo.bootstrap.TriggerField();
9966 trigger.onTriggerClick = myTriggerFn;
9967 trigger.applyTo('my-field');
9968 </code></pre>
9969  *
9970  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9971  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9972  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9973  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9974  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9975
9976  * @constructor
9977  * Create a new TriggerField.
9978  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9979  * to the base TextField)
9980  */
9981 Roo.bootstrap.TriggerField = function(config){
9982     this.mimicing = false;
9983     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9984 };
9985
9986 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9987     /**
9988      * @cfg {String} triggerClass A CSS class to apply to the trigger
9989      */
9990      /**
9991      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9992      */
9993     hideTrigger:false,
9994
9995     /**
9996      * @cfg {Boolean} removable (true|false) special filter default false
9997      */
9998     removable : false,
9999     
10000     /** @cfg {Boolean} grow @hide */
10001     /** @cfg {Number} growMin @hide */
10002     /** @cfg {Number} growMax @hide */
10003
10004     /**
10005      * @hide 
10006      * @method
10007      */
10008     autoSize: Roo.emptyFn,
10009     // private
10010     monitorTab : true,
10011     // private
10012     deferHeight : true,
10013
10014     
10015     actionMode : 'wrap',
10016     
10017     caret : false,
10018     
10019     
10020     getAutoCreate : function(){
10021        
10022         var align = this.labelAlign || this.parentLabelAlign();
10023         
10024         var id = Roo.id();
10025         
10026         var cfg = {
10027             cls: 'form-group' //input-group
10028         };
10029         
10030         
10031         var input =  {
10032             tag: 'input',
10033             id : id,
10034             type : this.inputType,
10035             cls : 'form-control',
10036             autocomplete: 'new-password',
10037             placeholder : this.placeholder || '' 
10038             
10039         };
10040         if (this.name) {
10041             input.name = this.name;
10042         }
10043         if (this.size) {
10044             input.cls += ' input-' + this.size;
10045         }
10046         
10047         if (this.disabled) {
10048             input.disabled=true;
10049         }
10050         
10051         var inputblock = input;
10052         
10053         if(this.hasFeedback && !this.allowBlank){
10054             
10055             var feedback = {
10056                 tag: 'span',
10057                 cls: 'glyphicon form-control-feedback'
10058             };
10059             
10060             if(this.removable && !this.editable && !this.tickable){
10061                 inputblock = {
10062                     cls : 'has-feedback',
10063                     cn :  [
10064                         inputblock,
10065                         {
10066                             tag: 'button',
10067                             html : 'x',
10068                             cls : 'roo-combo-removable-btn close'
10069                         },
10070                         feedback
10071                     ] 
10072                 };
10073             } else {
10074                 inputblock = {
10075                     cls : 'has-feedback',
10076                     cn :  [
10077                         inputblock,
10078                         feedback
10079                     ] 
10080                 };
10081             }
10082
10083         } else {
10084             if(this.removable && !this.editable && !this.tickable){
10085                 inputblock = {
10086                     cls : 'roo-removable',
10087                     cn :  [
10088                         inputblock,
10089                         {
10090                             tag: 'button',
10091                             html : 'x',
10092                             cls : 'roo-combo-removable-btn close'
10093                         }
10094                     ] 
10095                 };
10096             }
10097         }
10098         
10099         if (this.before || this.after) {
10100             
10101             inputblock = {
10102                 cls : 'input-group',
10103                 cn :  [] 
10104             };
10105             if (this.before) {
10106                 inputblock.cn.push({
10107                     tag :'span',
10108                     cls : 'input-group-addon',
10109                     html : this.before
10110                 });
10111             }
10112             
10113             inputblock.cn.push(input);
10114             
10115             if(this.hasFeedback && !this.allowBlank){
10116                 inputblock.cls += ' has-feedback';
10117                 inputblock.cn.push(feedback);
10118             }
10119             
10120             if (this.after) {
10121                 inputblock.cn.push({
10122                     tag :'span',
10123                     cls : 'input-group-addon',
10124                     html : this.after
10125                 });
10126             }
10127             
10128         };
10129         
10130         var box = {
10131             tag: 'div',
10132             cn: [
10133                 {
10134                     tag: 'input',
10135                     type : 'hidden',
10136                     cls: 'form-hidden-field'
10137                 },
10138                 inputblock
10139             ]
10140             
10141         };
10142         
10143         if(this.multiple){
10144             box = {
10145                 tag: 'div',
10146                 cn: [
10147                     {
10148                         tag: 'input',
10149                         type : 'hidden',
10150                         cls: 'form-hidden-field'
10151                     },
10152                     {
10153                         tag: 'ul',
10154                         cls: 'roo-select2-choices',
10155                         cn:[
10156                             {
10157                                 tag: 'li',
10158                                 cls: 'roo-select2-search-field',
10159                                 cn: [
10160
10161                                     inputblock
10162                                 ]
10163                             }
10164                         ]
10165                     }
10166                 ]
10167             }
10168         };
10169         
10170         var combobox = {
10171             cls: 'roo-select2-container input-group',
10172             cn: [
10173                 box
10174 //                {
10175 //                    tag: 'ul',
10176 //                    cls: 'typeahead typeahead-long dropdown-menu',
10177 //                    style: 'display:none'
10178 //                }
10179             ]
10180         };
10181         
10182         if(!this.multiple && this.showToggleBtn){
10183             
10184             var caret = {
10185                         tag: 'span',
10186                         cls: 'caret'
10187              };
10188             if (this.caret != false) {
10189                 caret = {
10190                      tag: 'i',
10191                      cls: 'fa fa-' + this.caret
10192                 };
10193                 
10194             }
10195             
10196             combobox.cn.push({
10197                 tag :'span',
10198                 cls : 'input-group-addon btn dropdown-toggle',
10199                 cn : [
10200                     caret,
10201                     {
10202                         tag: 'span',
10203                         cls: 'combobox-clear',
10204                         cn  : [
10205                             {
10206                                 tag : 'i',
10207                                 cls: 'icon-remove'
10208                             }
10209                         ]
10210                     }
10211                 ]
10212
10213             })
10214         }
10215         
10216         if(this.multiple){
10217             combobox.cls += ' roo-select2-container-multi';
10218         }
10219         
10220         if (align ==='left' && this.fieldLabel.length) {
10221             
10222             cfg.cls += ' roo-form-group-label-left';
10223
10224             cfg.cn = [
10225                 {
10226                     tag : 'i',
10227                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10228                     tooltip : 'This field is required'
10229                 },
10230                 {
10231                     tag: 'label',
10232                     'for' :  id,
10233                     cls : 'control-label',
10234                     html : this.fieldLabel
10235
10236                 },
10237                 {
10238                     cls : "", 
10239                     cn: [
10240                         combobox
10241                     ]
10242                 }
10243
10244             ];
10245             
10246             var labelCfg = cfg.cn[1];
10247             var contentCfg = cfg.cn[2];
10248             
10249             if(this.indicatorpos == 'right'){
10250                 cfg.cn = [
10251                     {
10252                         tag: 'label',
10253                         'for' :  id,
10254                         cls : 'control-label',
10255                         cn : [
10256                             {
10257                                 tag : 'span',
10258                                 html : this.fieldLabel
10259                             },
10260                             {
10261                                 tag : 'i',
10262                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10263                                 tooltip : 'This field is required'
10264                             }
10265                         ]
10266                     },
10267                     {
10268                         cls : "", 
10269                         cn: [
10270                             combobox
10271                         ]
10272                     }
10273
10274                 ];
10275                 
10276                 labelCfg = cfg.cn[0];
10277                 contentCfg = cfg.cn[1];
10278             }
10279             
10280             if(this.labelWidth > 12){
10281                 labelCfg.style = "width: " + this.labelWidth + 'px';
10282             }
10283             
10284             if(this.labelWidth < 13 && this.labelmd == 0){
10285                 this.labelmd = this.labelWidth;
10286             }
10287             
10288             if(this.labellg > 0){
10289                 labelCfg.cls += ' col-lg-' + this.labellg;
10290                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10291             }
10292             
10293             if(this.labelmd > 0){
10294                 labelCfg.cls += ' col-md-' + this.labelmd;
10295                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10296             }
10297             
10298             if(this.labelsm > 0){
10299                 labelCfg.cls += ' col-sm-' + this.labelsm;
10300                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10301             }
10302             
10303             if(this.labelxs > 0){
10304                 labelCfg.cls += ' col-xs-' + this.labelxs;
10305                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10306             }
10307             
10308         } else if ( this.fieldLabel.length) {
10309 //                Roo.log(" label");
10310             cfg.cn = [
10311                 {
10312                    tag : 'i',
10313                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10314                    tooltip : 'This field is required'
10315                },
10316                {
10317                    tag: 'label',
10318                    //cls : 'input-group-addon',
10319                    html : this.fieldLabel
10320
10321                },
10322
10323                combobox
10324
10325             ];
10326             
10327             if(this.indicatorpos == 'right'){
10328                 
10329                 cfg.cn = [
10330                     {
10331                        tag: 'label',
10332                        cn : [
10333                            {
10334                                tag : 'span',
10335                                html : this.fieldLabel
10336                            },
10337                            {
10338                               tag : 'i',
10339                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10340                               tooltip : 'This field is required'
10341                            }
10342                        ]
10343
10344                     },
10345                     combobox
10346
10347                 ];
10348
10349             }
10350
10351         } else {
10352             
10353 //                Roo.log(" no label && no align");
10354                 cfg = combobox
10355                      
10356                 
10357         }
10358         
10359         var settings=this;
10360         ['xs','sm','md','lg'].map(function(size){
10361             if (settings[size]) {
10362                 cfg.cls += ' col-' + size + '-' + settings[size];
10363             }
10364         });
10365         
10366         return cfg;
10367         
10368     },
10369     
10370     
10371     
10372     // private
10373     onResize : function(w, h){
10374 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10375 //        if(typeof w == 'number'){
10376 //            var x = w - this.trigger.getWidth();
10377 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10378 //            this.trigger.setStyle('left', x+'px');
10379 //        }
10380     },
10381
10382     // private
10383     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10384
10385     // private
10386     getResizeEl : function(){
10387         return this.inputEl();
10388     },
10389
10390     // private
10391     getPositionEl : function(){
10392         return this.inputEl();
10393     },
10394
10395     // private
10396     alignErrorIcon : function(){
10397         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10398     },
10399
10400     // private
10401     initEvents : function(){
10402         
10403         this.createList();
10404         
10405         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10406         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10407         if(!this.multiple && this.showToggleBtn){
10408             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10409             if(this.hideTrigger){
10410                 this.trigger.setDisplayed(false);
10411             }
10412             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10413         }
10414         
10415         if(this.multiple){
10416             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10417         }
10418         
10419         if(this.removable && !this.editable && !this.tickable){
10420             var close = this.closeTriggerEl();
10421             
10422             if(close){
10423                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10424                 close.on('click', this.removeBtnClick, this, close);
10425             }
10426         }
10427         
10428         //this.trigger.addClassOnOver('x-form-trigger-over');
10429         //this.trigger.addClassOnClick('x-form-trigger-click');
10430         
10431         //if(!this.width){
10432         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10433         //}
10434     },
10435     
10436     closeTriggerEl : function()
10437     {
10438         var close = this.el.select('.roo-combo-removable-btn', true).first();
10439         return close ? close : false;
10440     },
10441     
10442     removeBtnClick : function(e, h, el)
10443     {
10444         e.preventDefault();
10445         
10446         if(this.fireEvent("remove", this) !== false){
10447             this.reset();
10448             this.fireEvent("afterremove", this)
10449         }
10450     },
10451     
10452     createList : function()
10453     {
10454         this.list = Roo.get(document.body).createChild({
10455             tag: 'ul',
10456             cls: 'typeahead typeahead-long dropdown-menu',
10457             style: 'display:none'
10458         });
10459         
10460         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10461         
10462     },
10463
10464     // private
10465     initTrigger : function(){
10466        
10467     },
10468
10469     // private
10470     onDestroy : function(){
10471         if(this.trigger){
10472             this.trigger.removeAllListeners();
10473           //  this.trigger.remove();
10474         }
10475         //if(this.wrap){
10476         //    this.wrap.remove();
10477         //}
10478         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10479     },
10480
10481     // private
10482     onFocus : function(){
10483         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10484         /*
10485         if(!this.mimicing){
10486             this.wrap.addClass('x-trigger-wrap-focus');
10487             this.mimicing = true;
10488             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10489             if(this.monitorTab){
10490                 this.el.on("keydown", this.checkTab, this);
10491             }
10492         }
10493         */
10494     },
10495
10496     // private
10497     checkTab : function(e){
10498         if(e.getKey() == e.TAB){
10499             this.triggerBlur();
10500         }
10501     },
10502
10503     // private
10504     onBlur : function(){
10505         // do nothing
10506     },
10507
10508     // private
10509     mimicBlur : function(e, t){
10510         /*
10511         if(!this.wrap.contains(t) && this.validateBlur()){
10512             this.triggerBlur();
10513         }
10514         */
10515     },
10516
10517     // private
10518     triggerBlur : function(){
10519         this.mimicing = false;
10520         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10521         if(this.monitorTab){
10522             this.el.un("keydown", this.checkTab, this);
10523         }
10524         //this.wrap.removeClass('x-trigger-wrap-focus');
10525         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10526     },
10527
10528     // private
10529     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10530     validateBlur : function(e, t){
10531         return true;
10532     },
10533
10534     // private
10535     onDisable : function(){
10536         this.inputEl().dom.disabled = true;
10537         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10538         //if(this.wrap){
10539         //    this.wrap.addClass('x-item-disabled');
10540         //}
10541     },
10542
10543     // private
10544     onEnable : function(){
10545         this.inputEl().dom.disabled = false;
10546         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10547         //if(this.wrap){
10548         //    this.el.removeClass('x-item-disabled');
10549         //}
10550     },
10551
10552     // private
10553     onShow : function(){
10554         var ae = this.getActionEl();
10555         
10556         if(ae){
10557             ae.dom.style.display = '';
10558             ae.dom.style.visibility = 'visible';
10559         }
10560     },
10561
10562     // private
10563     
10564     onHide : function(){
10565         var ae = this.getActionEl();
10566         ae.dom.style.display = 'none';
10567     },
10568
10569     /**
10570      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10571      * by an implementing function.
10572      * @method
10573      * @param {EventObject} e
10574      */
10575     onTriggerClick : Roo.emptyFn
10576 });
10577  /*
10578  * Based on:
10579  * Ext JS Library 1.1.1
10580  * Copyright(c) 2006-2007, Ext JS, LLC.
10581  *
10582  * Originally Released Under LGPL - original licence link has changed is not relivant.
10583  *
10584  * Fork - LGPL
10585  * <script type="text/javascript">
10586  */
10587
10588
10589 /**
10590  * @class Roo.data.SortTypes
10591  * @singleton
10592  * Defines the default sorting (casting?) comparison functions used when sorting data.
10593  */
10594 Roo.data.SortTypes = {
10595     /**
10596      * Default sort that does nothing
10597      * @param {Mixed} s The value being converted
10598      * @return {Mixed} The comparison value
10599      */
10600     none : function(s){
10601         return s;
10602     },
10603     
10604     /**
10605      * The regular expression used to strip tags
10606      * @type {RegExp}
10607      * @property
10608      */
10609     stripTagsRE : /<\/?[^>]+>/gi,
10610     
10611     /**
10612      * Strips all HTML tags to sort on text only
10613      * @param {Mixed} s The value being converted
10614      * @return {String} The comparison value
10615      */
10616     asText : function(s){
10617         return String(s).replace(this.stripTagsRE, "");
10618     },
10619     
10620     /**
10621      * Strips all HTML tags to sort on text only - Case insensitive
10622      * @param {Mixed} s The value being converted
10623      * @return {String} The comparison value
10624      */
10625     asUCText : function(s){
10626         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10627     },
10628     
10629     /**
10630      * Case insensitive string
10631      * @param {Mixed} s The value being converted
10632      * @return {String} The comparison value
10633      */
10634     asUCString : function(s) {
10635         return String(s).toUpperCase();
10636     },
10637     
10638     /**
10639      * Date sorting
10640      * @param {Mixed} s The value being converted
10641      * @return {Number} The comparison value
10642      */
10643     asDate : function(s) {
10644         if(!s){
10645             return 0;
10646         }
10647         if(s instanceof Date){
10648             return s.getTime();
10649         }
10650         return Date.parse(String(s));
10651     },
10652     
10653     /**
10654      * Float sorting
10655      * @param {Mixed} s The value being converted
10656      * @return {Float} The comparison value
10657      */
10658     asFloat : function(s) {
10659         var val = parseFloat(String(s).replace(/,/g, ""));
10660         if(isNaN(val)) {
10661             val = 0;
10662         }
10663         return val;
10664     },
10665     
10666     /**
10667      * Integer sorting
10668      * @param {Mixed} s The value being converted
10669      * @return {Number} The comparison value
10670      */
10671     asInt : function(s) {
10672         var val = parseInt(String(s).replace(/,/g, ""));
10673         if(isNaN(val)) {
10674             val = 0;
10675         }
10676         return val;
10677     }
10678 };/*
10679  * Based on:
10680  * Ext JS Library 1.1.1
10681  * Copyright(c) 2006-2007, Ext JS, LLC.
10682  *
10683  * Originally Released Under LGPL - original licence link has changed is not relivant.
10684  *
10685  * Fork - LGPL
10686  * <script type="text/javascript">
10687  */
10688
10689 /**
10690 * @class Roo.data.Record
10691  * Instances of this class encapsulate both record <em>definition</em> information, and record
10692  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10693  * to access Records cached in an {@link Roo.data.Store} object.<br>
10694  * <p>
10695  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10696  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10697  * objects.<br>
10698  * <p>
10699  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10700  * @constructor
10701  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10702  * {@link #create}. The parameters are the same.
10703  * @param {Array} data An associative Array of data values keyed by the field name.
10704  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10705  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10706  * not specified an integer id is generated.
10707  */
10708 Roo.data.Record = function(data, id){
10709     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10710     this.data = data;
10711 };
10712
10713 /**
10714  * Generate a constructor for a specific record layout.
10715  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10716  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10717  * Each field definition object may contain the following properties: <ul>
10718  * <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,
10719  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10720  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10721  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10722  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10723  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10724  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10725  * this may be omitted.</p></li>
10726  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10727  * <ul><li>auto (Default, implies no conversion)</li>
10728  * <li>string</li>
10729  * <li>int</li>
10730  * <li>float</li>
10731  * <li>boolean</li>
10732  * <li>date</li></ul></p></li>
10733  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10734  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10735  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10736  * by the Reader into an object that will be stored in the Record. It is passed the
10737  * following parameters:<ul>
10738  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10739  * </ul></p></li>
10740  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10741  * </ul>
10742  * <br>usage:<br><pre><code>
10743 var TopicRecord = Roo.data.Record.create(
10744     {name: 'title', mapping: 'topic_title'},
10745     {name: 'author', mapping: 'username'},
10746     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10747     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10748     {name: 'lastPoster', mapping: 'user2'},
10749     {name: 'excerpt', mapping: 'post_text'}
10750 );
10751
10752 var myNewRecord = new TopicRecord({
10753     title: 'Do my job please',
10754     author: 'noobie',
10755     totalPosts: 1,
10756     lastPost: new Date(),
10757     lastPoster: 'Animal',
10758     excerpt: 'No way dude!'
10759 });
10760 myStore.add(myNewRecord);
10761 </code></pre>
10762  * @method create
10763  * @static
10764  */
10765 Roo.data.Record.create = function(o){
10766     var f = function(){
10767         f.superclass.constructor.apply(this, arguments);
10768     };
10769     Roo.extend(f, Roo.data.Record);
10770     var p = f.prototype;
10771     p.fields = new Roo.util.MixedCollection(false, function(field){
10772         return field.name;
10773     });
10774     for(var i = 0, len = o.length; i < len; i++){
10775         p.fields.add(new Roo.data.Field(o[i]));
10776     }
10777     f.getField = function(name){
10778         return p.fields.get(name);  
10779     };
10780     return f;
10781 };
10782
10783 Roo.data.Record.AUTO_ID = 1000;
10784 Roo.data.Record.EDIT = 'edit';
10785 Roo.data.Record.REJECT = 'reject';
10786 Roo.data.Record.COMMIT = 'commit';
10787
10788 Roo.data.Record.prototype = {
10789     /**
10790      * Readonly flag - true if this record has been modified.
10791      * @type Boolean
10792      */
10793     dirty : false,
10794     editing : false,
10795     error: null,
10796     modified: null,
10797
10798     // private
10799     join : function(store){
10800         this.store = store;
10801     },
10802
10803     /**
10804      * Set the named field to the specified value.
10805      * @param {String} name The name of the field to set.
10806      * @param {Object} value The value to set the field to.
10807      */
10808     set : function(name, value){
10809         if(this.data[name] == value){
10810             return;
10811         }
10812         this.dirty = true;
10813         if(!this.modified){
10814             this.modified = {};
10815         }
10816         if(typeof this.modified[name] == 'undefined'){
10817             this.modified[name] = this.data[name];
10818         }
10819         this.data[name] = value;
10820         if(!this.editing && this.store){
10821             this.store.afterEdit(this);
10822         }       
10823     },
10824
10825     /**
10826      * Get the value of the named field.
10827      * @param {String} name The name of the field to get the value of.
10828      * @return {Object} The value of the field.
10829      */
10830     get : function(name){
10831         return this.data[name]; 
10832     },
10833
10834     // private
10835     beginEdit : function(){
10836         this.editing = true;
10837         this.modified = {}; 
10838     },
10839
10840     // private
10841     cancelEdit : function(){
10842         this.editing = false;
10843         delete this.modified;
10844     },
10845
10846     // private
10847     endEdit : function(){
10848         this.editing = false;
10849         if(this.dirty && this.store){
10850             this.store.afterEdit(this);
10851         }
10852     },
10853
10854     /**
10855      * Usually called by the {@link Roo.data.Store} which owns the Record.
10856      * Rejects all changes made to the Record since either creation, or the last commit operation.
10857      * Modified fields are reverted to their original values.
10858      * <p>
10859      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10860      * of reject operations.
10861      */
10862     reject : function(){
10863         var m = this.modified;
10864         for(var n in m){
10865             if(typeof m[n] != "function"){
10866                 this.data[n] = m[n];
10867             }
10868         }
10869         this.dirty = false;
10870         delete this.modified;
10871         this.editing = false;
10872         if(this.store){
10873             this.store.afterReject(this);
10874         }
10875     },
10876
10877     /**
10878      * Usually called by the {@link Roo.data.Store} which owns the Record.
10879      * Commits all changes made to the Record since either creation, or the last commit operation.
10880      * <p>
10881      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10882      * of commit operations.
10883      */
10884     commit : function(){
10885         this.dirty = false;
10886         delete this.modified;
10887         this.editing = false;
10888         if(this.store){
10889             this.store.afterCommit(this);
10890         }
10891     },
10892
10893     // private
10894     hasError : function(){
10895         return this.error != null;
10896     },
10897
10898     // private
10899     clearError : function(){
10900         this.error = null;
10901     },
10902
10903     /**
10904      * Creates a copy of this record.
10905      * @param {String} id (optional) A new record id if you don't want to use this record's id
10906      * @return {Record}
10907      */
10908     copy : function(newId) {
10909         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10910     }
10911 };/*
10912  * Based on:
10913  * Ext JS Library 1.1.1
10914  * Copyright(c) 2006-2007, Ext JS, LLC.
10915  *
10916  * Originally Released Under LGPL - original licence link has changed is not relivant.
10917  *
10918  * Fork - LGPL
10919  * <script type="text/javascript">
10920  */
10921
10922
10923
10924 /**
10925  * @class Roo.data.Store
10926  * @extends Roo.util.Observable
10927  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10928  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10929  * <p>
10930  * 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
10931  * has no knowledge of the format of the data returned by the Proxy.<br>
10932  * <p>
10933  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10934  * instances from the data object. These records are cached and made available through accessor functions.
10935  * @constructor
10936  * Creates a new Store.
10937  * @param {Object} config A config object containing the objects needed for the Store to access data,
10938  * and read the data into Records.
10939  */
10940 Roo.data.Store = function(config){
10941     this.data = new Roo.util.MixedCollection(false);
10942     this.data.getKey = function(o){
10943         return o.id;
10944     };
10945     this.baseParams = {};
10946     // private
10947     this.paramNames = {
10948         "start" : "start",
10949         "limit" : "limit",
10950         "sort" : "sort",
10951         "dir" : "dir",
10952         "multisort" : "_multisort"
10953     };
10954
10955     if(config && config.data){
10956         this.inlineData = config.data;
10957         delete config.data;
10958     }
10959
10960     Roo.apply(this, config);
10961     
10962     if(this.reader){ // reader passed
10963         this.reader = Roo.factory(this.reader, Roo.data);
10964         this.reader.xmodule = this.xmodule || false;
10965         if(!this.recordType){
10966             this.recordType = this.reader.recordType;
10967         }
10968         if(this.reader.onMetaChange){
10969             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10970         }
10971     }
10972
10973     if(this.recordType){
10974         this.fields = this.recordType.prototype.fields;
10975     }
10976     this.modified = [];
10977
10978     this.addEvents({
10979         /**
10980          * @event datachanged
10981          * Fires when the data cache has changed, and a widget which is using this Store
10982          * as a Record cache should refresh its view.
10983          * @param {Store} this
10984          */
10985         datachanged : true,
10986         /**
10987          * @event metachange
10988          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10989          * @param {Store} this
10990          * @param {Object} meta The JSON metadata
10991          */
10992         metachange : true,
10993         /**
10994          * @event add
10995          * Fires when Records have been added to the Store
10996          * @param {Store} this
10997          * @param {Roo.data.Record[]} records The array of Records added
10998          * @param {Number} index The index at which the record(s) were added
10999          */
11000         add : true,
11001         /**
11002          * @event remove
11003          * Fires when a Record has been removed from the Store
11004          * @param {Store} this
11005          * @param {Roo.data.Record} record The Record that was removed
11006          * @param {Number} index The index at which the record was removed
11007          */
11008         remove : true,
11009         /**
11010          * @event update
11011          * Fires when a Record has been updated
11012          * @param {Store} this
11013          * @param {Roo.data.Record} record The Record that was updated
11014          * @param {String} operation The update operation being performed.  Value may be one of:
11015          * <pre><code>
11016  Roo.data.Record.EDIT
11017  Roo.data.Record.REJECT
11018  Roo.data.Record.COMMIT
11019          * </code></pre>
11020          */
11021         update : true,
11022         /**
11023          * @event clear
11024          * Fires when the data cache has been cleared.
11025          * @param {Store} this
11026          */
11027         clear : true,
11028         /**
11029          * @event beforeload
11030          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11031          * the load action will be canceled.
11032          * @param {Store} this
11033          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11034          */
11035         beforeload : true,
11036         /**
11037          * @event beforeloadadd
11038          * Fires after a new set of Records has been loaded.
11039          * @param {Store} this
11040          * @param {Roo.data.Record[]} records The Records that were loaded
11041          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11042          */
11043         beforeloadadd : true,
11044         /**
11045          * @event load
11046          * Fires after a new set of Records has been loaded, before they are added to the store.
11047          * @param {Store} this
11048          * @param {Roo.data.Record[]} records The Records that were loaded
11049          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11050          * @params {Object} return from reader
11051          */
11052         load : true,
11053         /**
11054          * @event loadexception
11055          * Fires if an exception occurs in the Proxy during loading.
11056          * Called with the signature of the Proxy's "loadexception" event.
11057          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11058          * 
11059          * @param {Proxy} 
11060          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11061          * @param {Object} load options 
11062          * @param {Object} jsonData from your request (normally this contains the Exception)
11063          */
11064         loadexception : true
11065     });
11066     
11067     if(this.proxy){
11068         this.proxy = Roo.factory(this.proxy, Roo.data);
11069         this.proxy.xmodule = this.xmodule || false;
11070         this.relayEvents(this.proxy,  ["loadexception"]);
11071     }
11072     this.sortToggle = {};
11073     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11074
11075     Roo.data.Store.superclass.constructor.call(this);
11076
11077     if(this.inlineData){
11078         this.loadData(this.inlineData);
11079         delete this.inlineData;
11080     }
11081 };
11082
11083 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11084      /**
11085     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11086     * without a remote query - used by combo/forms at present.
11087     */
11088     
11089     /**
11090     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11091     */
11092     /**
11093     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11094     */
11095     /**
11096     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11097     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11098     */
11099     /**
11100     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11101     * on any HTTP request
11102     */
11103     /**
11104     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11105     */
11106     /**
11107     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11108     */
11109     multiSort: false,
11110     /**
11111     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11112     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11113     */
11114     remoteSort : false,
11115
11116     /**
11117     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11118      * loaded or when a record is removed. (defaults to false).
11119     */
11120     pruneModifiedRecords : false,
11121
11122     // private
11123     lastOptions : null,
11124
11125     /**
11126      * Add Records to the Store and fires the add event.
11127      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11128      */
11129     add : function(records){
11130         records = [].concat(records);
11131         for(var i = 0, len = records.length; i < len; i++){
11132             records[i].join(this);
11133         }
11134         var index = this.data.length;
11135         this.data.addAll(records);
11136         this.fireEvent("add", this, records, index);
11137     },
11138
11139     /**
11140      * Remove a Record from the Store and fires the remove event.
11141      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11142      */
11143     remove : function(record){
11144         var index = this.data.indexOf(record);
11145         this.data.removeAt(index);
11146  
11147         if(this.pruneModifiedRecords){
11148             this.modified.remove(record);
11149         }
11150         this.fireEvent("remove", this, record, index);
11151     },
11152
11153     /**
11154      * Remove all Records from the Store and fires the clear event.
11155      */
11156     removeAll : function(){
11157         this.data.clear();
11158         if(this.pruneModifiedRecords){
11159             this.modified = [];
11160         }
11161         this.fireEvent("clear", this);
11162     },
11163
11164     /**
11165      * Inserts Records to the Store at the given index and fires the add event.
11166      * @param {Number} index The start index at which to insert the passed Records.
11167      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11168      */
11169     insert : function(index, records){
11170         records = [].concat(records);
11171         for(var i = 0, len = records.length; i < len; i++){
11172             this.data.insert(index, records[i]);
11173             records[i].join(this);
11174         }
11175         this.fireEvent("add", this, records, index);
11176     },
11177
11178     /**
11179      * Get the index within the cache of the passed Record.
11180      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11181      * @return {Number} The index of the passed Record. Returns -1 if not found.
11182      */
11183     indexOf : function(record){
11184         return this.data.indexOf(record);
11185     },
11186
11187     /**
11188      * Get the index within the cache of the Record with the passed id.
11189      * @param {String} id The id of the Record to find.
11190      * @return {Number} The index of the Record. Returns -1 if not found.
11191      */
11192     indexOfId : function(id){
11193         return this.data.indexOfKey(id);
11194     },
11195
11196     /**
11197      * Get the Record with the specified id.
11198      * @param {String} id The id of the Record to find.
11199      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11200      */
11201     getById : function(id){
11202         return this.data.key(id);
11203     },
11204
11205     /**
11206      * Get the Record at the specified index.
11207      * @param {Number} index The index of the Record to find.
11208      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11209      */
11210     getAt : function(index){
11211         return this.data.itemAt(index);
11212     },
11213
11214     /**
11215      * Returns a range of Records between specified indices.
11216      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11217      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11218      * @return {Roo.data.Record[]} An array of Records
11219      */
11220     getRange : function(start, end){
11221         return this.data.getRange(start, end);
11222     },
11223
11224     // private
11225     storeOptions : function(o){
11226         o = Roo.apply({}, o);
11227         delete o.callback;
11228         delete o.scope;
11229         this.lastOptions = o;
11230     },
11231
11232     /**
11233      * Loads the Record cache from the configured Proxy using the configured Reader.
11234      * <p>
11235      * If using remote paging, then the first load call must specify the <em>start</em>
11236      * and <em>limit</em> properties in the options.params property to establish the initial
11237      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11238      * <p>
11239      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11240      * and this call will return before the new data has been loaded. Perform any post-processing
11241      * in a callback function, or in a "load" event handler.</strong>
11242      * <p>
11243      * @param {Object} options An object containing properties which control loading options:<ul>
11244      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11245      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11246      * passed the following arguments:<ul>
11247      * <li>r : Roo.data.Record[]</li>
11248      * <li>options: Options object from the load call</li>
11249      * <li>success: Boolean success indicator</li></ul></li>
11250      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11251      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11252      * </ul>
11253      */
11254     load : function(options){
11255         options = options || {};
11256         if(this.fireEvent("beforeload", this, options) !== false){
11257             this.storeOptions(options);
11258             var p = Roo.apply(options.params || {}, this.baseParams);
11259             // if meta was not loaded from remote source.. try requesting it.
11260             if (!this.reader.metaFromRemote) {
11261                 p._requestMeta = 1;
11262             }
11263             if(this.sortInfo && this.remoteSort){
11264                 var pn = this.paramNames;
11265                 p[pn["sort"]] = this.sortInfo.field;
11266                 p[pn["dir"]] = this.sortInfo.direction;
11267             }
11268             if (this.multiSort) {
11269                 var pn = this.paramNames;
11270                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11271             }
11272             
11273             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11274         }
11275     },
11276
11277     /**
11278      * Reloads the Record cache from the configured Proxy using the configured Reader and
11279      * the options from the last load operation performed.
11280      * @param {Object} options (optional) An object containing properties which may override the options
11281      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11282      * the most recently used options are reused).
11283      */
11284     reload : function(options){
11285         this.load(Roo.applyIf(options||{}, this.lastOptions));
11286     },
11287
11288     // private
11289     // Called as a callback by the Reader during a load operation.
11290     loadRecords : function(o, options, success){
11291         if(!o || success === false){
11292             if(success !== false){
11293                 this.fireEvent("load", this, [], options, o);
11294             }
11295             if(options.callback){
11296                 options.callback.call(options.scope || this, [], options, false);
11297             }
11298             return;
11299         }
11300         // if data returned failure - throw an exception.
11301         if (o.success === false) {
11302             // show a message if no listener is registered.
11303             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11304                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11305             }
11306             // loadmask wil be hooked into this..
11307             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11308             return;
11309         }
11310         var r = o.records, t = o.totalRecords || r.length;
11311         
11312         this.fireEvent("beforeloadadd", this, r, options, o);
11313         
11314         if(!options || options.add !== true){
11315             if(this.pruneModifiedRecords){
11316                 this.modified = [];
11317             }
11318             for(var i = 0, len = r.length; i < len; i++){
11319                 r[i].join(this);
11320             }
11321             if(this.snapshot){
11322                 this.data = this.snapshot;
11323                 delete this.snapshot;
11324             }
11325             this.data.clear();
11326             this.data.addAll(r);
11327             this.totalLength = t;
11328             this.applySort();
11329             this.fireEvent("datachanged", this);
11330         }else{
11331             this.totalLength = Math.max(t, this.data.length+r.length);
11332             this.add(r);
11333         }
11334         
11335         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11336                 
11337             var e = new Roo.data.Record({});
11338
11339             e.set(this.parent.displayField, this.parent.emptyTitle);
11340             e.set(this.parent.valueField, '');
11341
11342             this.insert(0, e);
11343         }
11344             
11345         this.fireEvent("load", this, r, options, o);
11346         if(options.callback){
11347             options.callback.call(options.scope || this, r, options, true);
11348         }
11349     },
11350
11351
11352     /**
11353      * Loads data from a passed data block. A Reader which understands the format of the data
11354      * must have been configured in the constructor.
11355      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11356      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11357      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11358      */
11359     loadData : function(o, append){
11360         var r = this.reader.readRecords(o);
11361         this.loadRecords(r, {add: append}, true);
11362     },
11363
11364     /**
11365      * Gets the number of cached records.
11366      * <p>
11367      * <em>If using paging, this may not be the total size of the dataset. If the data object
11368      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11369      * the data set size</em>
11370      */
11371     getCount : function(){
11372         return this.data.length || 0;
11373     },
11374
11375     /**
11376      * Gets the total number of records in the dataset as returned by the server.
11377      * <p>
11378      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11379      * the dataset size</em>
11380      */
11381     getTotalCount : function(){
11382         return this.totalLength || 0;
11383     },
11384
11385     /**
11386      * Returns the sort state of the Store as an object with two properties:
11387      * <pre><code>
11388  field {String} The name of the field by which the Records are sorted
11389  direction {String} The sort order, "ASC" or "DESC"
11390      * </code></pre>
11391      */
11392     getSortState : function(){
11393         return this.sortInfo;
11394     },
11395
11396     // private
11397     applySort : function(){
11398         if(this.sortInfo && !this.remoteSort){
11399             var s = this.sortInfo, f = s.field;
11400             var st = this.fields.get(f).sortType;
11401             var fn = function(r1, r2){
11402                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11403                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11404             };
11405             this.data.sort(s.direction, fn);
11406             if(this.snapshot && this.snapshot != this.data){
11407                 this.snapshot.sort(s.direction, fn);
11408             }
11409         }
11410     },
11411
11412     /**
11413      * Sets the default sort column and order to be used by the next load operation.
11414      * @param {String} fieldName The name of the field to sort by.
11415      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11416      */
11417     setDefaultSort : function(field, dir){
11418         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11419     },
11420
11421     /**
11422      * Sort the Records.
11423      * If remote sorting is used, the sort is performed on the server, and the cache is
11424      * reloaded. If local sorting is used, the cache is sorted internally.
11425      * @param {String} fieldName The name of the field to sort by.
11426      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11427      */
11428     sort : function(fieldName, dir){
11429         var f = this.fields.get(fieldName);
11430         if(!dir){
11431             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11432             
11433             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11434                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11435             }else{
11436                 dir = f.sortDir;
11437             }
11438         }
11439         this.sortToggle[f.name] = dir;
11440         this.sortInfo = {field: f.name, direction: dir};
11441         if(!this.remoteSort){
11442             this.applySort();
11443             this.fireEvent("datachanged", this);
11444         }else{
11445             this.load(this.lastOptions);
11446         }
11447     },
11448
11449     /**
11450      * Calls the specified function for each of the Records in the cache.
11451      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11452      * Returning <em>false</em> aborts and exits the iteration.
11453      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11454      */
11455     each : function(fn, scope){
11456         this.data.each(fn, scope);
11457     },
11458
11459     /**
11460      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11461      * (e.g., during paging).
11462      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11463      */
11464     getModifiedRecords : function(){
11465         return this.modified;
11466     },
11467
11468     // private
11469     createFilterFn : function(property, value, anyMatch){
11470         if(!value.exec){ // not a regex
11471             value = String(value);
11472             if(value.length == 0){
11473                 return false;
11474             }
11475             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11476         }
11477         return function(r){
11478             return value.test(r.data[property]);
11479         };
11480     },
11481
11482     /**
11483      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11484      * @param {String} property A field on your records
11485      * @param {Number} start The record index to start at (defaults to 0)
11486      * @param {Number} end The last record index to include (defaults to length - 1)
11487      * @return {Number} The sum
11488      */
11489     sum : function(property, start, end){
11490         var rs = this.data.items, v = 0;
11491         start = start || 0;
11492         end = (end || end === 0) ? end : rs.length-1;
11493
11494         for(var i = start; i <= end; i++){
11495             v += (rs[i].data[property] || 0);
11496         }
11497         return v;
11498     },
11499
11500     /**
11501      * Filter the records by a specified property.
11502      * @param {String} field A field on your records
11503      * @param {String/RegExp} value Either a string that the field
11504      * should start with or a RegExp to test against the field
11505      * @param {Boolean} anyMatch True to match any part not just the beginning
11506      */
11507     filter : function(property, value, anyMatch){
11508         var fn = this.createFilterFn(property, value, anyMatch);
11509         return fn ? this.filterBy(fn) : this.clearFilter();
11510     },
11511
11512     /**
11513      * Filter by a function. The specified function will be called with each
11514      * record in this data source. If the function returns true the record is included,
11515      * otherwise it is filtered.
11516      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11517      * @param {Object} scope (optional) The scope of the function (defaults to this)
11518      */
11519     filterBy : function(fn, scope){
11520         this.snapshot = this.snapshot || this.data;
11521         this.data = this.queryBy(fn, scope||this);
11522         this.fireEvent("datachanged", this);
11523     },
11524
11525     /**
11526      * Query the records by a specified property.
11527      * @param {String} field A field on your records
11528      * @param {String/RegExp} value Either a string that the field
11529      * should start with or a RegExp to test against the field
11530      * @param {Boolean} anyMatch True to match any part not just the beginning
11531      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11532      */
11533     query : function(property, value, anyMatch){
11534         var fn = this.createFilterFn(property, value, anyMatch);
11535         return fn ? this.queryBy(fn) : this.data.clone();
11536     },
11537
11538     /**
11539      * Query by a function. The specified function will be called with each
11540      * record in this data source. If the function returns true the record is included
11541      * in the results.
11542      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11543      * @param {Object} scope (optional) The scope of the function (defaults to this)
11544       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11545      **/
11546     queryBy : function(fn, scope){
11547         var data = this.snapshot || this.data;
11548         return data.filterBy(fn, scope||this);
11549     },
11550
11551     /**
11552      * Collects unique values for a particular dataIndex from this store.
11553      * @param {String} dataIndex The property to collect
11554      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11555      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11556      * @return {Array} An array of the unique values
11557      **/
11558     collect : function(dataIndex, allowNull, bypassFilter){
11559         var d = (bypassFilter === true && this.snapshot) ?
11560                 this.snapshot.items : this.data.items;
11561         var v, sv, r = [], l = {};
11562         for(var i = 0, len = d.length; i < len; i++){
11563             v = d[i].data[dataIndex];
11564             sv = String(v);
11565             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11566                 l[sv] = true;
11567                 r[r.length] = v;
11568             }
11569         }
11570         return r;
11571     },
11572
11573     /**
11574      * Revert to a view of the Record cache with no filtering applied.
11575      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11576      */
11577     clearFilter : function(suppressEvent){
11578         if(this.snapshot && this.snapshot != this.data){
11579             this.data = this.snapshot;
11580             delete this.snapshot;
11581             if(suppressEvent !== true){
11582                 this.fireEvent("datachanged", this);
11583             }
11584         }
11585     },
11586
11587     // private
11588     afterEdit : function(record){
11589         if(this.modified.indexOf(record) == -1){
11590             this.modified.push(record);
11591         }
11592         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11593     },
11594     
11595     // private
11596     afterReject : function(record){
11597         this.modified.remove(record);
11598         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11599     },
11600
11601     // private
11602     afterCommit : function(record){
11603         this.modified.remove(record);
11604         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11605     },
11606
11607     /**
11608      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11609      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11610      */
11611     commitChanges : function(){
11612         var m = this.modified.slice(0);
11613         this.modified = [];
11614         for(var i = 0, len = m.length; i < len; i++){
11615             m[i].commit();
11616         }
11617     },
11618
11619     /**
11620      * Cancel outstanding changes on all changed records.
11621      */
11622     rejectChanges : function(){
11623         var m = this.modified.slice(0);
11624         this.modified = [];
11625         for(var i = 0, len = m.length; i < len; i++){
11626             m[i].reject();
11627         }
11628     },
11629
11630     onMetaChange : function(meta, rtype, o){
11631         this.recordType = rtype;
11632         this.fields = rtype.prototype.fields;
11633         delete this.snapshot;
11634         this.sortInfo = meta.sortInfo || this.sortInfo;
11635         this.modified = [];
11636         this.fireEvent('metachange', this, this.reader.meta);
11637     },
11638     
11639     moveIndex : function(data, type)
11640     {
11641         var index = this.indexOf(data);
11642         
11643         var newIndex = index + type;
11644         
11645         this.remove(data);
11646         
11647         this.insert(newIndex, data);
11648         
11649     }
11650 });/*
11651  * Based on:
11652  * Ext JS Library 1.1.1
11653  * Copyright(c) 2006-2007, Ext JS, LLC.
11654  *
11655  * Originally Released Under LGPL - original licence link has changed is not relivant.
11656  *
11657  * Fork - LGPL
11658  * <script type="text/javascript">
11659  */
11660
11661 /**
11662  * @class Roo.data.SimpleStore
11663  * @extends Roo.data.Store
11664  * Small helper class to make creating Stores from Array data easier.
11665  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11666  * @cfg {Array} fields An array of field definition objects, or field name strings.
11667  * @cfg {Array} data The multi-dimensional array of data
11668  * @constructor
11669  * @param {Object} config
11670  */
11671 Roo.data.SimpleStore = function(config){
11672     Roo.data.SimpleStore.superclass.constructor.call(this, {
11673         isLocal : true,
11674         reader: new Roo.data.ArrayReader({
11675                 id: config.id
11676             },
11677             Roo.data.Record.create(config.fields)
11678         ),
11679         proxy : new Roo.data.MemoryProxy(config.data)
11680     });
11681     this.load();
11682 };
11683 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
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 /**
11696  * @extends Roo.data.Store
11697  * @class Roo.data.JsonStore
11698  * Small helper class to make creating Stores for JSON data easier. <br/>
11699 <pre><code>
11700 var store = new Roo.data.JsonStore({
11701     url: 'get-images.php',
11702     root: 'images',
11703     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11704 });
11705 </code></pre>
11706  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11707  * JsonReader and HttpProxy (unless inline data is provided).</b>
11708  * @cfg {Array} fields An array of field definition objects, or field name strings.
11709  * @constructor
11710  * @param {Object} config
11711  */
11712 Roo.data.JsonStore = function(c){
11713     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11714         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11715         reader: new Roo.data.JsonReader(c, c.fields)
11716     }));
11717 };
11718 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11719  * Based on:
11720  * Ext JS Library 1.1.1
11721  * Copyright(c) 2006-2007, Ext JS, LLC.
11722  *
11723  * Originally Released Under LGPL - original licence link has changed is not relivant.
11724  *
11725  * Fork - LGPL
11726  * <script type="text/javascript">
11727  */
11728
11729  
11730 Roo.data.Field = function(config){
11731     if(typeof config == "string"){
11732         config = {name: config};
11733     }
11734     Roo.apply(this, config);
11735     
11736     if(!this.type){
11737         this.type = "auto";
11738     }
11739     
11740     var st = Roo.data.SortTypes;
11741     // named sortTypes are supported, here we look them up
11742     if(typeof this.sortType == "string"){
11743         this.sortType = st[this.sortType];
11744     }
11745     
11746     // set default sortType for strings and dates
11747     if(!this.sortType){
11748         switch(this.type){
11749             case "string":
11750                 this.sortType = st.asUCString;
11751                 break;
11752             case "date":
11753                 this.sortType = st.asDate;
11754                 break;
11755             default:
11756                 this.sortType = st.none;
11757         }
11758     }
11759
11760     // define once
11761     var stripRe = /[\$,%]/g;
11762
11763     // prebuilt conversion function for this field, instead of
11764     // switching every time we're reading a value
11765     if(!this.convert){
11766         var cv, dateFormat = this.dateFormat;
11767         switch(this.type){
11768             case "":
11769             case "auto":
11770             case undefined:
11771                 cv = function(v){ return v; };
11772                 break;
11773             case "string":
11774                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11775                 break;
11776             case "int":
11777                 cv = function(v){
11778                     return v !== undefined && v !== null && v !== '' ?
11779                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11780                     };
11781                 break;
11782             case "float":
11783                 cv = function(v){
11784                     return v !== undefined && v !== null && v !== '' ?
11785                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11786                     };
11787                 break;
11788             case "bool":
11789             case "boolean":
11790                 cv = function(v){ return v === true || v === "true" || v == 1; };
11791                 break;
11792             case "date":
11793                 cv = function(v){
11794                     if(!v){
11795                         return '';
11796                     }
11797                     if(v instanceof Date){
11798                         return v;
11799                     }
11800                     if(dateFormat){
11801                         if(dateFormat == "timestamp"){
11802                             return new Date(v*1000);
11803                         }
11804                         return Date.parseDate(v, dateFormat);
11805                     }
11806                     var parsed = Date.parse(v);
11807                     return parsed ? new Date(parsed) : null;
11808                 };
11809              break;
11810             
11811         }
11812         this.convert = cv;
11813     }
11814 };
11815
11816 Roo.data.Field.prototype = {
11817     dateFormat: null,
11818     defaultValue: "",
11819     mapping: null,
11820     sortType : null,
11821     sortDir : "ASC"
11822 };/*
11823  * Based on:
11824  * Ext JS Library 1.1.1
11825  * Copyright(c) 2006-2007, Ext JS, LLC.
11826  *
11827  * Originally Released Under LGPL - original licence link has changed is not relivant.
11828  *
11829  * Fork - LGPL
11830  * <script type="text/javascript">
11831  */
11832  
11833 // Base class for reading structured data from a data source.  This class is intended to be
11834 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11835
11836 /**
11837  * @class Roo.data.DataReader
11838  * Base class for reading structured data from a data source.  This class is intended to be
11839  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11840  */
11841
11842 Roo.data.DataReader = function(meta, recordType){
11843     
11844     this.meta = meta;
11845     
11846     this.recordType = recordType instanceof Array ? 
11847         Roo.data.Record.create(recordType) : recordType;
11848 };
11849
11850 Roo.data.DataReader.prototype = {
11851      /**
11852      * Create an empty record
11853      * @param {Object} data (optional) - overlay some values
11854      * @return {Roo.data.Record} record created.
11855      */
11856     newRow :  function(d) {
11857         var da =  {};
11858         this.recordType.prototype.fields.each(function(c) {
11859             switch( c.type) {
11860                 case 'int' : da[c.name] = 0; break;
11861                 case 'date' : da[c.name] = new Date(); break;
11862                 case 'float' : da[c.name] = 0.0; break;
11863                 case 'boolean' : da[c.name] = false; break;
11864                 default : da[c.name] = ""; break;
11865             }
11866             
11867         });
11868         return new this.recordType(Roo.apply(da, d));
11869     }
11870     
11871 };/*
11872  * Based on:
11873  * Ext JS Library 1.1.1
11874  * Copyright(c) 2006-2007, Ext JS, LLC.
11875  *
11876  * Originally Released Under LGPL - original licence link has changed is not relivant.
11877  *
11878  * Fork - LGPL
11879  * <script type="text/javascript">
11880  */
11881
11882 /**
11883  * @class Roo.data.DataProxy
11884  * @extends Roo.data.Observable
11885  * This class is an abstract base class for implementations which provide retrieval of
11886  * unformatted data objects.<br>
11887  * <p>
11888  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11889  * (of the appropriate type which knows how to parse the data object) to provide a block of
11890  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11891  * <p>
11892  * Custom implementations must implement the load method as described in
11893  * {@link Roo.data.HttpProxy#load}.
11894  */
11895 Roo.data.DataProxy = function(){
11896     this.addEvents({
11897         /**
11898          * @event beforeload
11899          * Fires before a network request is made to retrieve a data object.
11900          * @param {Object} This DataProxy object.
11901          * @param {Object} params The params parameter to the load function.
11902          */
11903         beforeload : true,
11904         /**
11905          * @event load
11906          * Fires before the load method's callback is called.
11907          * @param {Object} This DataProxy object.
11908          * @param {Object} o The data object.
11909          * @param {Object} arg The callback argument object passed to the load function.
11910          */
11911         load : true,
11912         /**
11913          * @event loadexception
11914          * Fires if an Exception occurs during data retrieval.
11915          * @param {Object} This DataProxy object.
11916          * @param {Object} o The data object.
11917          * @param {Object} arg The callback argument object passed to the load function.
11918          * @param {Object} e The Exception.
11919          */
11920         loadexception : true
11921     });
11922     Roo.data.DataProxy.superclass.constructor.call(this);
11923 };
11924
11925 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11926
11927     /**
11928      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11929      */
11930 /*
11931  * Based on:
11932  * Ext JS Library 1.1.1
11933  * Copyright(c) 2006-2007, Ext JS, LLC.
11934  *
11935  * Originally Released Under LGPL - original licence link has changed is not relivant.
11936  *
11937  * Fork - LGPL
11938  * <script type="text/javascript">
11939  */
11940 /**
11941  * @class Roo.data.MemoryProxy
11942  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11943  * to the Reader when its load method is called.
11944  * @constructor
11945  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11946  */
11947 Roo.data.MemoryProxy = function(data){
11948     if (data.data) {
11949         data = data.data;
11950     }
11951     Roo.data.MemoryProxy.superclass.constructor.call(this);
11952     this.data = data;
11953 };
11954
11955 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11956     
11957     /**
11958      * Load data from the requested source (in this case an in-memory
11959      * data object passed to the constructor), read the data object into
11960      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11961      * process that block using the passed callback.
11962      * @param {Object} params This parameter is not used by the MemoryProxy class.
11963      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11964      * object into a block of Roo.data.Records.
11965      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11966      * The function must be passed <ul>
11967      * <li>The Record block object</li>
11968      * <li>The "arg" argument from the load function</li>
11969      * <li>A boolean success indicator</li>
11970      * </ul>
11971      * @param {Object} scope The scope in which to call the callback
11972      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11973      */
11974     load : function(params, reader, callback, scope, arg){
11975         params = params || {};
11976         var result;
11977         try {
11978             result = reader.readRecords(this.data);
11979         }catch(e){
11980             this.fireEvent("loadexception", this, arg, null, e);
11981             callback.call(scope, null, arg, false);
11982             return;
11983         }
11984         callback.call(scope, result, arg, true);
11985     },
11986     
11987     // private
11988     update : function(params, records){
11989         
11990     }
11991 });/*
11992  * Based on:
11993  * Ext JS Library 1.1.1
11994  * Copyright(c) 2006-2007, Ext JS, LLC.
11995  *
11996  * Originally Released Under LGPL - original licence link has changed is not relivant.
11997  *
11998  * Fork - LGPL
11999  * <script type="text/javascript">
12000  */
12001 /**
12002  * @class Roo.data.HttpProxy
12003  * @extends Roo.data.DataProxy
12004  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12005  * configured to reference a certain URL.<br><br>
12006  * <p>
12007  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12008  * from which the running page was served.<br><br>
12009  * <p>
12010  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12011  * <p>
12012  * Be aware that to enable the browser to parse an XML document, the server must set
12013  * the Content-Type header in the HTTP response to "text/xml".
12014  * @constructor
12015  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12016  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12017  * will be used to make the request.
12018  */
12019 Roo.data.HttpProxy = function(conn){
12020     Roo.data.HttpProxy.superclass.constructor.call(this);
12021     // is conn a conn config or a real conn?
12022     this.conn = conn;
12023     this.useAjax = !conn || !conn.events;
12024   
12025 };
12026
12027 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12028     // thse are take from connection...
12029     
12030     /**
12031      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12032      */
12033     /**
12034      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12035      * extra parameters to each request made by this object. (defaults to undefined)
12036      */
12037     /**
12038      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12039      *  to each request made by this object. (defaults to undefined)
12040      */
12041     /**
12042      * @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)
12043      */
12044     /**
12045      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12046      */
12047      /**
12048      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12049      * @type Boolean
12050      */
12051   
12052
12053     /**
12054      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12055      * @type Boolean
12056      */
12057     /**
12058      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12059      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12060      * a finer-grained basis than the DataProxy events.
12061      */
12062     getConnection : function(){
12063         return this.useAjax ? Roo.Ajax : this.conn;
12064     },
12065
12066     /**
12067      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12068      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12069      * process that block using the passed callback.
12070      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12071      * for the request to the remote server.
12072      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12073      * object into a block of Roo.data.Records.
12074      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12075      * The function must be passed <ul>
12076      * <li>The Record block object</li>
12077      * <li>The "arg" argument from the load function</li>
12078      * <li>A boolean success indicator</li>
12079      * </ul>
12080      * @param {Object} scope The scope in which to call the callback
12081      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12082      */
12083     load : function(params, reader, callback, scope, arg){
12084         if(this.fireEvent("beforeload", this, params) !== false){
12085             var  o = {
12086                 params : params || {},
12087                 request: {
12088                     callback : callback,
12089                     scope : scope,
12090                     arg : arg
12091                 },
12092                 reader: reader,
12093                 callback : this.loadResponse,
12094                 scope: this
12095             };
12096             if(this.useAjax){
12097                 Roo.applyIf(o, this.conn);
12098                 if(this.activeRequest){
12099                     Roo.Ajax.abort(this.activeRequest);
12100                 }
12101                 this.activeRequest = Roo.Ajax.request(o);
12102             }else{
12103                 this.conn.request(o);
12104             }
12105         }else{
12106             callback.call(scope||this, null, arg, false);
12107         }
12108     },
12109
12110     // private
12111     loadResponse : function(o, success, response){
12112         delete this.activeRequest;
12113         if(!success){
12114             this.fireEvent("loadexception", this, o, response);
12115             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12116             return;
12117         }
12118         var result;
12119         try {
12120             result = o.reader.read(response);
12121         }catch(e){
12122             this.fireEvent("loadexception", this, o, response, e);
12123             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12124             return;
12125         }
12126         
12127         this.fireEvent("load", this, o, o.request.arg);
12128         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12129     },
12130
12131     // private
12132     update : function(dataSet){
12133
12134     },
12135
12136     // private
12137     updateResponse : function(dataSet){
12138
12139     }
12140 });/*
12141  * Based on:
12142  * Ext JS Library 1.1.1
12143  * Copyright(c) 2006-2007, Ext JS, LLC.
12144  *
12145  * Originally Released Under LGPL - original licence link has changed is not relivant.
12146  *
12147  * Fork - LGPL
12148  * <script type="text/javascript">
12149  */
12150
12151 /**
12152  * @class Roo.data.ScriptTagProxy
12153  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12154  * other than the originating domain of the running page.<br><br>
12155  * <p>
12156  * <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
12157  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12158  * <p>
12159  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12160  * source code that is used as the source inside a &lt;script> tag.<br><br>
12161  * <p>
12162  * In order for the browser to process the returned data, the server must wrap the data object
12163  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12164  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12165  * depending on whether the callback name was passed:
12166  * <p>
12167  * <pre><code>
12168 boolean scriptTag = false;
12169 String cb = request.getParameter("callback");
12170 if (cb != null) {
12171     scriptTag = true;
12172     response.setContentType("text/javascript");
12173 } else {
12174     response.setContentType("application/x-json");
12175 }
12176 Writer out = response.getWriter();
12177 if (scriptTag) {
12178     out.write(cb + "(");
12179 }
12180 out.print(dataBlock.toJsonString());
12181 if (scriptTag) {
12182     out.write(");");
12183 }
12184 </pre></code>
12185  *
12186  * @constructor
12187  * @param {Object} config A configuration object.
12188  */
12189 Roo.data.ScriptTagProxy = function(config){
12190     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12191     Roo.apply(this, config);
12192     this.head = document.getElementsByTagName("head")[0];
12193 };
12194
12195 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12196
12197 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12198     /**
12199      * @cfg {String} url The URL from which to request the data object.
12200      */
12201     /**
12202      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12203      */
12204     timeout : 30000,
12205     /**
12206      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12207      * the server the name of the callback function set up by the load call to process the returned data object.
12208      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12209      * javascript output which calls this named function passing the data object as its only parameter.
12210      */
12211     callbackParam : "callback",
12212     /**
12213      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12214      * name to the request.
12215      */
12216     nocache : true,
12217
12218     /**
12219      * Load data from the configured URL, read the data object into
12220      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12221      * process that block using the passed callback.
12222      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12223      * for the request to the remote server.
12224      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12225      * object into a block of Roo.data.Records.
12226      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12227      * The function must be passed <ul>
12228      * <li>The Record block object</li>
12229      * <li>The "arg" argument from the load function</li>
12230      * <li>A boolean success indicator</li>
12231      * </ul>
12232      * @param {Object} scope The scope in which to call the callback
12233      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12234      */
12235     load : function(params, reader, callback, scope, arg){
12236         if(this.fireEvent("beforeload", this, params) !== false){
12237
12238             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12239
12240             var url = this.url;
12241             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12242             if(this.nocache){
12243                 url += "&_dc=" + (new Date().getTime());
12244             }
12245             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12246             var trans = {
12247                 id : transId,
12248                 cb : "stcCallback"+transId,
12249                 scriptId : "stcScript"+transId,
12250                 params : params,
12251                 arg : arg,
12252                 url : url,
12253                 callback : callback,
12254                 scope : scope,
12255                 reader : reader
12256             };
12257             var conn = this;
12258
12259             window[trans.cb] = function(o){
12260                 conn.handleResponse(o, trans);
12261             };
12262
12263             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12264
12265             if(this.autoAbort !== false){
12266                 this.abort();
12267             }
12268
12269             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12270
12271             var script = document.createElement("script");
12272             script.setAttribute("src", url);
12273             script.setAttribute("type", "text/javascript");
12274             script.setAttribute("id", trans.scriptId);
12275             this.head.appendChild(script);
12276
12277             this.trans = trans;
12278         }else{
12279             callback.call(scope||this, null, arg, false);
12280         }
12281     },
12282
12283     // private
12284     isLoading : function(){
12285         return this.trans ? true : false;
12286     },
12287
12288     /**
12289      * Abort the current server request.
12290      */
12291     abort : function(){
12292         if(this.isLoading()){
12293             this.destroyTrans(this.trans);
12294         }
12295     },
12296
12297     // private
12298     destroyTrans : function(trans, isLoaded){
12299         this.head.removeChild(document.getElementById(trans.scriptId));
12300         clearTimeout(trans.timeoutId);
12301         if(isLoaded){
12302             window[trans.cb] = undefined;
12303             try{
12304                 delete window[trans.cb];
12305             }catch(e){}
12306         }else{
12307             // if hasn't been loaded, wait for load to remove it to prevent script error
12308             window[trans.cb] = function(){
12309                 window[trans.cb] = undefined;
12310                 try{
12311                     delete window[trans.cb];
12312                 }catch(e){}
12313             };
12314         }
12315     },
12316
12317     // private
12318     handleResponse : function(o, trans){
12319         this.trans = false;
12320         this.destroyTrans(trans, true);
12321         var result;
12322         try {
12323             result = trans.reader.readRecords(o);
12324         }catch(e){
12325             this.fireEvent("loadexception", this, o, trans.arg, e);
12326             trans.callback.call(trans.scope||window, null, trans.arg, false);
12327             return;
12328         }
12329         this.fireEvent("load", this, o, trans.arg);
12330         trans.callback.call(trans.scope||window, result, trans.arg, true);
12331     },
12332
12333     // private
12334     handleFailure : function(trans){
12335         this.trans = false;
12336         this.destroyTrans(trans, false);
12337         this.fireEvent("loadexception", this, null, trans.arg);
12338         trans.callback.call(trans.scope||window, null, trans.arg, false);
12339     }
12340 });/*
12341  * Based on:
12342  * Ext JS Library 1.1.1
12343  * Copyright(c) 2006-2007, Ext JS, LLC.
12344  *
12345  * Originally Released Under LGPL - original licence link has changed is not relivant.
12346  *
12347  * Fork - LGPL
12348  * <script type="text/javascript">
12349  */
12350
12351 /**
12352  * @class Roo.data.JsonReader
12353  * @extends Roo.data.DataReader
12354  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12355  * based on mappings in a provided Roo.data.Record constructor.
12356  * 
12357  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12358  * in the reply previously. 
12359  * 
12360  * <p>
12361  * Example code:
12362  * <pre><code>
12363 var RecordDef = Roo.data.Record.create([
12364     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12365     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12366 ]);
12367 var myReader = new Roo.data.JsonReader({
12368     totalProperty: "results",    // The property which contains the total dataset size (optional)
12369     root: "rows",                // The property which contains an Array of row objects
12370     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12371 }, RecordDef);
12372 </code></pre>
12373  * <p>
12374  * This would consume a JSON file like this:
12375  * <pre><code>
12376 { 'results': 2, 'rows': [
12377     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12378     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12379 }
12380 </code></pre>
12381  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12382  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12383  * paged from the remote server.
12384  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12385  * @cfg {String} root name of the property which contains the Array of row objects.
12386  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12387  * @cfg {Array} fields Array of field definition objects
12388  * @constructor
12389  * Create a new JsonReader
12390  * @param {Object} meta Metadata configuration options
12391  * @param {Object} recordType Either an Array of field definition objects,
12392  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12393  */
12394 Roo.data.JsonReader = function(meta, recordType){
12395     
12396     meta = meta || {};
12397     // set some defaults:
12398     Roo.applyIf(meta, {
12399         totalProperty: 'total',
12400         successProperty : 'success',
12401         root : 'data',
12402         id : 'id'
12403     });
12404     
12405     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12406 };
12407 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12408     
12409     /**
12410      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12411      * Used by Store query builder to append _requestMeta to params.
12412      * 
12413      */
12414     metaFromRemote : false,
12415     /**
12416      * This method is only used by a DataProxy which has retrieved data from a remote server.
12417      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12418      * @return {Object} data A data block which is used by an Roo.data.Store object as
12419      * a cache of Roo.data.Records.
12420      */
12421     read : function(response){
12422         var json = response.responseText;
12423        
12424         var o = /* eval:var:o */ eval("("+json+")");
12425         if(!o) {
12426             throw {message: "JsonReader.read: Json object not found"};
12427         }
12428         
12429         if(o.metaData){
12430             
12431             delete this.ef;
12432             this.metaFromRemote = true;
12433             this.meta = o.metaData;
12434             this.recordType = Roo.data.Record.create(o.metaData.fields);
12435             this.onMetaChange(this.meta, this.recordType, o);
12436         }
12437         return this.readRecords(o);
12438     },
12439
12440     // private function a store will implement
12441     onMetaChange : function(meta, recordType, o){
12442
12443     },
12444
12445     /**
12446          * @ignore
12447          */
12448     simpleAccess: function(obj, subsc) {
12449         return obj[subsc];
12450     },
12451
12452         /**
12453          * @ignore
12454          */
12455     getJsonAccessor: function(){
12456         var re = /[\[\.]/;
12457         return function(expr) {
12458             try {
12459                 return(re.test(expr))
12460                     ? new Function("obj", "return obj." + expr)
12461                     : function(obj){
12462                         return obj[expr];
12463                     };
12464             } catch(e){}
12465             return Roo.emptyFn;
12466         };
12467     }(),
12468
12469     /**
12470      * Create a data block containing Roo.data.Records from an XML document.
12471      * @param {Object} o An object which contains an Array of row objects in the property specified
12472      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12473      * which contains the total size of the dataset.
12474      * @return {Object} data A data block which is used by an Roo.data.Store object as
12475      * a cache of Roo.data.Records.
12476      */
12477     readRecords : function(o){
12478         /**
12479          * After any data loads, the raw JSON data is available for further custom processing.
12480          * @type Object
12481          */
12482         this.o = o;
12483         var s = this.meta, Record = this.recordType,
12484             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12485
12486 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12487         if (!this.ef) {
12488             if(s.totalProperty) {
12489                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12490                 }
12491                 if(s.successProperty) {
12492                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12493                 }
12494                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12495                 if (s.id) {
12496                         var g = this.getJsonAccessor(s.id);
12497                         this.getId = function(rec) {
12498                                 var r = g(rec);  
12499                                 return (r === undefined || r === "") ? null : r;
12500                         };
12501                 } else {
12502                         this.getId = function(){return null;};
12503                 }
12504             this.ef = [];
12505             for(var jj = 0; jj < fl; jj++){
12506                 f = fi[jj];
12507                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12508                 this.ef[jj] = this.getJsonAccessor(map);
12509             }
12510         }
12511
12512         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12513         if(s.totalProperty){
12514             var vt = parseInt(this.getTotal(o), 10);
12515             if(!isNaN(vt)){
12516                 totalRecords = vt;
12517             }
12518         }
12519         if(s.successProperty){
12520             var vs = this.getSuccess(o);
12521             if(vs === false || vs === 'false'){
12522                 success = false;
12523             }
12524         }
12525         var records = [];
12526         for(var i = 0; i < c; i++){
12527                 var n = root[i];
12528             var values = {};
12529             var id = this.getId(n);
12530             for(var j = 0; j < fl; j++){
12531                 f = fi[j];
12532             var v = this.ef[j](n);
12533             if (!f.convert) {
12534                 Roo.log('missing convert for ' + f.name);
12535                 Roo.log(f);
12536                 continue;
12537             }
12538             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12539             }
12540             var record = new Record(values, id);
12541             record.json = n;
12542             records[i] = record;
12543         }
12544         return {
12545             raw : o,
12546             success : success,
12547             records : records,
12548             totalRecords : totalRecords
12549         };
12550     }
12551 });/*
12552  * Based on:
12553  * Ext JS Library 1.1.1
12554  * Copyright(c) 2006-2007, Ext JS, LLC.
12555  *
12556  * Originally Released Under LGPL - original licence link has changed is not relivant.
12557  *
12558  * Fork - LGPL
12559  * <script type="text/javascript">
12560  */
12561
12562 /**
12563  * @class Roo.data.ArrayReader
12564  * @extends Roo.data.DataReader
12565  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12566  * Each element of that Array represents a row of data fields. The
12567  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12568  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12569  * <p>
12570  * Example code:.
12571  * <pre><code>
12572 var RecordDef = Roo.data.Record.create([
12573     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12574     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12575 ]);
12576 var myReader = new Roo.data.ArrayReader({
12577     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12578 }, RecordDef);
12579 </code></pre>
12580  * <p>
12581  * This would consume an Array like this:
12582  * <pre><code>
12583 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12584   </code></pre>
12585  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12586  * @constructor
12587  * Create a new JsonReader
12588  * @param {Object} meta Metadata configuration options.
12589  * @param {Object} recordType Either an Array of field definition objects
12590  * as specified to {@link Roo.data.Record#create},
12591  * or an {@link Roo.data.Record} object
12592  * created using {@link Roo.data.Record#create}.
12593  */
12594 Roo.data.ArrayReader = function(meta, recordType){
12595     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12596 };
12597
12598 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12599     /**
12600      * Create a data block containing Roo.data.Records from an XML document.
12601      * @param {Object} o An Array of row objects which represents the dataset.
12602      * @return {Object} data A data block which is used by an Roo.data.Store object as
12603      * a cache of Roo.data.Records.
12604      */
12605     readRecords : function(o){
12606         var sid = this.meta ? this.meta.id : null;
12607         var recordType = this.recordType, fields = recordType.prototype.fields;
12608         var records = [];
12609         var root = o;
12610             for(var i = 0; i < root.length; i++){
12611                     var n = root[i];
12612                 var values = {};
12613                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12614                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12615                 var f = fields.items[j];
12616                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12617                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12618                 v = f.convert(v);
12619                 values[f.name] = v;
12620             }
12621                 var record = new recordType(values, id);
12622                 record.json = n;
12623                 records[records.length] = record;
12624             }
12625             return {
12626                 records : records,
12627                 totalRecords : records.length
12628             };
12629     }
12630 });/*
12631  * - LGPL
12632  * * 
12633  */
12634
12635 /**
12636  * @class Roo.bootstrap.ComboBox
12637  * @extends Roo.bootstrap.TriggerField
12638  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12639  * @cfg {Boolean} append (true|false) default false
12640  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12641  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12642  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12643  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12644  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12645  * @cfg {Boolean} animate default true
12646  * @cfg {Boolean} emptyResultText only for touch device
12647  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12648  * @cfg {String} emptyTitle default ''
12649  * @constructor
12650  * Create a new ComboBox.
12651  * @param {Object} config Configuration options
12652  */
12653 Roo.bootstrap.ComboBox = function(config){
12654     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12655     this.addEvents({
12656         /**
12657          * @event expand
12658          * Fires when the dropdown list is expanded
12659         * @param {Roo.bootstrap.ComboBox} combo This combo box
12660         */
12661         'expand' : true,
12662         /**
12663          * @event collapse
12664          * Fires when the dropdown list is collapsed
12665         * @param {Roo.bootstrap.ComboBox} combo This combo box
12666         */
12667         'collapse' : true,
12668         /**
12669          * @event beforeselect
12670          * Fires before a list item is selected. Return false to cancel the selection.
12671         * @param {Roo.bootstrap.ComboBox} combo This combo box
12672         * @param {Roo.data.Record} record The data record returned from the underlying store
12673         * @param {Number} index The index of the selected item in the dropdown list
12674         */
12675         'beforeselect' : true,
12676         /**
12677          * @event select
12678          * Fires when a list item is selected
12679         * @param {Roo.bootstrap.ComboBox} combo This combo box
12680         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12681         * @param {Number} index The index of the selected item in the dropdown list
12682         */
12683         'select' : true,
12684         /**
12685          * @event beforequery
12686          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12687          * The event object passed has these properties:
12688         * @param {Roo.bootstrap.ComboBox} combo This combo box
12689         * @param {String} query The query
12690         * @param {Boolean} forceAll true to force "all" query
12691         * @param {Boolean} cancel true to cancel the query
12692         * @param {Object} e The query event object
12693         */
12694         'beforequery': true,
12695          /**
12696          * @event add
12697          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12698         * @param {Roo.bootstrap.ComboBox} combo This combo box
12699         */
12700         'add' : true,
12701         /**
12702          * @event edit
12703          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12704         * @param {Roo.bootstrap.ComboBox} combo This combo box
12705         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12706         */
12707         'edit' : true,
12708         /**
12709          * @event remove
12710          * Fires when the remove value from the combobox array
12711         * @param {Roo.bootstrap.ComboBox} combo This combo box
12712         */
12713         'remove' : true,
12714         /**
12715          * @event afterremove
12716          * Fires when the remove value from the combobox array
12717         * @param {Roo.bootstrap.ComboBox} combo This combo box
12718         */
12719         'afterremove' : true,
12720         /**
12721          * @event specialfilter
12722          * Fires when specialfilter
12723             * @param {Roo.bootstrap.ComboBox} combo This combo box
12724             */
12725         'specialfilter' : true,
12726         /**
12727          * @event tick
12728          * Fires when tick the element
12729             * @param {Roo.bootstrap.ComboBox} combo This combo box
12730             */
12731         'tick' : true,
12732         /**
12733          * @event touchviewdisplay
12734          * Fires when touch view require special display (default is using displayField)
12735             * @param {Roo.bootstrap.ComboBox} combo This combo box
12736             * @param {Object} cfg set html .
12737             */
12738         'touchviewdisplay' : true
12739         
12740     });
12741     
12742     this.item = [];
12743     this.tickItems = [];
12744     
12745     this.selectedIndex = -1;
12746     if(this.mode == 'local'){
12747         if(config.queryDelay === undefined){
12748             this.queryDelay = 10;
12749         }
12750         if(config.minChars === undefined){
12751             this.minChars = 0;
12752         }
12753     }
12754 };
12755
12756 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12757      
12758     /**
12759      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12760      * rendering into an Roo.Editor, defaults to false)
12761      */
12762     /**
12763      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12764      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12765      */
12766     /**
12767      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12768      */
12769     /**
12770      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12771      * the dropdown list (defaults to undefined, with no header element)
12772      */
12773
12774      /**
12775      * @cfg {String/Roo.Template} tpl The template to use to render the output
12776      */
12777      
12778      /**
12779      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12780      */
12781     listWidth: undefined,
12782     /**
12783      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12784      * mode = 'remote' or 'text' if mode = 'local')
12785      */
12786     displayField: undefined,
12787     
12788     /**
12789      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12790      * mode = 'remote' or 'value' if mode = 'local'). 
12791      * Note: use of a valueField requires the user make a selection
12792      * in order for a value to be mapped.
12793      */
12794     valueField: undefined,
12795     /**
12796      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12797      */
12798     modalTitle : '',
12799     
12800     /**
12801      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12802      * field's data value (defaults to the underlying DOM element's name)
12803      */
12804     hiddenName: undefined,
12805     /**
12806      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12807      */
12808     listClass: '',
12809     /**
12810      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12811      */
12812     selectedClass: 'active',
12813     
12814     /**
12815      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12816      */
12817     shadow:'sides',
12818     /**
12819      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12820      * anchor positions (defaults to 'tl-bl')
12821      */
12822     listAlign: 'tl-bl?',
12823     /**
12824      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12825      */
12826     maxHeight: 300,
12827     /**
12828      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12829      * query specified by the allQuery config option (defaults to 'query')
12830      */
12831     triggerAction: 'query',
12832     /**
12833      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12834      * (defaults to 4, does not apply if editable = false)
12835      */
12836     minChars : 4,
12837     /**
12838      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12839      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12840      */
12841     typeAhead: false,
12842     /**
12843      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12844      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12845      */
12846     queryDelay: 500,
12847     /**
12848      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12849      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12850      */
12851     pageSize: 0,
12852     /**
12853      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12854      * when editable = true (defaults to false)
12855      */
12856     selectOnFocus:false,
12857     /**
12858      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12859      */
12860     queryParam: 'query',
12861     /**
12862      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12863      * when mode = 'remote' (defaults to 'Loading...')
12864      */
12865     loadingText: 'Loading...',
12866     /**
12867      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12868      */
12869     resizable: false,
12870     /**
12871      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12872      */
12873     handleHeight : 8,
12874     /**
12875      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12876      * traditional select (defaults to true)
12877      */
12878     editable: true,
12879     /**
12880      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12881      */
12882     allQuery: '',
12883     /**
12884      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12885      */
12886     mode: 'remote',
12887     /**
12888      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12889      * listWidth has a higher value)
12890      */
12891     minListWidth : 70,
12892     /**
12893      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12894      * allow the user to set arbitrary text into the field (defaults to false)
12895      */
12896     forceSelection:false,
12897     /**
12898      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12899      * if typeAhead = true (defaults to 250)
12900      */
12901     typeAheadDelay : 250,
12902     /**
12903      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12904      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12905      */
12906     valueNotFoundText : undefined,
12907     /**
12908      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12909      */
12910     blockFocus : false,
12911     
12912     /**
12913      * @cfg {Boolean} disableClear Disable showing of clear button.
12914      */
12915     disableClear : false,
12916     /**
12917      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12918      */
12919     alwaysQuery : false,
12920     
12921     /**
12922      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12923      */
12924     multiple : false,
12925     
12926     /**
12927      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12928      */
12929     invalidClass : "has-warning",
12930     
12931     /**
12932      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12933      */
12934     validClass : "has-success",
12935     
12936     /**
12937      * @cfg {Boolean} specialFilter (true|false) special filter default false
12938      */
12939     specialFilter : false,
12940     
12941     /**
12942      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12943      */
12944     mobileTouchView : true,
12945     
12946     /**
12947      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12948      */
12949     useNativeIOS : false,
12950     
12951     ios_options : false,
12952     
12953     //private
12954     addicon : false,
12955     editicon: false,
12956     
12957     page: 0,
12958     hasQuery: false,
12959     append: false,
12960     loadNext: false,
12961     autoFocus : true,
12962     tickable : false,
12963     btnPosition : 'right',
12964     triggerList : true,
12965     showToggleBtn : true,
12966     animate : true,
12967     emptyResultText: 'Empty',
12968     triggerText : 'Select',
12969     emptyTitle : '',
12970     
12971     // element that contains real text value.. (when hidden is used..)
12972     
12973     getAutoCreate : function()
12974     {   
12975         var cfg = false;
12976         //render
12977         /*
12978          * Render classic select for iso
12979          */
12980         
12981         if(Roo.isIOS && this.useNativeIOS){
12982             cfg = this.getAutoCreateNativeIOS();
12983             return cfg;
12984         }
12985         
12986         /*
12987          * Touch Devices
12988          */
12989         
12990         if(Roo.isTouch && this.mobileTouchView){
12991             cfg = this.getAutoCreateTouchView();
12992             return cfg;;
12993         }
12994         
12995         /*
12996          *  Normal ComboBox
12997          */
12998         if(!this.tickable){
12999             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13000             return cfg;
13001         }
13002         
13003         /*
13004          *  ComboBox with tickable selections
13005          */
13006              
13007         var align = this.labelAlign || this.parentLabelAlign();
13008         
13009         cfg = {
13010             cls : 'form-group roo-combobox-tickable' //input-group
13011         };
13012         
13013         var btn_text_select = '';
13014         var btn_text_done = '';
13015         var btn_text_cancel = '';
13016         
13017         if (this.btn_text_show) {
13018             btn_text_select = 'Select';
13019             btn_text_done = 'Done';
13020             btn_text_cancel = 'Cancel'; 
13021         }
13022         
13023         var buttons = {
13024             tag : 'div',
13025             cls : 'tickable-buttons',
13026             cn : [
13027                 {
13028                     tag : 'button',
13029                     type : 'button',
13030                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13031                     //html : this.triggerText
13032                     html: btn_text_select
13033                 },
13034                 {
13035                     tag : 'button',
13036                     type : 'button',
13037                     name : 'ok',
13038                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13039                     //html : 'Done'
13040                     html: btn_text_done
13041                 },
13042                 {
13043                     tag : 'button',
13044                     type : 'button',
13045                     name : 'cancel',
13046                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13047                     //html : 'Cancel'
13048                     html: btn_text_cancel
13049                 }
13050             ]
13051         };
13052         
13053         if(this.editable){
13054             buttons.cn.unshift({
13055                 tag: 'input',
13056                 cls: 'roo-select2-search-field-input'
13057             });
13058         }
13059         
13060         var _this = this;
13061         
13062         Roo.each(buttons.cn, function(c){
13063             if (_this.size) {
13064                 c.cls += ' btn-' + _this.size;
13065             }
13066
13067             if (_this.disabled) {
13068                 c.disabled = true;
13069             }
13070         });
13071         
13072         var box = {
13073             tag: 'div',
13074             cn: [
13075                 {
13076                     tag: 'input',
13077                     type : 'hidden',
13078                     cls: 'form-hidden-field'
13079                 },
13080                 {
13081                     tag: 'ul',
13082                     cls: 'roo-select2-choices',
13083                     cn:[
13084                         {
13085                             tag: 'li',
13086                             cls: 'roo-select2-search-field',
13087                             cn: [
13088                                 buttons
13089                             ]
13090                         }
13091                     ]
13092                 }
13093             ]
13094         };
13095         
13096         var combobox = {
13097             cls: 'roo-select2-container input-group roo-select2-container-multi',
13098             cn: [
13099                 box
13100 //                {
13101 //                    tag: 'ul',
13102 //                    cls: 'typeahead typeahead-long dropdown-menu',
13103 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13104 //                }
13105             ]
13106         };
13107         
13108         if(this.hasFeedback && !this.allowBlank){
13109             
13110             var feedback = {
13111                 tag: 'span',
13112                 cls: 'glyphicon form-control-feedback'
13113             };
13114
13115             combobox.cn.push(feedback);
13116         }
13117         
13118         
13119         if (align ==='left' && this.fieldLabel.length) {
13120             
13121             cfg.cls += ' roo-form-group-label-left';
13122             
13123             cfg.cn = [
13124                 {
13125                     tag : 'i',
13126                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13127                     tooltip : 'This field is required'
13128                 },
13129                 {
13130                     tag: 'label',
13131                     'for' :  id,
13132                     cls : 'control-label',
13133                     html : this.fieldLabel
13134
13135                 },
13136                 {
13137                     cls : "", 
13138                     cn: [
13139                         combobox
13140                     ]
13141                 }
13142
13143             ];
13144             
13145             var labelCfg = cfg.cn[1];
13146             var contentCfg = cfg.cn[2];
13147             
13148
13149             if(this.indicatorpos == 'right'){
13150                 
13151                 cfg.cn = [
13152                     {
13153                         tag: 'label',
13154                         'for' :  id,
13155                         cls : 'control-label',
13156                         cn : [
13157                             {
13158                                 tag : 'span',
13159                                 html : this.fieldLabel
13160                             },
13161                             {
13162                                 tag : 'i',
13163                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13164                                 tooltip : 'This field is required'
13165                             }
13166                         ]
13167                     },
13168                     {
13169                         cls : "",
13170                         cn: [
13171                             combobox
13172                         ]
13173                     }
13174
13175                 ];
13176                 
13177                 
13178                 
13179                 labelCfg = cfg.cn[0];
13180                 contentCfg = cfg.cn[1];
13181             
13182             }
13183             
13184             if(this.labelWidth > 12){
13185                 labelCfg.style = "width: " + this.labelWidth + 'px';
13186             }
13187             
13188             if(this.labelWidth < 13 && this.labelmd == 0){
13189                 this.labelmd = this.labelWidth;
13190             }
13191             
13192             if(this.labellg > 0){
13193                 labelCfg.cls += ' col-lg-' + this.labellg;
13194                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13195             }
13196             
13197             if(this.labelmd > 0){
13198                 labelCfg.cls += ' col-md-' + this.labelmd;
13199                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13200             }
13201             
13202             if(this.labelsm > 0){
13203                 labelCfg.cls += ' col-sm-' + this.labelsm;
13204                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13205             }
13206             
13207             if(this.labelxs > 0){
13208                 labelCfg.cls += ' col-xs-' + this.labelxs;
13209                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13210             }
13211                 
13212                 
13213         } else if ( this.fieldLabel.length) {
13214 //                Roo.log(" label");
13215                  cfg.cn = [
13216                     {
13217                         tag : 'i',
13218                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13219                         tooltip : 'This field is required'
13220                     },
13221                     {
13222                         tag: 'label',
13223                         //cls : 'input-group-addon',
13224                         html : this.fieldLabel
13225                     },
13226                     combobox
13227                 ];
13228                 
13229                 if(this.indicatorpos == 'right'){
13230                     cfg.cn = [
13231                         {
13232                             tag: 'label',
13233                             //cls : 'input-group-addon',
13234                             html : this.fieldLabel
13235                         },
13236                         {
13237                             tag : 'i',
13238                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13239                             tooltip : 'This field is required'
13240                         },
13241                         combobox
13242                     ];
13243                     
13244                 }
13245
13246         } else {
13247             
13248 //                Roo.log(" no label && no align");
13249                 cfg = combobox
13250                      
13251                 
13252         }
13253          
13254         var settings=this;
13255         ['xs','sm','md','lg'].map(function(size){
13256             if (settings[size]) {
13257                 cfg.cls += ' col-' + size + '-' + settings[size];
13258             }
13259         });
13260         
13261         return cfg;
13262         
13263     },
13264     
13265     _initEventsCalled : false,
13266     
13267     // private
13268     initEvents: function()
13269     {   
13270         if (this._initEventsCalled) { // as we call render... prevent looping...
13271             return;
13272         }
13273         this._initEventsCalled = true;
13274         
13275         if (!this.store) {
13276             throw "can not find store for combo";
13277         }
13278         
13279         this.indicator = this.indicatorEl();
13280         
13281         this.store = Roo.factory(this.store, Roo.data);
13282         this.store.parent = this;
13283         
13284         // if we are building from html. then this element is so complex, that we can not really
13285         // use the rendered HTML.
13286         // so we have to trash and replace the previous code.
13287         if (Roo.XComponent.build_from_html) {
13288             // remove this element....
13289             var e = this.el.dom, k=0;
13290             while (e ) { e = e.previousSibling;  ++k;}
13291
13292             this.el.remove();
13293             
13294             this.el=false;
13295             this.rendered = false;
13296             
13297             this.render(this.parent().getChildContainer(true), k);
13298         }
13299         
13300         if(Roo.isIOS && this.useNativeIOS){
13301             this.initIOSView();
13302             return;
13303         }
13304         
13305         /*
13306          * Touch Devices
13307          */
13308         
13309         if(Roo.isTouch && this.mobileTouchView){
13310             this.initTouchView();
13311             return;
13312         }
13313         
13314         if(this.tickable){
13315             this.initTickableEvents();
13316             return;
13317         }
13318         
13319         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13320         
13321         if(this.hiddenName){
13322             
13323             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13324             
13325             this.hiddenField.dom.value =
13326                 this.hiddenValue !== undefined ? this.hiddenValue :
13327                 this.value !== undefined ? this.value : '';
13328
13329             // prevent input submission
13330             this.el.dom.removeAttribute('name');
13331             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13332              
13333              
13334         }
13335         //if(Roo.isGecko){
13336         //    this.el.dom.setAttribute('autocomplete', 'off');
13337         //}
13338         
13339         var cls = 'x-combo-list';
13340         
13341         //this.list = new Roo.Layer({
13342         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13343         //});
13344         
13345         var _this = this;
13346         
13347         (function(){
13348             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13349             _this.list.setWidth(lw);
13350         }).defer(100);
13351         
13352         this.list.on('mouseover', this.onViewOver, this);
13353         this.list.on('mousemove', this.onViewMove, this);
13354         this.list.on('scroll', this.onViewScroll, this);
13355         
13356         /*
13357         this.list.swallowEvent('mousewheel');
13358         this.assetHeight = 0;
13359
13360         if(this.title){
13361             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13362             this.assetHeight += this.header.getHeight();
13363         }
13364
13365         this.innerList = this.list.createChild({cls:cls+'-inner'});
13366         this.innerList.on('mouseover', this.onViewOver, this);
13367         this.innerList.on('mousemove', this.onViewMove, this);
13368         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13369         
13370         if(this.allowBlank && !this.pageSize && !this.disableClear){
13371             this.footer = this.list.createChild({cls:cls+'-ft'});
13372             this.pageTb = new Roo.Toolbar(this.footer);
13373            
13374         }
13375         if(this.pageSize){
13376             this.footer = this.list.createChild({cls:cls+'-ft'});
13377             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13378                     {pageSize: this.pageSize});
13379             
13380         }
13381         
13382         if (this.pageTb && this.allowBlank && !this.disableClear) {
13383             var _this = this;
13384             this.pageTb.add(new Roo.Toolbar.Fill(), {
13385                 cls: 'x-btn-icon x-btn-clear',
13386                 text: '&#160;',
13387                 handler: function()
13388                 {
13389                     _this.collapse();
13390                     _this.clearValue();
13391                     _this.onSelect(false, -1);
13392                 }
13393             });
13394         }
13395         if (this.footer) {
13396             this.assetHeight += this.footer.getHeight();
13397         }
13398         */
13399             
13400         if(!this.tpl){
13401             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13402         }
13403
13404         this.view = new Roo.View(this.list, this.tpl, {
13405             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13406         });
13407         //this.view.wrapEl.setDisplayed(false);
13408         this.view.on('click', this.onViewClick, this);
13409         
13410         
13411         this.store.on('beforeload', this.onBeforeLoad, this);
13412         this.store.on('load', this.onLoad, this);
13413         this.store.on('loadexception', this.onLoadException, this);
13414         /*
13415         if(this.resizable){
13416             this.resizer = new Roo.Resizable(this.list,  {
13417                pinned:true, handles:'se'
13418             });
13419             this.resizer.on('resize', function(r, w, h){
13420                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13421                 this.listWidth = w;
13422                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13423                 this.restrictHeight();
13424             }, this);
13425             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13426         }
13427         */
13428         if(!this.editable){
13429             this.editable = true;
13430             this.setEditable(false);
13431         }
13432         
13433         /*
13434         
13435         if (typeof(this.events.add.listeners) != 'undefined') {
13436             
13437             this.addicon = this.wrap.createChild(
13438                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13439        
13440             this.addicon.on('click', function(e) {
13441                 this.fireEvent('add', this);
13442             }, this);
13443         }
13444         if (typeof(this.events.edit.listeners) != 'undefined') {
13445             
13446             this.editicon = this.wrap.createChild(
13447                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13448             if (this.addicon) {
13449                 this.editicon.setStyle('margin-left', '40px');
13450             }
13451             this.editicon.on('click', function(e) {
13452                 
13453                 // we fire even  if inothing is selected..
13454                 this.fireEvent('edit', this, this.lastData );
13455                 
13456             }, this);
13457         }
13458         */
13459         
13460         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13461             "up" : function(e){
13462                 this.inKeyMode = true;
13463                 this.selectPrev();
13464             },
13465
13466             "down" : function(e){
13467                 if(!this.isExpanded()){
13468                     this.onTriggerClick();
13469                 }else{
13470                     this.inKeyMode = true;
13471                     this.selectNext();
13472                 }
13473             },
13474
13475             "enter" : function(e){
13476 //                this.onViewClick();
13477                 //return true;
13478                 this.collapse();
13479                 
13480                 if(this.fireEvent("specialkey", this, e)){
13481                     this.onViewClick(false);
13482                 }
13483                 
13484                 return true;
13485             },
13486
13487             "esc" : function(e){
13488                 this.collapse();
13489             },
13490
13491             "tab" : function(e){
13492                 this.collapse();
13493                 
13494                 if(this.fireEvent("specialkey", this, e)){
13495                     this.onViewClick(false);
13496                 }
13497                 
13498                 return true;
13499             },
13500
13501             scope : this,
13502
13503             doRelay : function(foo, bar, hname){
13504                 if(hname == 'down' || this.scope.isExpanded()){
13505                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13506                 }
13507                 return true;
13508             },
13509
13510             forceKeyDown: true
13511         });
13512         
13513         
13514         this.queryDelay = Math.max(this.queryDelay || 10,
13515                 this.mode == 'local' ? 10 : 250);
13516         
13517         
13518         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13519         
13520         if(this.typeAhead){
13521             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13522         }
13523         if(this.editable !== false){
13524             this.inputEl().on("keyup", this.onKeyUp, this);
13525         }
13526         if(this.forceSelection){
13527             this.inputEl().on('blur', this.doForce, this);
13528         }
13529         
13530         if(this.multiple){
13531             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13532             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13533         }
13534     },
13535     
13536     initTickableEvents: function()
13537     {   
13538         this.createList();
13539         
13540         if(this.hiddenName){
13541             
13542             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13543             
13544             this.hiddenField.dom.value =
13545                 this.hiddenValue !== undefined ? this.hiddenValue :
13546                 this.value !== undefined ? this.value : '';
13547
13548             // prevent input submission
13549             this.el.dom.removeAttribute('name');
13550             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13551              
13552              
13553         }
13554         
13555 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13556         
13557         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13558         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13559         if(this.triggerList){
13560             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13561         }
13562          
13563         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13564         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13565         
13566         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13567         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13568         
13569         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13570         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13571         
13572         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13573         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13574         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13575         
13576         this.okBtn.hide();
13577         this.cancelBtn.hide();
13578         
13579         var _this = this;
13580         
13581         (function(){
13582             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13583             _this.list.setWidth(lw);
13584         }).defer(100);
13585         
13586         this.list.on('mouseover', this.onViewOver, this);
13587         this.list.on('mousemove', this.onViewMove, this);
13588         
13589         this.list.on('scroll', this.onViewScroll, this);
13590         
13591         if(!this.tpl){
13592             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13593         }
13594
13595         this.view = new Roo.View(this.list, this.tpl, {
13596             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13597         });
13598         
13599         //this.view.wrapEl.setDisplayed(false);
13600         this.view.on('click', this.onViewClick, this);
13601         
13602         
13603         
13604         this.store.on('beforeload', this.onBeforeLoad, this);
13605         this.store.on('load', this.onLoad, this);
13606         this.store.on('loadexception', this.onLoadException, this);
13607         
13608         if(this.editable){
13609             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13610                 "up" : function(e){
13611                     this.inKeyMode = true;
13612                     this.selectPrev();
13613                 },
13614
13615                 "down" : function(e){
13616                     this.inKeyMode = true;
13617                     this.selectNext();
13618                 },
13619
13620                 "enter" : function(e){
13621                     if(this.fireEvent("specialkey", this, e)){
13622                         this.onViewClick(false);
13623                     }
13624                     
13625                     return true;
13626                 },
13627
13628                 "esc" : function(e){
13629                     this.onTickableFooterButtonClick(e, false, false);
13630                 },
13631
13632                 "tab" : function(e){
13633                     this.fireEvent("specialkey", this, e);
13634                     
13635                     this.onTickableFooterButtonClick(e, false, false);
13636                     
13637                     return true;
13638                 },
13639
13640                 scope : this,
13641
13642                 doRelay : function(e, fn, key){
13643                     if(this.scope.isExpanded()){
13644                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13645                     }
13646                     return true;
13647                 },
13648
13649                 forceKeyDown: true
13650             });
13651         }
13652         
13653         this.queryDelay = Math.max(this.queryDelay || 10,
13654                 this.mode == 'local' ? 10 : 250);
13655         
13656         
13657         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13658         
13659         if(this.typeAhead){
13660             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13661         }
13662         
13663         if(this.editable !== false){
13664             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13665         }
13666         
13667         this.indicator = this.indicatorEl();
13668         
13669         if(this.indicator){
13670             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13671             this.indicator.hide();
13672         }
13673         
13674     },
13675
13676     onDestroy : function(){
13677         if(this.view){
13678             this.view.setStore(null);
13679             this.view.el.removeAllListeners();
13680             this.view.el.remove();
13681             this.view.purgeListeners();
13682         }
13683         if(this.list){
13684             this.list.dom.innerHTML  = '';
13685         }
13686         
13687         if(this.store){
13688             this.store.un('beforeload', this.onBeforeLoad, this);
13689             this.store.un('load', this.onLoad, this);
13690             this.store.un('loadexception', this.onLoadException, this);
13691         }
13692         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13693     },
13694
13695     // private
13696     fireKey : function(e){
13697         if(e.isNavKeyPress() && !this.list.isVisible()){
13698             this.fireEvent("specialkey", this, e);
13699         }
13700     },
13701
13702     // private
13703     onResize: function(w, h){
13704 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13705 //        
13706 //        if(typeof w != 'number'){
13707 //            // we do not handle it!?!?
13708 //            return;
13709 //        }
13710 //        var tw = this.trigger.getWidth();
13711 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13712 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13713 //        var x = w - tw;
13714 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13715 //            
13716 //        //this.trigger.setStyle('left', x+'px');
13717 //        
13718 //        if(this.list && this.listWidth === undefined){
13719 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13720 //            this.list.setWidth(lw);
13721 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13722 //        }
13723         
13724     
13725         
13726     },
13727
13728     /**
13729      * Allow or prevent the user from directly editing the field text.  If false is passed,
13730      * the user will only be able to select from the items defined in the dropdown list.  This method
13731      * is the runtime equivalent of setting the 'editable' config option at config time.
13732      * @param {Boolean} value True to allow the user to directly edit the field text
13733      */
13734     setEditable : function(value){
13735         if(value == this.editable){
13736             return;
13737         }
13738         this.editable = value;
13739         if(!value){
13740             this.inputEl().dom.setAttribute('readOnly', true);
13741             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13742             this.inputEl().addClass('x-combo-noedit');
13743         }else{
13744             this.inputEl().dom.setAttribute('readOnly', false);
13745             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13746             this.inputEl().removeClass('x-combo-noedit');
13747         }
13748     },
13749
13750     // private
13751     
13752     onBeforeLoad : function(combo,opts){
13753         if(!this.hasFocus){
13754             return;
13755         }
13756          if (!opts.add) {
13757             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13758          }
13759         this.restrictHeight();
13760         this.selectedIndex = -1;
13761     },
13762
13763     // private
13764     onLoad : function(){
13765         
13766         this.hasQuery = false;
13767         
13768         if(!this.hasFocus){
13769             return;
13770         }
13771         
13772         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13773             this.loading.hide();
13774         }
13775         
13776         if(this.store.getCount() > 0){
13777             
13778             this.expand();
13779             this.restrictHeight();
13780             if(this.lastQuery == this.allQuery){
13781                 if(this.editable && !this.tickable){
13782                     this.inputEl().dom.select();
13783                 }
13784                 
13785                 if(
13786                     !this.selectByValue(this.value, true) &&
13787                     this.autoFocus && 
13788                     (
13789                         !this.store.lastOptions ||
13790                         typeof(this.store.lastOptions.add) == 'undefined' || 
13791                         this.store.lastOptions.add != true
13792                     )
13793                 ){
13794                     this.select(0, true);
13795                 }
13796             }else{
13797                 if(this.autoFocus){
13798                     this.selectNext();
13799                 }
13800                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13801                     this.taTask.delay(this.typeAheadDelay);
13802                 }
13803             }
13804         }else{
13805             this.onEmptyResults();
13806         }
13807         
13808         //this.el.focus();
13809     },
13810     // private
13811     onLoadException : function()
13812     {
13813         this.hasQuery = false;
13814         
13815         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13816             this.loading.hide();
13817         }
13818         
13819         if(this.tickable && this.editable){
13820             return;
13821         }
13822         
13823         this.collapse();
13824         // only causes errors at present
13825         //Roo.log(this.store.reader.jsonData);
13826         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13827             // fixme
13828             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13829         //}
13830         
13831         
13832     },
13833     // private
13834     onTypeAhead : function(){
13835         if(this.store.getCount() > 0){
13836             var r = this.store.getAt(0);
13837             var newValue = r.data[this.displayField];
13838             var len = newValue.length;
13839             var selStart = this.getRawValue().length;
13840             
13841             if(selStart != len){
13842                 this.setRawValue(newValue);
13843                 this.selectText(selStart, newValue.length);
13844             }
13845         }
13846     },
13847
13848     // private
13849     onSelect : function(record, index){
13850         
13851         if(this.fireEvent('beforeselect', this, record, index) !== false){
13852         
13853             this.setFromData(index > -1 ? record.data : false);
13854             
13855             this.collapse();
13856             this.fireEvent('select', this, record, index);
13857         }
13858     },
13859
13860     /**
13861      * Returns the currently selected field value or empty string if no value is set.
13862      * @return {String} value The selected value
13863      */
13864     getValue : function()
13865     {
13866         if(Roo.isIOS && this.useNativeIOS){
13867             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13868         }
13869         
13870         if(this.multiple){
13871             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13872         }
13873         
13874         if(this.valueField){
13875             return typeof this.value != 'undefined' ? this.value : '';
13876         }else{
13877             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13878         }
13879     },
13880     
13881     getRawValue : function()
13882     {
13883         if(Roo.isIOS && this.useNativeIOS){
13884             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13885         }
13886         
13887         var v = this.inputEl().getValue();
13888         
13889         return v;
13890     },
13891
13892     /**
13893      * Clears any text/value currently set in the field
13894      */
13895     clearValue : function(){
13896         
13897         if(this.hiddenField){
13898             this.hiddenField.dom.value = '';
13899         }
13900         this.value = '';
13901         this.setRawValue('');
13902         this.lastSelectionText = '';
13903         this.lastData = false;
13904         
13905         var close = this.closeTriggerEl();
13906         
13907         if(close){
13908             close.hide();
13909         }
13910         
13911         this.validate();
13912         
13913     },
13914
13915     /**
13916      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13917      * will be displayed in the field.  If the value does not match the data value of an existing item,
13918      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13919      * Otherwise the field will be blank (although the value will still be set).
13920      * @param {String} value The value to match
13921      */
13922     setValue : function(v)
13923     {
13924         if(Roo.isIOS && this.useNativeIOS){
13925             this.setIOSValue(v);
13926             return;
13927         }
13928         
13929         if(this.multiple){
13930             this.syncValue();
13931             return;
13932         }
13933         
13934         var text = v;
13935         if(this.valueField){
13936             var r = this.findRecord(this.valueField, v);
13937             if(r){
13938                 text = r.data[this.displayField];
13939             }else if(this.valueNotFoundText !== undefined){
13940                 text = this.valueNotFoundText;
13941             }
13942         }
13943         this.lastSelectionText = text;
13944         if(this.hiddenField){
13945             this.hiddenField.dom.value = v;
13946         }
13947         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13948         this.value = v;
13949         
13950         var close = this.closeTriggerEl();
13951         
13952         if(close){
13953             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13954         }
13955         
13956         this.validate();
13957     },
13958     /**
13959      * @property {Object} the last set data for the element
13960      */
13961     
13962     lastData : false,
13963     /**
13964      * Sets the value of the field based on a object which is related to the record format for the store.
13965      * @param {Object} value the value to set as. or false on reset?
13966      */
13967     setFromData : function(o){
13968         
13969         if(this.multiple){
13970             this.addItem(o);
13971             return;
13972         }
13973             
13974         var dv = ''; // display value
13975         var vv = ''; // value value..
13976         this.lastData = o;
13977         if (this.displayField) {
13978             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13979         } else {
13980             // this is an error condition!!!
13981             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13982         }
13983         
13984         if(this.valueField){
13985             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13986         }
13987         
13988         var close = this.closeTriggerEl();
13989         
13990         if(close){
13991             if(dv.length || vv * 1 > 0){
13992                 close.show() ;
13993                 this.blockFocus=true;
13994             } else {
13995                 close.hide();
13996             }             
13997         }
13998         
13999         if(this.hiddenField){
14000             this.hiddenField.dom.value = vv;
14001             
14002             this.lastSelectionText = dv;
14003             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14004             this.value = vv;
14005             return;
14006         }
14007         // no hidden field.. - we store the value in 'value', but still display
14008         // display field!!!!
14009         this.lastSelectionText = dv;
14010         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14011         this.value = vv;
14012         
14013         
14014         
14015     },
14016     // private
14017     reset : function(){
14018         // overridden so that last data is reset..
14019         
14020         if(this.multiple){
14021             this.clearItem();
14022             return;
14023         }
14024         
14025         this.setValue(this.originalValue);
14026         //this.clearInvalid();
14027         this.lastData = false;
14028         if (this.view) {
14029             this.view.clearSelections();
14030         }
14031         
14032         this.validate();
14033     },
14034     // private
14035     findRecord : function(prop, value){
14036         var record;
14037         if(this.store.getCount() > 0){
14038             this.store.each(function(r){
14039                 if(r.data[prop] == value){
14040                     record = r;
14041                     return false;
14042                 }
14043                 return true;
14044             });
14045         }
14046         return record;
14047     },
14048     
14049     getName: function()
14050     {
14051         // returns hidden if it's set..
14052         if (!this.rendered) {return ''};
14053         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14054         
14055     },
14056     // private
14057     onViewMove : function(e, t){
14058         this.inKeyMode = false;
14059     },
14060
14061     // private
14062     onViewOver : function(e, t){
14063         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14064             return;
14065         }
14066         var item = this.view.findItemFromChild(t);
14067         
14068         if(item){
14069             var index = this.view.indexOf(item);
14070             this.select(index, false);
14071         }
14072     },
14073
14074     // private
14075     onViewClick : function(view, doFocus, el, e)
14076     {
14077         var index = this.view.getSelectedIndexes()[0];
14078         
14079         var r = this.store.getAt(index);
14080         
14081         if(this.tickable){
14082             
14083             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14084                 return;
14085             }
14086             
14087             var rm = false;
14088             var _this = this;
14089             
14090             Roo.each(this.tickItems, function(v,k){
14091                 
14092                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14093                     Roo.log(v);
14094                     _this.tickItems.splice(k, 1);
14095                     
14096                     if(typeof(e) == 'undefined' && view == false){
14097                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14098                     }
14099                     
14100                     rm = true;
14101                     return;
14102                 }
14103             });
14104             
14105             if(rm){
14106                 return;
14107             }
14108             
14109             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14110                 this.tickItems.push(r.data);
14111             }
14112             
14113             if(typeof(e) == 'undefined' && view == false){
14114                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14115             }
14116                     
14117             return;
14118         }
14119         
14120         if(r){
14121             this.onSelect(r, index);
14122         }
14123         if(doFocus !== false && !this.blockFocus){
14124             this.inputEl().focus();
14125         }
14126     },
14127
14128     // private
14129     restrictHeight : function(){
14130         //this.innerList.dom.style.height = '';
14131         //var inner = this.innerList.dom;
14132         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14133         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14134         //this.list.beginUpdate();
14135         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14136         this.list.alignTo(this.inputEl(), this.listAlign);
14137         this.list.alignTo(this.inputEl(), this.listAlign);
14138         //this.list.endUpdate();
14139     },
14140
14141     // private
14142     onEmptyResults : function(){
14143         
14144         if(this.tickable && this.editable){
14145             this.hasFocus = false;
14146             this.restrictHeight();
14147             return;
14148         }
14149         
14150         this.collapse();
14151     },
14152
14153     /**
14154      * Returns true if the dropdown list is expanded, else false.
14155      */
14156     isExpanded : function(){
14157         return this.list.isVisible();
14158     },
14159
14160     /**
14161      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14162      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14163      * @param {String} value The data value of the item to select
14164      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14165      * selected item if it is not currently in view (defaults to true)
14166      * @return {Boolean} True if the value matched an item in the list, else false
14167      */
14168     selectByValue : function(v, scrollIntoView){
14169         if(v !== undefined && v !== null){
14170             var r = this.findRecord(this.valueField || this.displayField, v);
14171             if(r){
14172                 this.select(this.store.indexOf(r), scrollIntoView);
14173                 return true;
14174             }
14175         }
14176         return false;
14177     },
14178
14179     /**
14180      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14181      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14182      * @param {Number} index The zero-based index of the list item to select
14183      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14184      * selected item if it is not currently in view (defaults to true)
14185      */
14186     select : function(index, scrollIntoView){
14187         this.selectedIndex = index;
14188         this.view.select(index);
14189         if(scrollIntoView !== false){
14190             var el = this.view.getNode(index);
14191             /*
14192              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14193              */
14194             if(el){
14195                 this.list.scrollChildIntoView(el, false);
14196             }
14197         }
14198     },
14199
14200     // private
14201     selectNext : function(){
14202         var ct = this.store.getCount();
14203         if(ct > 0){
14204             if(this.selectedIndex == -1){
14205                 this.select(0);
14206             }else if(this.selectedIndex < ct-1){
14207                 this.select(this.selectedIndex+1);
14208             }
14209         }
14210     },
14211
14212     // private
14213     selectPrev : function(){
14214         var ct = this.store.getCount();
14215         if(ct > 0){
14216             if(this.selectedIndex == -1){
14217                 this.select(0);
14218             }else if(this.selectedIndex != 0){
14219                 this.select(this.selectedIndex-1);
14220             }
14221         }
14222     },
14223
14224     // private
14225     onKeyUp : function(e){
14226         if(this.editable !== false && !e.isSpecialKey()){
14227             this.lastKey = e.getKey();
14228             this.dqTask.delay(this.queryDelay);
14229         }
14230     },
14231
14232     // private
14233     validateBlur : function(){
14234         return !this.list || !this.list.isVisible();   
14235     },
14236
14237     // private
14238     initQuery : function(){
14239         
14240         var v = this.getRawValue();
14241         
14242         if(this.tickable && this.editable){
14243             v = this.tickableInputEl().getValue();
14244         }
14245         
14246         this.doQuery(v);
14247     },
14248
14249     // private
14250     doForce : function(){
14251         if(this.inputEl().dom.value.length > 0){
14252             this.inputEl().dom.value =
14253                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14254              
14255         }
14256     },
14257
14258     /**
14259      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14260      * query allowing the query action to be canceled if needed.
14261      * @param {String} query The SQL query to execute
14262      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14263      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14264      * saved in the current store (defaults to false)
14265      */
14266     doQuery : function(q, forceAll){
14267         
14268         if(q === undefined || q === null){
14269             q = '';
14270         }
14271         var qe = {
14272             query: q,
14273             forceAll: forceAll,
14274             combo: this,
14275             cancel:false
14276         };
14277         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14278             return false;
14279         }
14280         q = qe.query;
14281         
14282         forceAll = qe.forceAll;
14283         if(forceAll === true || (q.length >= this.minChars)){
14284             
14285             this.hasQuery = true;
14286             
14287             if(this.lastQuery != q || this.alwaysQuery){
14288                 this.lastQuery = q;
14289                 if(this.mode == 'local'){
14290                     this.selectedIndex = -1;
14291                     if(forceAll){
14292                         this.store.clearFilter();
14293                     }else{
14294                         
14295                         if(this.specialFilter){
14296                             this.fireEvent('specialfilter', this);
14297                             this.onLoad();
14298                             return;
14299                         }
14300                         
14301                         this.store.filter(this.displayField, q);
14302                     }
14303                     
14304                     this.store.fireEvent("datachanged", this.store);
14305                     
14306                     this.onLoad();
14307                     
14308                     
14309                 }else{
14310                     
14311                     this.store.baseParams[this.queryParam] = q;
14312                     
14313                     var options = {params : this.getParams(q)};
14314                     
14315                     if(this.loadNext){
14316                         options.add = true;
14317                         options.params.start = this.page * this.pageSize;
14318                     }
14319                     
14320                     this.store.load(options);
14321                     
14322                     /*
14323                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14324                      *  we should expand the list on onLoad
14325                      *  so command out it
14326                      */
14327 //                    this.expand();
14328                 }
14329             }else{
14330                 this.selectedIndex = -1;
14331                 this.onLoad();   
14332             }
14333         }
14334         
14335         this.loadNext = false;
14336     },
14337     
14338     // private
14339     getParams : function(q){
14340         var p = {};
14341         //p[this.queryParam] = q;
14342         
14343         if(this.pageSize){
14344             p.start = 0;
14345             p.limit = this.pageSize;
14346         }
14347         return p;
14348     },
14349
14350     /**
14351      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14352      */
14353     collapse : function(){
14354         if(!this.isExpanded()){
14355             return;
14356         }
14357         
14358         this.list.hide();
14359         
14360         this.hasFocus = false;
14361         
14362         if(this.tickable){
14363             this.okBtn.hide();
14364             this.cancelBtn.hide();
14365             this.trigger.show();
14366             
14367             if(this.editable){
14368                 this.tickableInputEl().dom.value = '';
14369                 this.tickableInputEl().blur();
14370             }
14371             
14372         }
14373         
14374         Roo.get(document).un('mousedown', this.collapseIf, this);
14375         Roo.get(document).un('mousewheel', this.collapseIf, this);
14376         if (!this.editable) {
14377             Roo.get(document).un('keydown', this.listKeyPress, this);
14378         }
14379         this.fireEvent('collapse', this);
14380         
14381         this.validate();
14382     },
14383
14384     // private
14385     collapseIf : function(e){
14386         var in_combo  = e.within(this.el);
14387         var in_list =  e.within(this.list);
14388         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14389         
14390         if (in_combo || in_list || is_list) {
14391             //e.stopPropagation();
14392             return;
14393         }
14394         
14395         if(this.tickable){
14396             this.onTickableFooterButtonClick(e, false, false);
14397         }
14398
14399         this.collapse();
14400         
14401     },
14402
14403     /**
14404      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14405      */
14406     expand : function(){
14407        
14408         if(this.isExpanded() || !this.hasFocus){
14409             return;
14410         }
14411         
14412         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14413         this.list.setWidth(lw);
14414         
14415         Roo.log('expand');
14416         
14417         this.list.show();
14418         
14419         this.restrictHeight();
14420         
14421         if(this.tickable){
14422             
14423             this.tickItems = Roo.apply([], this.item);
14424             
14425             this.okBtn.show();
14426             this.cancelBtn.show();
14427             this.trigger.hide();
14428             
14429             if(this.editable){
14430                 this.tickableInputEl().focus();
14431             }
14432             
14433         }
14434         
14435         Roo.get(document).on('mousedown', this.collapseIf, this);
14436         Roo.get(document).on('mousewheel', this.collapseIf, this);
14437         if (!this.editable) {
14438             Roo.get(document).on('keydown', this.listKeyPress, this);
14439         }
14440         
14441         this.fireEvent('expand', this);
14442     },
14443
14444     // private
14445     // Implements the default empty TriggerField.onTriggerClick function
14446     onTriggerClick : function(e)
14447     {
14448         Roo.log('trigger click');
14449         
14450         if(this.disabled || !this.triggerList){
14451             return;
14452         }
14453         
14454         this.page = 0;
14455         this.loadNext = false;
14456         
14457         if(this.isExpanded()){
14458             this.collapse();
14459             if (!this.blockFocus) {
14460                 this.inputEl().focus();
14461             }
14462             
14463         }else {
14464             this.hasFocus = true;
14465             if(this.triggerAction == 'all') {
14466                 this.doQuery(this.allQuery, true);
14467             } else {
14468                 this.doQuery(this.getRawValue());
14469             }
14470             if (!this.blockFocus) {
14471                 this.inputEl().focus();
14472             }
14473         }
14474     },
14475     
14476     onTickableTriggerClick : function(e)
14477     {
14478         if(this.disabled){
14479             return;
14480         }
14481         
14482         this.page = 0;
14483         this.loadNext = false;
14484         this.hasFocus = true;
14485         
14486         if(this.triggerAction == 'all') {
14487             this.doQuery(this.allQuery, true);
14488         } else {
14489             this.doQuery(this.getRawValue());
14490         }
14491     },
14492     
14493     onSearchFieldClick : function(e)
14494     {
14495         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14496             this.onTickableFooterButtonClick(e, false, false);
14497             return;
14498         }
14499         
14500         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14501             return;
14502         }
14503         
14504         this.page = 0;
14505         this.loadNext = false;
14506         this.hasFocus = true;
14507         
14508         if(this.triggerAction == 'all') {
14509             this.doQuery(this.allQuery, true);
14510         } else {
14511             this.doQuery(this.getRawValue());
14512         }
14513     },
14514     
14515     listKeyPress : function(e)
14516     {
14517         //Roo.log('listkeypress');
14518         // scroll to first matching element based on key pres..
14519         if (e.isSpecialKey()) {
14520             return false;
14521         }
14522         var k = String.fromCharCode(e.getKey()).toUpperCase();
14523         //Roo.log(k);
14524         var match  = false;
14525         var csel = this.view.getSelectedNodes();
14526         var cselitem = false;
14527         if (csel.length) {
14528             var ix = this.view.indexOf(csel[0]);
14529             cselitem  = this.store.getAt(ix);
14530             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14531                 cselitem = false;
14532             }
14533             
14534         }
14535         
14536         this.store.each(function(v) { 
14537             if (cselitem) {
14538                 // start at existing selection.
14539                 if (cselitem.id == v.id) {
14540                     cselitem = false;
14541                 }
14542                 return true;
14543             }
14544                 
14545             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14546                 match = this.store.indexOf(v);
14547                 return false;
14548             }
14549             return true;
14550         }, this);
14551         
14552         if (match === false) {
14553             return true; // no more action?
14554         }
14555         // scroll to?
14556         this.view.select(match);
14557         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14558         sn.scrollIntoView(sn.dom.parentNode, false);
14559     },
14560     
14561     onViewScroll : function(e, t){
14562         
14563         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){
14564             return;
14565         }
14566         
14567         this.hasQuery = true;
14568         
14569         this.loading = this.list.select('.loading', true).first();
14570         
14571         if(this.loading === null){
14572             this.list.createChild({
14573                 tag: 'div',
14574                 cls: 'loading roo-select2-more-results roo-select2-active',
14575                 html: 'Loading more results...'
14576             });
14577             
14578             this.loading = this.list.select('.loading', true).first();
14579             
14580             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14581             
14582             this.loading.hide();
14583         }
14584         
14585         this.loading.show();
14586         
14587         var _combo = this;
14588         
14589         this.page++;
14590         this.loadNext = true;
14591         
14592         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14593         
14594         return;
14595     },
14596     
14597     addItem : function(o)
14598     {   
14599         var dv = ''; // display value
14600         
14601         if (this.displayField) {
14602             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14603         } else {
14604             // this is an error condition!!!
14605             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14606         }
14607         
14608         if(!dv.length){
14609             return;
14610         }
14611         
14612         var choice = this.choices.createChild({
14613             tag: 'li',
14614             cls: 'roo-select2-search-choice',
14615             cn: [
14616                 {
14617                     tag: 'div',
14618                     html: dv
14619                 },
14620                 {
14621                     tag: 'a',
14622                     href: '#',
14623                     cls: 'roo-select2-search-choice-close fa fa-times',
14624                     tabindex: '-1'
14625                 }
14626             ]
14627             
14628         }, this.searchField);
14629         
14630         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14631         
14632         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14633         
14634         this.item.push(o);
14635         
14636         this.lastData = o;
14637         
14638         this.syncValue();
14639         
14640         this.inputEl().dom.value = '';
14641         
14642         this.validate();
14643     },
14644     
14645     onRemoveItem : function(e, _self, o)
14646     {
14647         e.preventDefault();
14648         
14649         this.lastItem = Roo.apply([], this.item);
14650         
14651         var index = this.item.indexOf(o.data) * 1;
14652         
14653         if( index < 0){
14654             Roo.log('not this item?!');
14655             return;
14656         }
14657         
14658         this.item.splice(index, 1);
14659         o.item.remove();
14660         
14661         this.syncValue();
14662         
14663         this.fireEvent('remove', this, e);
14664         
14665         this.validate();
14666         
14667     },
14668     
14669     syncValue : function()
14670     {
14671         if(!this.item.length){
14672             this.clearValue();
14673             return;
14674         }
14675             
14676         var value = [];
14677         var _this = this;
14678         Roo.each(this.item, function(i){
14679             if(_this.valueField){
14680                 value.push(i[_this.valueField]);
14681                 return;
14682             }
14683
14684             value.push(i);
14685         });
14686
14687         this.value = value.join(',');
14688
14689         if(this.hiddenField){
14690             this.hiddenField.dom.value = this.value;
14691         }
14692         
14693         this.store.fireEvent("datachanged", this.store);
14694         
14695         this.validate();
14696     },
14697     
14698     clearItem : function()
14699     {
14700         if(!this.multiple){
14701             return;
14702         }
14703         
14704         this.item = [];
14705         
14706         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14707            c.remove();
14708         });
14709         
14710         this.syncValue();
14711         
14712         this.validate();
14713         
14714         if(this.tickable && !Roo.isTouch){
14715             this.view.refresh();
14716         }
14717     },
14718     
14719     inputEl: function ()
14720     {
14721         if(Roo.isIOS && this.useNativeIOS){
14722             return this.el.select('select.roo-ios-select', true).first();
14723         }
14724         
14725         if(Roo.isTouch && this.mobileTouchView){
14726             return this.el.select('input.form-control',true).first();
14727         }
14728         
14729         if(this.tickable){
14730             return this.searchField;
14731         }
14732         
14733         return this.el.select('input.form-control',true).first();
14734     },
14735     
14736     onTickableFooterButtonClick : function(e, btn, el)
14737     {
14738         e.preventDefault();
14739         
14740         this.lastItem = Roo.apply([], this.item);
14741         
14742         if(btn && btn.name == 'cancel'){
14743             this.tickItems = Roo.apply([], this.item);
14744             this.collapse();
14745             return;
14746         }
14747         
14748         this.clearItem();
14749         
14750         var _this = this;
14751         
14752         Roo.each(this.tickItems, function(o){
14753             _this.addItem(o);
14754         });
14755         
14756         this.collapse();
14757         
14758     },
14759     
14760     validate : function()
14761     {
14762         if(this.getVisibilityEl().hasClass('hidden')){
14763             return true;
14764         }
14765         
14766         var v = this.getRawValue();
14767         
14768         if(this.multiple){
14769             v = this.getValue();
14770         }
14771         
14772         if(this.disabled || this.allowBlank || v.length){
14773             this.markValid();
14774             return true;
14775         }
14776         
14777         this.markInvalid();
14778         return false;
14779     },
14780     
14781     tickableInputEl : function()
14782     {
14783         if(!this.tickable || !this.editable){
14784             return this.inputEl();
14785         }
14786         
14787         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14788     },
14789     
14790     
14791     getAutoCreateTouchView : function()
14792     {
14793         var id = Roo.id();
14794         
14795         var cfg = {
14796             cls: 'form-group' //input-group
14797         };
14798         
14799         var input =  {
14800             tag: 'input',
14801             id : id,
14802             type : this.inputType,
14803             cls : 'form-control x-combo-noedit',
14804             autocomplete: 'new-password',
14805             placeholder : this.placeholder || '',
14806             readonly : true
14807         };
14808         
14809         if (this.name) {
14810             input.name = this.name;
14811         }
14812         
14813         if (this.size) {
14814             input.cls += ' input-' + this.size;
14815         }
14816         
14817         if (this.disabled) {
14818             input.disabled = true;
14819         }
14820         
14821         var inputblock = {
14822             cls : '',
14823             cn : [
14824                 input
14825             ]
14826         };
14827         
14828         if(this.before){
14829             inputblock.cls += ' input-group';
14830             
14831             inputblock.cn.unshift({
14832                 tag :'span',
14833                 cls : 'input-group-addon',
14834                 html : this.before
14835             });
14836         }
14837         
14838         if(this.removable && !this.multiple){
14839             inputblock.cls += ' roo-removable';
14840             
14841             inputblock.cn.push({
14842                 tag: 'button',
14843                 html : 'x',
14844                 cls : 'roo-combo-removable-btn close'
14845             });
14846         }
14847
14848         if(this.hasFeedback && !this.allowBlank){
14849             
14850             inputblock.cls += ' has-feedback';
14851             
14852             inputblock.cn.push({
14853                 tag: 'span',
14854                 cls: 'glyphicon form-control-feedback'
14855             });
14856             
14857         }
14858         
14859         if (this.after) {
14860             
14861             inputblock.cls += (this.before) ? '' : ' input-group';
14862             
14863             inputblock.cn.push({
14864                 tag :'span',
14865                 cls : 'input-group-addon',
14866                 html : this.after
14867             });
14868         }
14869
14870         var box = {
14871             tag: 'div',
14872             cn: [
14873                 {
14874                     tag: 'input',
14875                     type : 'hidden',
14876                     cls: 'form-hidden-field'
14877                 },
14878                 inputblock
14879             ]
14880             
14881         };
14882         
14883         if(this.multiple){
14884             box = {
14885                 tag: 'div',
14886                 cn: [
14887                     {
14888                         tag: 'input',
14889                         type : 'hidden',
14890                         cls: 'form-hidden-field'
14891                     },
14892                     {
14893                         tag: 'ul',
14894                         cls: 'roo-select2-choices',
14895                         cn:[
14896                             {
14897                                 tag: 'li',
14898                                 cls: 'roo-select2-search-field',
14899                                 cn: [
14900
14901                                     inputblock
14902                                 ]
14903                             }
14904                         ]
14905                     }
14906                 ]
14907             }
14908         };
14909         
14910         var combobox = {
14911             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14912             cn: [
14913                 box
14914             ]
14915         };
14916         
14917         if(!this.multiple && this.showToggleBtn){
14918             
14919             var caret = {
14920                         tag: 'span',
14921                         cls: 'caret'
14922             };
14923             
14924             if (this.caret != false) {
14925                 caret = {
14926                      tag: 'i',
14927                      cls: 'fa fa-' + this.caret
14928                 };
14929                 
14930             }
14931             
14932             combobox.cn.push({
14933                 tag :'span',
14934                 cls : 'input-group-addon btn dropdown-toggle',
14935                 cn : [
14936                     caret,
14937                     {
14938                         tag: 'span',
14939                         cls: 'combobox-clear',
14940                         cn  : [
14941                             {
14942                                 tag : 'i',
14943                                 cls: 'icon-remove'
14944                             }
14945                         ]
14946                     }
14947                 ]
14948
14949             })
14950         }
14951         
14952         if(this.multiple){
14953             combobox.cls += ' roo-select2-container-multi';
14954         }
14955         
14956         var align = this.labelAlign || this.parentLabelAlign();
14957         
14958         if (align ==='left' && this.fieldLabel.length) {
14959
14960             cfg.cn = [
14961                 {
14962                    tag : 'i',
14963                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14964                    tooltip : 'This field is required'
14965                 },
14966                 {
14967                     tag: 'label',
14968                     cls : 'control-label',
14969                     html : this.fieldLabel
14970
14971                 },
14972                 {
14973                     cls : '', 
14974                     cn: [
14975                         combobox
14976                     ]
14977                 }
14978             ];
14979             
14980             var labelCfg = cfg.cn[1];
14981             var contentCfg = cfg.cn[2];
14982             
14983
14984             if(this.indicatorpos == 'right'){
14985                 cfg.cn = [
14986                     {
14987                         tag: 'label',
14988                         'for' :  id,
14989                         cls : 'control-label',
14990                         cn : [
14991                             {
14992                                 tag : 'span',
14993                                 html : this.fieldLabel
14994                             },
14995                             {
14996                                 tag : 'i',
14997                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14998                                 tooltip : 'This field is required'
14999                             }
15000                         ]
15001                     },
15002                     {
15003                         cls : "",
15004                         cn: [
15005                             combobox
15006                         ]
15007                     }
15008
15009                 ];
15010                 
15011                 labelCfg = cfg.cn[0];
15012                 contentCfg = cfg.cn[1];
15013             }
15014             
15015            
15016             
15017             if(this.labelWidth > 12){
15018                 labelCfg.style = "width: " + this.labelWidth + 'px';
15019             }
15020             
15021             if(this.labelWidth < 13 && this.labelmd == 0){
15022                 this.labelmd = this.labelWidth;
15023             }
15024             
15025             if(this.labellg > 0){
15026                 labelCfg.cls += ' col-lg-' + this.labellg;
15027                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15028             }
15029             
15030             if(this.labelmd > 0){
15031                 labelCfg.cls += ' col-md-' + this.labelmd;
15032                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15033             }
15034             
15035             if(this.labelsm > 0){
15036                 labelCfg.cls += ' col-sm-' + this.labelsm;
15037                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15038             }
15039             
15040             if(this.labelxs > 0){
15041                 labelCfg.cls += ' col-xs-' + this.labelxs;
15042                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15043             }
15044                 
15045                 
15046         } else if ( this.fieldLabel.length) {
15047             cfg.cn = [
15048                 {
15049                    tag : 'i',
15050                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15051                    tooltip : 'This field is required'
15052                 },
15053                 {
15054                     tag: 'label',
15055                     cls : 'control-label',
15056                     html : this.fieldLabel
15057
15058                 },
15059                 {
15060                     cls : '', 
15061                     cn: [
15062                         combobox
15063                     ]
15064                 }
15065             ];
15066             
15067             if(this.indicatorpos == 'right'){
15068                 cfg.cn = [
15069                     {
15070                         tag: 'label',
15071                         cls : 'control-label',
15072                         html : this.fieldLabel,
15073                         cn : [
15074                             {
15075                                tag : 'i',
15076                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15077                                tooltip : 'This field is required'
15078                             }
15079                         ]
15080                     },
15081                     {
15082                         cls : '', 
15083                         cn: [
15084                             combobox
15085                         ]
15086                     }
15087                 ];
15088             }
15089         } else {
15090             cfg.cn = combobox;    
15091         }
15092         
15093         
15094         var settings = this;
15095         
15096         ['xs','sm','md','lg'].map(function(size){
15097             if (settings[size]) {
15098                 cfg.cls += ' col-' + size + '-' + settings[size];
15099             }
15100         });
15101         
15102         return cfg;
15103     },
15104     
15105     initTouchView : function()
15106     {
15107         this.renderTouchView();
15108         
15109         this.touchViewEl.on('scroll', function(){
15110             this.el.dom.scrollTop = 0;
15111         }, this);
15112         
15113         this.originalValue = this.getValue();
15114         
15115         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15116         
15117         this.inputEl().on("click", this.showTouchView, this);
15118         if (this.triggerEl) {
15119             this.triggerEl.on("click", this.showTouchView, this);
15120         }
15121         
15122         
15123         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15124         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15125         
15126         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15127         
15128         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15129         this.store.on('load', this.onTouchViewLoad, this);
15130         this.store.on('loadexception', this.onTouchViewLoadException, this);
15131         
15132         if(this.hiddenName){
15133             
15134             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15135             
15136             this.hiddenField.dom.value =
15137                 this.hiddenValue !== undefined ? this.hiddenValue :
15138                 this.value !== undefined ? this.value : '';
15139         
15140             this.el.dom.removeAttribute('name');
15141             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15142         }
15143         
15144         if(this.multiple){
15145             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15146             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15147         }
15148         
15149         if(this.removable && !this.multiple){
15150             var close = this.closeTriggerEl();
15151             if(close){
15152                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15153                 close.on('click', this.removeBtnClick, this, close);
15154             }
15155         }
15156         /*
15157          * fix the bug in Safari iOS8
15158          */
15159         this.inputEl().on("focus", function(e){
15160             document.activeElement.blur();
15161         }, this);
15162         
15163         return;
15164         
15165         
15166     },
15167     
15168     renderTouchView : function()
15169     {
15170         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15171         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15172         
15173         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15174         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15175         
15176         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15177         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15178         this.touchViewBodyEl.setStyle('overflow', 'auto');
15179         
15180         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15181         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15182         
15183         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15184         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15185         
15186     },
15187     
15188     showTouchView : function()
15189     {
15190         if(this.disabled){
15191             return;
15192         }
15193         
15194         this.touchViewHeaderEl.hide();
15195
15196         if(this.modalTitle.length){
15197             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15198             this.touchViewHeaderEl.show();
15199         }
15200
15201         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15202         this.touchViewEl.show();
15203
15204         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15205         
15206         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15207         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15208
15209         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15210
15211         if(this.modalTitle.length){
15212             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15213         }
15214         
15215         this.touchViewBodyEl.setHeight(bodyHeight);
15216
15217         if(this.animate){
15218             var _this = this;
15219             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15220         }else{
15221             this.touchViewEl.addClass('in');
15222         }
15223
15224         this.doTouchViewQuery();
15225         
15226     },
15227     
15228     hideTouchView : function()
15229     {
15230         this.touchViewEl.removeClass('in');
15231
15232         if(this.animate){
15233             var _this = this;
15234             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15235         }else{
15236             this.touchViewEl.setStyle('display', 'none');
15237         }
15238         
15239     },
15240     
15241     setTouchViewValue : function()
15242     {
15243         if(this.multiple){
15244             this.clearItem();
15245         
15246             var _this = this;
15247
15248             Roo.each(this.tickItems, function(o){
15249                 this.addItem(o);
15250             }, this);
15251         }
15252         
15253         this.hideTouchView();
15254     },
15255     
15256     doTouchViewQuery : function()
15257     {
15258         var qe = {
15259             query: '',
15260             forceAll: true,
15261             combo: this,
15262             cancel:false
15263         };
15264         
15265         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15266             return false;
15267         }
15268         
15269         if(!this.alwaysQuery || this.mode == 'local'){
15270             this.onTouchViewLoad();
15271             return;
15272         }
15273         
15274         this.store.load();
15275     },
15276     
15277     onTouchViewBeforeLoad : function(combo,opts)
15278     {
15279         return;
15280     },
15281
15282     // private
15283     onTouchViewLoad : function()
15284     {
15285         if(this.store.getCount() < 1){
15286             this.onTouchViewEmptyResults();
15287             return;
15288         }
15289         
15290         this.clearTouchView();
15291         
15292         var rawValue = this.getRawValue();
15293         
15294         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15295         
15296         this.tickItems = [];
15297         
15298         this.store.data.each(function(d, rowIndex){
15299             var row = this.touchViewListGroup.createChild(template);
15300             
15301             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15302                 row.addClass(d.data.cls);
15303             }
15304             
15305             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15306                 var cfg = {
15307                     data : d.data,
15308                     html : d.data[this.displayField]
15309                 };
15310                 
15311                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15312                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15313                 }
15314             }
15315             row.removeClass('selected');
15316             if(!this.multiple && this.valueField &&
15317                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15318             {
15319                 // radio buttons..
15320                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15321                 row.addClass('selected');
15322             }
15323             
15324             if(this.multiple && this.valueField &&
15325                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15326             {
15327                 
15328                 // checkboxes...
15329                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15330                 this.tickItems.push(d.data);
15331             }
15332             
15333             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15334             
15335         }, this);
15336         
15337         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15338         
15339         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15340
15341         if(this.modalTitle.length){
15342             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15343         }
15344
15345         var listHeight = this.touchViewListGroup.getHeight();
15346         
15347         var _this = this;
15348         
15349         if(firstChecked && listHeight > bodyHeight){
15350             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15351         }
15352         
15353     },
15354     
15355     onTouchViewLoadException : function()
15356     {
15357         this.hideTouchView();
15358     },
15359     
15360     onTouchViewEmptyResults : function()
15361     {
15362         this.clearTouchView();
15363         
15364         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15365         
15366         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15367         
15368     },
15369     
15370     clearTouchView : function()
15371     {
15372         this.touchViewListGroup.dom.innerHTML = '';
15373     },
15374     
15375     onTouchViewClick : function(e, el, o)
15376     {
15377         e.preventDefault();
15378         
15379         var row = o.row;
15380         var rowIndex = o.rowIndex;
15381         
15382         var r = this.store.getAt(rowIndex);
15383         
15384         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15385             
15386             if(!this.multiple){
15387                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15388                     c.dom.removeAttribute('checked');
15389                 }, this);
15390
15391                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15392
15393                 this.setFromData(r.data);
15394
15395                 var close = this.closeTriggerEl();
15396
15397                 if(close){
15398                     close.show();
15399                 }
15400
15401                 this.hideTouchView();
15402
15403                 this.fireEvent('select', this, r, rowIndex);
15404
15405                 return;
15406             }
15407
15408             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15409                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15410                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15411                 return;
15412             }
15413
15414             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15415             this.addItem(r.data);
15416             this.tickItems.push(r.data);
15417         }
15418     },
15419     
15420     getAutoCreateNativeIOS : function()
15421     {
15422         var cfg = {
15423             cls: 'form-group' //input-group,
15424         };
15425         
15426         var combobox =  {
15427             tag: 'select',
15428             cls : 'roo-ios-select'
15429         };
15430         
15431         if (this.name) {
15432             combobox.name = this.name;
15433         }
15434         
15435         if (this.disabled) {
15436             combobox.disabled = true;
15437         }
15438         
15439         var settings = this;
15440         
15441         ['xs','sm','md','lg'].map(function(size){
15442             if (settings[size]) {
15443                 cfg.cls += ' col-' + size + '-' + settings[size];
15444             }
15445         });
15446         
15447         cfg.cn = combobox;
15448         
15449         return cfg;
15450         
15451     },
15452     
15453     initIOSView : function()
15454     {
15455         this.store.on('load', this.onIOSViewLoad, this);
15456         
15457         return;
15458     },
15459     
15460     onIOSViewLoad : function()
15461     {
15462         if(this.store.getCount() < 1){
15463             return;
15464         }
15465         
15466         this.clearIOSView();
15467         
15468         if(this.allowBlank) {
15469             
15470             var default_text = '-- SELECT --';
15471             
15472             if(this.placeholder.length){
15473                 default_text = this.placeholder;
15474             }
15475             
15476             if(this.emptyTitle.length){
15477                 default_text += ' - ' + this.emptyTitle + ' -';
15478             }
15479             
15480             var opt = this.inputEl().createChild({
15481                 tag: 'option',
15482                 value : 0,
15483                 html : default_text
15484             });
15485             
15486             var o = {};
15487             o[this.valueField] = 0;
15488             o[this.displayField] = default_text;
15489             
15490             this.ios_options.push({
15491                 data : o,
15492                 el : opt
15493             });
15494             
15495         }
15496         
15497         this.store.data.each(function(d, rowIndex){
15498             
15499             var html = '';
15500             
15501             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15502                 html = d.data[this.displayField];
15503             }
15504             
15505             var value = '';
15506             
15507             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15508                 value = d.data[this.valueField];
15509             }
15510             
15511             var option = {
15512                 tag: 'option',
15513                 value : value,
15514                 html : html
15515             };
15516             
15517             if(this.value == d.data[this.valueField]){
15518                 option['selected'] = true;
15519             }
15520             
15521             var opt = this.inputEl().createChild(option);
15522             
15523             this.ios_options.push({
15524                 data : d.data,
15525                 el : opt
15526             });
15527             
15528         }, this);
15529         
15530         this.inputEl().on('change', function(){
15531            this.fireEvent('select', this);
15532         }, this);
15533         
15534     },
15535     
15536     clearIOSView: function()
15537     {
15538         this.inputEl().dom.innerHTML = '';
15539         
15540         this.ios_options = [];
15541     },
15542     
15543     setIOSValue: function(v)
15544     {
15545         this.value = v;
15546         
15547         if(!this.ios_options){
15548             return;
15549         }
15550         
15551         Roo.each(this.ios_options, function(opts){
15552            
15553            opts.el.dom.removeAttribute('selected');
15554            
15555            if(opts.data[this.valueField] != v){
15556                return;
15557            }
15558            
15559            opts.el.dom.setAttribute('selected', true);
15560            
15561         }, this);
15562     }
15563
15564     /** 
15565     * @cfg {Boolean} grow 
15566     * @hide 
15567     */
15568     /** 
15569     * @cfg {Number} growMin 
15570     * @hide 
15571     */
15572     /** 
15573     * @cfg {Number} growMax 
15574     * @hide 
15575     */
15576     /**
15577      * @hide
15578      * @method autoSize
15579      */
15580 });
15581
15582 Roo.apply(Roo.bootstrap.ComboBox,  {
15583     
15584     header : {
15585         tag: 'div',
15586         cls: 'modal-header',
15587         cn: [
15588             {
15589                 tag: 'h4',
15590                 cls: 'modal-title'
15591             }
15592         ]
15593     },
15594     
15595     body : {
15596         tag: 'div',
15597         cls: 'modal-body',
15598         cn: [
15599             {
15600                 tag: 'ul',
15601                 cls: 'list-group'
15602             }
15603         ]
15604     },
15605     
15606     listItemRadio : {
15607         tag: 'li',
15608         cls: 'list-group-item',
15609         cn: [
15610             {
15611                 tag: 'span',
15612                 cls: 'roo-combobox-list-group-item-value'
15613             },
15614             {
15615                 tag: 'div',
15616                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15617                 cn: [
15618                     {
15619                         tag: 'input',
15620                         type: 'radio'
15621                     },
15622                     {
15623                         tag: 'label'
15624                     }
15625                 ]
15626             }
15627         ]
15628     },
15629     
15630     listItemCheckbox : {
15631         tag: 'li',
15632         cls: 'list-group-item',
15633         cn: [
15634             {
15635                 tag: 'span',
15636                 cls: 'roo-combobox-list-group-item-value'
15637             },
15638             {
15639                 tag: 'div',
15640                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15641                 cn: [
15642                     {
15643                         tag: 'input',
15644                         type: 'checkbox'
15645                     },
15646                     {
15647                         tag: 'label'
15648                     }
15649                 ]
15650             }
15651         ]
15652     },
15653     
15654     emptyResult : {
15655         tag: 'div',
15656         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15657     },
15658     
15659     footer : {
15660         tag: 'div',
15661         cls: 'modal-footer',
15662         cn: [
15663             {
15664                 tag: 'div',
15665                 cls: 'row',
15666                 cn: [
15667                     {
15668                         tag: 'div',
15669                         cls: 'col-xs-6 text-left',
15670                         cn: {
15671                             tag: 'button',
15672                             cls: 'btn btn-danger roo-touch-view-cancel',
15673                             html: 'Cancel'
15674                         }
15675                     },
15676                     {
15677                         tag: 'div',
15678                         cls: 'col-xs-6 text-right',
15679                         cn: {
15680                             tag: 'button',
15681                             cls: 'btn btn-success roo-touch-view-ok',
15682                             html: 'OK'
15683                         }
15684                     }
15685                 ]
15686             }
15687         ]
15688         
15689     }
15690 });
15691
15692 Roo.apply(Roo.bootstrap.ComboBox,  {
15693     
15694     touchViewTemplate : {
15695         tag: 'div',
15696         cls: 'modal fade roo-combobox-touch-view',
15697         cn: [
15698             {
15699                 tag: 'div',
15700                 cls: 'modal-dialog',
15701                 style : 'position:fixed', // we have to fix position....
15702                 cn: [
15703                     {
15704                         tag: 'div',
15705                         cls: 'modal-content',
15706                         cn: [
15707                             Roo.bootstrap.ComboBox.header,
15708                             Roo.bootstrap.ComboBox.body,
15709                             Roo.bootstrap.ComboBox.footer
15710                         ]
15711                     }
15712                 ]
15713             }
15714         ]
15715     }
15716 });/*
15717  * Based on:
15718  * Ext JS Library 1.1.1
15719  * Copyright(c) 2006-2007, Ext JS, LLC.
15720  *
15721  * Originally Released Under LGPL - original licence link has changed is not relivant.
15722  *
15723  * Fork - LGPL
15724  * <script type="text/javascript">
15725  */
15726
15727 /**
15728  * @class Roo.View
15729  * @extends Roo.util.Observable
15730  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15731  * This class also supports single and multi selection modes. <br>
15732  * Create a data model bound view:
15733  <pre><code>
15734  var store = new Roo.data.Store(...);
15735
15736  var view = new Roo.View({
15737     el : "my-element",
15738     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15739  
15740     singleSelect: true,
15741     selectedClass: "ydataview-selected",
15742     store: store
15743  });
15744
15745  // listen for node click?
15746  view.on("click", function(vw, index, node, e){
15747  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15748  });
15749
15750  // load XML data
15751  dataModel.load("foobar.xml");
15752  </code></pre>
15753  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15754  * <br><br>
15755  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15756  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15757  * 
15758  * Note: old style constructor is still suported (container, template, config)
15759  * 
15760  * @constructor
15761  * Create a new View
15762  * @param {Object} config The config object
15763  * 
15764  */
15765 Roo.View = function(config, depreciated_tpl, depreciated_config){
15766     
15767     this.parent = false;
15768     
15769     if (typeof(depreciated_tpl) == 'undefined') {
15770         // new way.. - universal constructor.
15771         Roo.apply(this, config);
15772         this.el  = Roo.get(this.el);
15773     } else {
15774         // old format..
15775         this.el  = Roo.get(config);
15776         this.tpl = depreciated_tpl;
15777         Roo.apply(this, depreciated_config);
15778     }
15779     this.wrapEl  = this.el.wrap().wrap();
15780     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15781     
15782     
15783     if(typeof(this.tpl) == "string"){
15784         this.tpl = new Roo.Template(this.tpl);
15785     } else {
15786         // support xtype ctors..
15787         this.tpl = new Roo.factory(this.tpl, Roo);
15788     }
15789     
15790     
15791     this.tpl.compile();
15792     
15793     /** @private */
15794     this.addEvents({
15795         /**
15796          * @event beforeclick
15797          * Fires before a click is processed. Returns false to cancel the default action.
15798          * @param {Roo.View} this
15799          * @param {Number} index The index of the target node
15800          * @param {HTMLElement} node The target node
15801          * @param {Roo.EventObject} e The raw event object
15802          */
15803             "beforeclick" : true,
15804         /**
15805          * @event click
15806          * Fires when a template node is clicked.
15807          * @param {Roo.View} this
15808          * @param {Number} index The index of the target node
15809          * @param {HTMLElement} node The target node
15810          * @param {Roo.EventObject} e The raw event object
15811          */
15812             "click" : true,
15813         /**
15814          * @event dblclick
15815          * Fires when a template node is double clicked.
15816          * @param {Roo.View} this
15817          * @param {Number} index The index of the target node
15818          * @param {HTMLElement} node The target node
15819          * @param {Roo.EventObject} e The raw event object
15820          */
15821             "dblclick" : true,
15822         /**
15823          * @event contextmenu
15824          * Fires when a template node is right clicked.
15825          * @param {Roo.View} this
15826          * @param {Number} index The index of the target node
15827          * @param {HTMLElement} node The target node
15828          * @param {Roo.EventObject} e The raw event object
15829          */
15830             "contextmenu" : true,
15831         /**
15832          * @event selectionchange
15833          * Fires when the selected nodes change.
15834          * @param {Roo.View} this
15835          * @param {Array} selections Array of the selected nodes
15836          */
15837             "selectionchange" : true,
15838     
15839         /**
15840          * @event beforeselect
15841          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15842          * @param {Roo.View} this
15843          * @param {HTMLElement} node The node to be selected
15844          * @param {Array} selections Array of currently selected nodes
15845          */
15846             "beforeselect" : true,
15847         /**
15848          * @event preparedata
15849          * Fires on every row to render, to allow you to change the data.
15850          * @param {Roo.View} this
15851          * @param {Object} data to be rendered (change this)
15852          */
15853           "preparedata" : true
15854           
15855           
15856         });
15857
15858
15859
15860     this.el.on({
15861         "click": this.onClick,
15862         "dblclick": this.onDblClick,
15863         "contextmenu": this.onContextMenu,
15864         scope:this
15865     });
15866
15867     this.selections = [];
15868     this.nodes = [];
15869     this.cmp = new Roo.CompositeElementLite([]);
15870     if(this.store){
15871         this.store = Roo.factory(this.store, Roo.data);
15872         this.setStore(this.store, true);
15873     }
15874     
15875     if ( this.footer && this.footer.xtype) {
15876            
15877          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15878         
15879         this.footer.dataSource = this.store;
15880         this.footer.container = fctr;
15881         this.footer = Roo.factory(this.footer, Roo);
15882         fctr.insertFirst(this.el);
15883         
15884         // this is a bit insane - as the paging toolbar seems to detach the el..
15885 //        dom.parentNode.parentNode.parentNode
15886          // they get detached?
15887     }
15888     
15889     
15890     Roo.View.superclass.constructor.call(this);
15891     
15892     
15893 };
15894
15895 Roo.extend(Roo.View, Roo.util.Observable, {
15896     
15897      /**
15898      * @cfg {Roo.data.Store} store Data store to load data from.
15899      */
15900     store : false,
15901     
15902     /**
15903      * @cfg {String|Roo.Element} el The container element.
15904      */
15905     el : '',
15906     
15907     /**
15908      * @cfg {String|Roo.Template} tpl The template used by this View 
15909      */
15910     tpl : false,
15911     /**
15912      * @cfg {String} dataName the named area of the template to use as the data area
15913      *                          Works with domtemplates roo-name="name"
15914      */
15915     dataName: false,
15916     /**
15917      * @cfg {String} selectedClass The css class to add to selected nodes
15918      */
15919     selectedClass : "x-view-selected",
15920      /**
15921      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15922      */
15923     emptyText : "",
15924     
15925     /**
15926      * @cfg {String} text to display on mask (default Loading)
15927      */
15928     mask : false,
15929     /**
15930      * @cfg {Boolean} multiSelect Allow multiple selection
15931      */
15932     multiSelect : false,
15933     /**
15934      * @cfg {Boolean} singleSelect Allow single selection
15935      */
15936     singleSelect:  false,
15937     
15938     /**
15939      * @cfg {Boolean} toggleSelect - selecting 
15940      */
15941     toggleSelect : false,
15942     
15943     /**
15944      * @cfg {Boolean} tickable - selecting 
15945      */
15946     tickable : false,
15947     
15948     /**
15949      * Returns the element this view is bound to.
15950      * @return {Roo.Element}
15951      */
15952     getEl : function(){
15953         return this.wrapEl;
15954     },
15955     
15956     
15957
15958     /**
15959      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15960      */
15961     refresh : function(){
15962         //Roo.log('refresh');
15963         var t = this.tpl;
15964         
15965         // if we are using something like 'domtemplate', then
15966         // the what gets used is:
15967         // t.applySubtemplate(NAME, data, wrapping data..)
15968         // the outer template then get' applied with
15969         //     the store 'extra data'
15970         // and the body get's added to the
15971         //      roo-name="data" node?
15972         //      <span class='roo-tpl-{name}'></span> ?????
15973         
15974         
15975         
15976         this.clearSelections();
15977         this.el.update("");
15978         var html = [];
15979         var records = this.store.getRange();
15980         if(records.length < 1) {
15981             
15982             // is this valid??  = should it render a template??
15983             
15984             this.el.update(this.emptyText);
15985             return;
15986         }
15987         var el = this.el;
15988         if (this.dataName) {
15989             this.el.update(t.apply(this.store.meta)); //????
15990             el = this.el.child('.roo-tpl-' + this.dataName);
15991         }
15992         
15993         for(var i = 0, len = records.length; i < len; i++){
15994             var data = this.prepareData(records[i].data, i, records[i]);
15995             this.fireEvent("preparedata", this, data, i, records[i]);
15996             
15997             var d = Roo.apply({}, data);
15998             
15999             if(this.tickable){
16000                 Roo.apply(d, {'roo-id' : Roo.id()});
16001                 
16002                 var _this = this;
16003             
16004                 Roo.each(this.parent.item, function(item){
16005                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16006                         return;
16007                     }
16008                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16009                 });
16010             }
16011             
16012             html[html.length] = Roo.util.Format.trim(
16013                 this.dataName ?
16014                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16015                     t.apply(d)
16016             );
16017         }
16018         
16019         
16020         
16021         el.update(html.join(""));
16022         this.nodes = el.dom.childNodes;
16023         this.updateIndexes(0);
16024     },
16025     
16026
16027     /**
16028      * Function to override to reformat the data that is sent to
16029      * the template for each node.
16030      * DEPRICATED - use the preparedata event handler.
16031      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16032      * a JSON object for an UpdateManager bound view).
16033      */
16034     prepareData : function(data, index, record)
16035     {
16036         this.fireEvent("preparedata", this, data, index, record);
16037         return data;
16038     },
16039
16040     onUpdate : function(ds, record){
16041         // Roo.log('on update');   
16042         this.clearSelections();
16043         var index = this.store.indexOf(record);
16044         var n = this.nodes[index];
16045         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16046         n.parentNode.removeChild(n);
16047         this.updateIndexes(index, index);
16048     },
16049
16050     
16051     
16052 // --------- FIXME     
16053     onAdd : function(ds, records, index)
16054     {
16055         //Roo.log(['on Add', ds, records, index] );        
16056         this.clearSelections();
16057         if(this.nodes.length == 0){
16058             this.refresh();
16059             return;
16060         }
16061         var n = this.nodes[index];
16062         for(var i = 0, len = records.length; i < len; i++){
16063             var d = this.prepareData(records[i].data, i, records[i]);
16064             if(n){
16065                 this.tpl.insertBefore(n, d);
16066             }else{
16067                 
16068                 this.tpl.append(this.el, d);
16069             }
16070         }
16071         this.updateIndexes(index);
16072     },
16073
16074     onRemove : function(ds, record, index){
16075        // Roo.log('onRemove');
16076         this.clearSelections();
16077         var el = this.dataName  ?
16078             this.el.child('.roo-tpl-' + this.dataName) :
16079             this.el; 
16080         
16081         el.dom.removeChild(this.nodes[index]);
16082         this.updateIndexes(index);
16083     },
16084
16085     /**
16086      * Refresh an individual node.
16087      * @param {Number} index
16088      */
16089     refreshNode : function(index){
16090         this.onUpdate(this.store, this.store.getAt(index));
16091     },
16092
16093     updateIndexes : function(startIndex, endIndex){
16094         var ns = this.nodes;
16095         startIndex = startIndex || 0;
16096         endIndex = endIndex || ns.length - 1;
16097         for(var i = startIndex; i <= endIndex; i++){
16098             ns[i].nodeIndex = i;
16099         }
16100     },
16101
16102     /**
16103      * Changes the data store this view uses and refresh the view.
16104      * @param {Store} store
16105      */
16106     setStore : function(store, initial){
16107         if(!initial && this.store){
16108             this.store.un("datachanged", this.refresh);
16109             this.store.un("add", this.onAdd);
16110             this.store.un("remove", this.onRemove);
16111             this.store.un("update", this.onUpdate);
16112             this.store.un("clear", this.refresh);
16113             this.store.un("beforeload", this.onBeforeLoad);
16114             this.store.un("load", this.onLoad);
16115             this.store.un("loadexception", this.onLoad);
16116         }
16117         if(store){
16118           
16119             store.on("datachanged", this.refresh, this);
16120             store.on("add", this.onAdd, this);
16121             store.on("remove", this.onRemove, this);
16122             store.on("update", this.onUpdate, this);
16123             store.on("clear", this.refresh, this);
16124             store.on("beforeload", this.onBeforeLoad, this);
16125             store.on("load", this.onLoad, this);
16126             store.on("loadexception", this.onLoad, this);
16127         }
16128         
16129         if(store){
16130             this.refresh();
16131         }
16132     },
16133     /**
16134      * onbeforeLoad - masks the loading area.
16135      *
16136      */
16137     onBeforeLoad : function(store,opts)
16138     {
16139          //Roo.log('onBeforeLoad');   
16140         if (!opts.add) {
16141             this.el.update("");
16142         }
16143         this.el.mask(this.mask ? this.mask : "Loading" ); 
16144     },
16145     onLoad : function ()
16146     {
16147         this.el.unmask();
16148     },
16149     
16150
16151     /**
16152      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16153      * @param {HTMLElement} node
16154      * @return {HTMLElement} The template node
16155      */
16156     findItemFromChild : function(node){
16157         var el = this.dataName  ?
16158             this.el.child('.roo-tpl-' + this.dataName,true) :
16159             this.el.dom; 
16160         
16161         if(!node || node.parentNode == el){
16162                     return node;
16163             }
16164             var p = node.parentNode;
16165             while(p && p != el){
16166             if(p.parentNode == el){
16167                 return p;
16168             }
16169             p = p.parentNode;
16170         }
16171             return null;
16172     },
16173
16174     /** @ignore */
16175     onClick : function(e){
16176         var item = this.findItemFromChild(e.getTarget());
16177         if(item){
16178             var index = this.indexOf(item);
16179             if(this.onItemClick(item, index, e) !== false){
16180                 this.fireEvent("click", this, index, item, e);
16181             }
16182         }else{
16183             this.clearSelections();
16184         }
16185     },
16186
16187     /** @ignore */
16188     onContextMenu : function(e){
16189         var item = this.findItemFromChild(e.getTarget());
16190         if(item){
16191             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16192         }
16193     },
16194
16195     /** @ignore */
16196     onDblClick : function(e){
16197         var item = this.findItemFromChild(e.getTarget());
16198         if(item){
16199             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16200         }
16201     },
16202
16203     onItemClick : function(item, index, e)
16204     {
16205         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16206             return false;
16207         }
16208         if (this.toggleSelect) {
16209             var m = this.isSelected(item) ? 'unselect' : 'select';
16210             //Roo.log(m);
16211             var _t = this;
16212             _t[m](item, true, false);
16213             return true;
16214         }
16215         if(this.multiSelect || this.singleSelect){
16216             if(this.multiSelect && e.shiftKey && this.lastSelection){
16217                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16218             }else{
16219                 this.select(item, this.multiSelect && e.ctrlKey);
16220                 this.lastSelection = item;
16221             }
16222             
16223             if(!this.tickable){
16224                 e.preventDefault();
16225             }
16226             
16227         }
16228         return true;
16229     },
16230
16231     /**
16232      * Get the number of selected nodes.
16233      * @return {Number}
16234      */
16235     getSelectionCount : function(){
16236         return this.selections.length;
16237     },
16238
16239     /**
16240      * Get the currently selected nodes.
16241      * @return {Array} An array of HTMLElements
16242      */
16243     getSelectedNodes : function(){
16244         return this.selections;
16245     },
16246
16247     /**
16248      * Get the indexes of the selected nodes.
16249      * @return {Array}
16250      */
16251     getSelectedIndexes : function(){
16252         var indexes = [], s = this.selections;
16253         for(var i = 0, len = s.length; i < len; i++){
16254             indexes.push(s[i].nodeIndex);
16255         }
16256         return indexes;
16257     },
16258
16259     /**
16260      * Clear all selections
16261      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16262      */
16263     clearSelections : function(suppressEvent){
16264         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16265             this.cmp.elements = this.selections;
16266             this.cmp.removeClass(this.selectedClass);
16267             this.selections = [];
16268             if(!suppressEvent){
16269                 this.fireEvent("selectionchange", this, this.selections);
16270             }
16271         }
16272     },
16273
16274     /**
16275      * Returns true if the passed node is selected
16276      * @param {HTMLElement/Number} node The node or node index
16277      * @return {Boolean}
16278      */
16279     isSelected : function(node){
16280         var s = this.selections;
16281         if(s.length < 1){
16282             return false;
16283         }
16284         node = this.getNode(node);
16285         return s.indexOf(node) !== -1;
16286     },
16287
16288     /**
16289      * Selects nodes.
16290      * @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
16291      * @param {Boolean} keepExisting (optional) true to keep existing selections
16292      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16293      */
16294     select : function(nodeInfo, keepExisting, suppressEvent){
16295         if(nodeInfo instanceof Array){
16296             if(!keepExisting){
16297                 this.clearSelections(true);
16298             }
16299             for(var i = 0, len = nodeInfo.length; i < len; i++){
16300                 this.select(nodeInfo[i], true, true);
16301             }
16302             return;
16303         } 
16304         var node = this.getNode(nodeInfo);
16305         if(!node || this.isSelected(node)){
16306             return; // already selected.
16307         }
16308         if(!keepExisting){
16309             this.clearSelections(true);
16310         }
16311         
16312         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16313             Roo.fly(node).addClass(this.selectedClass);
16314             this.selections.push(node);
16315             if(!suppressEvent){
16316                 this.fireEvent("selectionchange", this, this.selections);
16317             }
16318         }
16319         
16320         
16321     },
16322       /**
16323      * Unselects nodes.
16324      * @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
16325      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16326      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16327      */
16328     unselect : function(nodeInfo, keepExisting, suppressEvent)
16329     {
16330         if(nodeInfo instanceof Array){
16331             Roo.each(this.selections, function(s) {
16332                 this.unselect(s, nodeInfo);
16333             }, this);
16334             return;
16335         }
16336         var node = this.getNode(nodeInfo);
16337         if(!node || !this.isSelected(node)){
16338             //Roo.log("not selected");
16339             return; // not selected.
16340         }
16341         // fireevent???
16342         var ns = [];
16343         Roo.each(this.selections, function(s) {
16344             if (s == node ) {
16345                 Roo.fly(node).removeClass(this.selectedClass);
16346
16347                 return;
16348             }
16349             ns.push(s);
16350         },this);
16351         
16352         this.selections= ns;
16353         this.fireEvent("selectionchange", this, this.selections);
16354     },
16355
16356     /**
16357      * Gets a template node.
16358      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16359      * @return {HTMLElement} The node or null if it wasn't found
16360      */
16361     getNode : function(nodeInfo){
16362         if(typeof nodeInfo == "string"){
16363             return document.getElementById(nodeInfo);
16364         }else if(typeof nodeInfo == "number"){
16365             return this.nodes[nodeInfo];
16366         }
16367         return nodeInfo;
16368     },
16369
16370     /**
16371      * Gets a range template nodes.
16372      * @param {Number} startIndex
16373      * @param {Number} endIndex
16374      * @return {Array} An array of nodes
16375      */
16376     getNodes : function(start, end){
16377         var ns = this.nodes;
16378         start = start || 0;
16379         end = typeof end == "undefined" ? ns.length - 1 : end;
16380         var nodes = [];
16381         if(start <= end){
16382             for(var i = start; i <= end; i++){
16383                 nodes.push(ns[i]);
16384             }
16385         } else{
16386             for(var i = start; i >= end; i--){
16387                 nodes.push(ns[i]);
16388             }
16389         }
16390         return nodes;
16391     },
16392
16393     /**
16394      * Finds the index of the passed node
16395      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16396      * @return {Number} The index of the node or -1
16397      */
16398     indexOf : function(node){
16399         node = this.getNode(node);
16400         if(typeof node.nodeIndex == "number"){
16401             return node.nodeIndex;
16402         }
16403         var ns = this.nodes;
16404         for(var i = 0, len = ns.length; i < len; i++){
16405             if(ns[i] == node){
16406                 return i;
16407             }
16408         }
16409         return -1;
16410     }
16411 });
16412 /*
16413  * - LGPL
16414  *
16415  * based on jquery fullcalendar
16416  * 
16417  */
16418
16419 Roo.bootstrap = Roo.bootstrap || {};
16420 /**
16421  * @class Roo.bootstrap.Calendar
16422  * @extends Roo.bootstrap.Component
16423  * Bootstrap Calendar class
16424  * @cfg {Boolean} loadMask (true|false) default false
16425  * @cfg {Object} header generate the user specific header of the calendar, default false
16426
16427  * @constructor
16428  * Create a new Container
16429  * @param {Object} config The config object
16430  */
16431
16432
16433
16434 Roo.bootstrap.Calendar = function(config){
16435     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16436      this.addEvents({
16437         /**
16438              * @event select
16439              * Fires when a date is selected
16440              * @param {DatePicker} this
16441              * @param {Date} date The selected date
16442              */
16443         'select': true,
16444         /**
16445              * @event monthchange
16446              * Fires when the displayed month changes 
16447              * @param {DatePicker} this
16448              * @param {Date} date The selected month
16449              */
16450         'monthchange': true,
16451         /**
16452              * @event evententer
16453              * Fires when mouse over an event
16454              * @param {Calendar} this
16455              * @param {event} Event
16456              */
16457         'evententer': true,
16458         /**
16459              * @event eventleave
16460              * Fires when the mouse leaves an
16461              * @param {Calendar} this
16462              * @param {event}
16463              */
16464         'eventleave': true,
16465         /**
16466              * @event eventclick
16467              * Fires when the mouse click an
16468              * @param {Calendar} this
16469              * @param {event}
16470              */
16471         'eventclick': true
16472         
16473     });
16474
16475 };
16476
16477 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16478     
16479      /**
16480      * @cfg {Number} startDay
16481      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16482      */
16483     startDay : 0,
16484     
16485     loadMask : false,
16486     
16487     header : false,
16488       
16489     getAutoCreate : function(){
16490         
16491         
16492         var fc_button = function(name, corner, style, content ) {
16493             return Roo.apply({},{
16494                 tag : 'span',
16495                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16496                          (corner.length ?
16497                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16498                             ''
16499                         ),
16500                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16501                 unselectable: 'on'
16502             });
16503         };
16504         
16505         var header = {};
16506         
16507         if(!this.header){
16508             header = {
16509                 tag : 'table',
16510                 cls : 'fc-header',
16511                 style : 'width:100%',
16512                 cn : [
16513                     {
16514                         tag: 'tr',
16515                         cn : [
16516                             {
16517                                 tag : 'td',
16518                                 cls : 'fc-header-left',
16519                                 cn : [
16520                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16521                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16522                                     { tag: 'span', cls: 'fc-header-space' },
16523                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16524
16525
16526                                 ]
16527                             },
16528
16529                             {
16530                                 tag : 'td',
16531                                 cls : 'fc-header-center',
16532                                 cn : [
16533                                     {
16534                                         tag: 'span',
16535                                         cls: 'fc-header-title',
16536                                         cn : {
16537                                             tag: 'H2',
16538                                             html : 'month / year'
16539                                         }
16540                                     }
16541
16542                                 ]
16543                             },
16544                             {
16545                                 tag : 'td',
16546                                 cls : 'fc-header-right',
16547                                 cn : [
16548                               /*      fc_button('month', 'left', '', 'month' ),
16549                                     fc_button('week', '', '', 'week' ),
16550                                     fc_button('day', 'right', '', 'day' )
16551                                 */    
16552
16553                                 ]
16554                             }
16555
16556                         ]
16557                     }
16558                 ]
16559             };
16560         }
16561         
16562         header = this.header;
16563         
16564        
16565         var cal_heads = function() {
16566             var ret = [];
16567             // fixme - handle this.
16568             
16569             for (var i =0; i < Date.dayNames.length; i++) {
16570                 var d = Date.dayNames[i];
16571                 ret.push({
16572                     tag: 'th',
16573                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16574                     html : d.substring(0,3)
16575                 });
16576                 
16577             }
16578             ret[0].cls += ' fc-first';
16579             ret[6].cls += ' fc-last';
16580             return ret;
16581         };
16582         var cal_cell = function(n) {
16583             return  {
16584                 tag: 'td',
16585                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16586                 cn : [
16587                     {
16588                         cn : [
16589                             {
16590                                 cls: 'fc-day-number',
16591                                 html: 'D'
16592                             },
16593                             {
16594                                 cls: 'fc-day-content',
16595                              
16596                                 cn : [
16597                                      {
16598                                         style: 'position: relative;' // height: 17px;
16599                                     }
16600                                 ]
16601                             }
16602                             
16603                             
16604                         ]
16605                     }
16606                 ]
16607                 
16608             }
16609         };
16610         var cal_rows = function() {
16611             
16612             var ret = [];
16613             for (var r = 0; r < 6; r++) {
16614                 var row= {
16615                     tag : 'tr',
16616                     cls : 'fc-week',
16617                     cn : []
16618                 };
16619                 
16620                 for (var i =0; i < Date.dayNames.length; i++) {
16621                     var d = Date.dayNames[i];
16622                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16623
16624                 }
16625                 row.cn[0].cls+=' fc-first';
16626                 row.cn[0].cn[0].style = 'min-height:90px';
16627                 row.cn[6].cls+=' fc-last';
16628                 ret.push(row);
16629                 
16630             }
16631             ret[0].cls += ' fc-first';
16632             ret[4].cls += ' fc-prev-last';
16633             ret[5].cls += ' fc-last';
16634             return ret;
16635             
16636         };
16637         
16638         var cal_table = {
16639             tag: 'table',
16640             cls: 'fc-border-separate',
16641             style : 'width:100%',
16642             cellspacing  : 0,
16643             cn : [
16644                 { 
16645                     tag: 'thead',
16646                     cn : [
16647                         { 
16648                             tag: 'tr',
16649                             cls : 'fc-first fc-last',
16650                             cn : cal_heads()
16651                         }
16652                     ]
16653                 },
16654                 { 
16655                     tag: 'tbody',
16656                     cn : cal_rows()
16657                 }
16658                   
16659             ]
16660         };
16661          
16662          var cfg = {
16663             cls : 'fc fc-ltr',
16664             cn : [
16665                 header,
16666                 {
16667                     cls : 'fc-content',
16668                     style : "position: relative;",
16669                     cn : [
16670                         {
16671                             cls : 'fc-view fc-view-month fc-grid',
16672                             style : 'position: relative',
16673                             unselectable : 'on',
16674                             cn : [
16675                                 {
16676                                     cls : 'fc-event-container',
16677                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16678                                 },
16679                                 cal_table
16680                             ]
16681                         }
16682                     ]
16683     
16684                 }
16685            ] 
16686             
16687         };
16688         
16689          
16690         
16691         return cfg;
16692     },
16693     
16694     
16695     initEvents : function()
16696     {
16697         if(!this.store){
16698             throw "can not find store for calendar";
16699         }
16700         
16701         var mark = {
16702             tag: "div",
16703             cls:"x-dlg-mask",
16704             style: "text-align:center",
16705             cn: [
16706                 {
16707                     tag: "div",
16708                     style: "background-color:white;width:50%;margin:250 auto",
16709                     cn: [
16710                         {
16711                             tag: "img",
16712                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16713                         },
16714                         {
16715                             tag: "span",
16716                             html: "Loading"
16717                         }
16718                         
16719                     ]
16720                 }
16721             ]
16722         };
16723         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16724         
16725         var size = this.el.select('.fc-content', true).first().getSize();
16726         this.maskEl.setSize(size.width, size.height);
16727         this.maskEl.enableDisplayMode("block");
16728         if(!this.loadMask){
16729             this.maskEl.hide();
16730         }
16731         
16732         this.store = Roo.factory(this.store, Roo.data);
16733         this.store.on('load', this.onLoad, this);
16734         this.store.on('beforeload', this.onBeforeLoad, this);
16735         
16736         this.resize();
16737         
16738         this.cells = this.el.select('.fc-day',true);
16739         //Roo.log(this.cells);
16740         this.textNodes = this.el.query('.fc-day-number');
16741         this.cells.addClassOnOver('fc-state-hover');
16742         
16743         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16744         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16745         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16746         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16747         
16748         this.on('monthchange', this.onMonthChange, this);
16749         
16750         this.update(new Date().clearTime());
16751     },
16752     
16753     resize : function() {
16754         var sz  = this.el.getSize();
16755         
16756         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16757         this.el.select('.fc-day-content div',true).setHeight(34);
16758     },
16759     
16760     
16761     // private
16762     showPrevMonth : function(e){
16763         this.update(this.activeDate.add("mo", -1));
16764     },
16765     showToday : function(e){
16766         this.update(new Date().clearTime());
16767     },
16768     // private
16769     showNextMonth : function(e){
16770         this.update(this.activeDate.add("mo", 1));
16771     },
16772
16773     // private
16774     showPrevYear : function(){
16775         this.update(this.activeDate.add("y", -1));
16776     },
16777
16778     // private
16779     showNextYear : function(){
16780         this.update(this.activeDate.add("y", 1));
16781     },
16782
16783     
16784    // private
16785     update : function(date)
16786     {
16787         var vd = this.activeDate;
16788         this.activeDate = date;
16789 //        if(vd && this.el){
16790 //            var t = date.getTime();
16791 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16792 //                Roo.log('using add remove');
16793 //                
16794 //                this.fireEvent('monthchange', this, date);
16795 //                
16796 //                this.cells.removeClass("fc-state-highlight");
16797 //                this.cells.each(function(c){
16798 //                   if(c.dateValue == t){
16799 //                       c.addClass("fc-state-highlight");
16800 //                       setTimeout(function(){
16801 //                            try{c.dom.firstChild.focus();}catch(e){}
16802 //                       }, 50);
16803 //                       return false;
16804 //                   }
16805 //                   return true;
16806 //                });
16807 //                return;
16808 //            }
16809 //        }
16810         
16811         var days = date.getDaysInMonth();
16812         
16813         var firstOfMonth = date.getFirstDateOfMonth();
16814         var startingPos = firstOfMonth.getDay()-this.startDay;
16815         
16816         if(startingPos < this.startDay){
16817             startingPos += 7;
16818         }
16819         
16820         var pm = date.add(Date.MONTH, -1);
16821         var prevStart = pm.getDaysInMonth()-startingPos;
16822 //        
16823         this.cells = this.el.select('.fc-day',true);
16824         this.textNodes = this.el.query('.fc-day-number');
16825         this.cells.addClassOnOver('fc-state-hover');
16826         
16827         var cells = this.cells.elements;
16828         var textEls = this.textNodes;
16829         
16830         Roo.each(cells, function(cell){
16831             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16832         });
16833         
16834         days += startingPos;
16835
16836         // convert everything to numbers so it's fast
16837         var day = 86400000;
16838         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16839         //Roo.log(d);
16840         //Roo.log(pm);
16841         //Roo.log(prevStart);
16842         
16843         var today = new Date().clearTime().getTime();
16844         var sel = date.clearTime().getTime();
16845         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16846         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16847         var ddMatch = this.disabledDatesRE;
16848         var ddText = this.disabledDatesText;
16849         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16850         var ddaysText = this.disabledDaysText;
16851         var format = this.format;
16852         
16853         var setCellClass = function(cal, cell){
16854             cell.row = 0;
16855             cell.events = [];
16856             cell.more = [];
16857             //Roo.log('set Cell Class');
16858             cell.title = "";
16859             var t = d.getTime();
16860             
16861             //Roo.log(d);
16862             
16863             cell.dateValue = t;
16864             if(t == today){
16865                 cell.className += " fc-today";
16866                 cell.className += " fc-state-highlight";
16867                 cell.title = cal.todayText;
16868             }
16869             if(t == sel){
16870                 // disable highlight in other month..
16871                 //cell.className += " fc-state-highlight";
16872                 
16873             }
16874             // disabling
16875             if(t < min) {
16876                 cell.className = " fc-state-disabled";
16877                 cell.title = cal.minText;
16878                 return;
16879             }
16880             if(t > max) {
16881                 cell.className = " fc-state-disabled";
16882                 cell.title = cal.maxText;
16883                 return;
16884             }
16885             if(ddays){
16886                 if(ddays.indexOf(d.getDay()) != -1){
16887                     cell.title = ddaysText;
16888                     cell.className = " fc-state-disabled";
16889                 }
16890             }
16891             if(ddMatch && format){
16892                 var fvalue = d.dateFormat(format);
16893                 if(ddMatch.test(fvalue)){
16894                     cell.title = ddText.replace("%0", fvalue);
16895                     cell.className = " fc-state-disabled";
16896                 }
16897             }
16898             
16899             if (!cell.initialClassName) {
16900                 cell.initialClassName = cell.dom.className;
16901             }
16902             
16903             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16904         };
16905
16906         var i = 0;
16907         
16908         for(; i < startingPos; i++) {
16909             textEls[i].innerHTML = (++prevStart);
16910             d.setDate(d.getDate()+1);
16911             
16912             cells[i].className = "fc-past fc-other-month";
16913             setCellClass(this, cells[i]);
16914         }
16915         
16916         var intDay = 0;
16917         
16918         for(; i < days; i++){
16919             intDay = i - startingPos + 1;
16920             textEls[i].innerHTML = (intDay);
16921             d.setDate(d.getDate()+1);
16922             
16923             cells[i].className = ''; // "x-date-active";
16924             setCellClass(this, cells[i]);
16925         }
16926         var extraDays = 0;
16927         
16928         for(; i < 42; i++) {
16929             textEls[i].innerHTML = (++extraDays);
16930             d.setDate(d.getDate()+1);
16931             
16932             cells[i].className = "fc-future fc-other-month";
16933             setCellClass(this, cells[i]);
16934         }
16935         
16936         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16937         
16938         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16939         
16940         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16941         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16942         
16943         if(totalRows != 6){
16944             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16945             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16946         }
16947         
16948         this.fireEvent('monthchange', this, date);
16949         
16950         
16951         /*
16952         if(!this.internalRender){
16953             var main = this.el.dom.firstChild;
16954             var w = main.offsetWidth;
16955             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16956             Roo.fly(main).setWidth(w);
16957             this.internalRender = true;
16958             // opera does not respect the auto grow header center column
16959             // then, after it gets a width opera refuses to recalculate
16960             // without a second pass
16961             if(Roo.isOpera && !this.secondPass){
16962                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16963                 this.secondPass = true;
16964                 this.update.defer(10, this, [date]);
16965             }
16966         }
16967         */
16968         
16969     },
16970     
16971     findCell : function(dt) {
16972         dt = dt.clearTime().getTime();
16973         var ret = false;
16974         this.cells.each(function(c){
16975             //Roo.log("check " +c.dateValue + '?=' + dt);
16976             if(c.dateValue == dt){
16977                 ret = c;
16978                 return false;
16979             }
16980             return true;
16981         });
16982         
16983         return ret;
16984     },
16985     
16986     findCells : function(ev) {
16987         var s = ev.start.clone().clearTime().getTime();
16988        // Roo.log(s);
16989         var e= ev.end.clone().clearTime().getTime();
16990        // Roo.log(e);
16991         var ret = [];
16992         this.cells.each(function(c){
16993              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16994             
16995             if(c.dateValue > e){
16996                 return ;
16997             }
16998             if(c.dateValue < s){
16999                 return ;
17000             }
17001             ret.push(c);
17002         });
17003         
17004         return ret;    
17005     },
17006     
17007 //    findBestRow: function(cells)
17008 //    {
17009 //        var ret = 0;
17010 //        
17011 //        for (var i =0 ; i < cells.length;i++) {
17012 //            ret  = Math.max(cells[i].rows || 0,ret);
17013 //        }
17014 //        return ret;
17015 //        
17016 //    },
17017     
17018     
17019     addItem : function(ev)
17020     {
17021         // look for vertical location slot in
17022         var cells = this.findCells(ev);
17023         
17024 //        ev.row = this.findBestRow(cells);
17025         
17026         // work out the location.
17027         
17028         var crow = false;
17029         var rows = [];
17030         for(var i =0; i < cells.length; i++) {
17031             
17032             cells[i].row = cells[0].row;
17033             
17034             if(i == 0){
17035                 cells[i].row = cells[i].row + 1;
17036             }
17037             
17038             if (!crow) {
17039                 crow = {
17040                     start : cells[i],
17041                     end :  cells[i]
17042                 };
17043                 continue;
17044             }
17045             if (crow.start.getY() == cells[i].getY()) {
17046                 // on same row.
17047                 crow.end = cells[i];
17048                 continue;
17049             }
17050             // different row.
17051             rows.push(crow);
17052             crow = {
17053                 start: cells[i],
17054                 end : cells[i]
17055             };
17056             
17057         }
17058         
17059         rows.push(crow);
17060         ev.els = [];
17061         ev.rows = rows;
17062         ev.cells = cells;
17063         
17064         cells[0].events.push(ev);
17065         
17066         this.calevents.push(ev);
17067     },
17068     
17069     clearEvents: function() {
17070         
17071         if(!this.calevents){
17072             return;
17073         }
17074         
17075         Roo.each(this.cells.elements, function(c){
17076             c.row = 0;
17077             c.events = [];
17078             c.more = [];
17079         });
17080         
17081         Roo.each(this.calevents, function(e) {
17082             Roo.each(e.els, function(el) {
17083                 el.un('mouseenter' ,this.onEventEnter, this);
17084                 el.un('mouseleave' ,this.onEventLeave, this);
17085                 el.remove();
17086             },this);
17087         },this);
17088         
17089         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17090             e.remove();
17091         });
17092         
17093     },
17094     
17095     renderEvents: function()
17096     {   
17097         var _this = this;
17098         
17099         this.cells.each(function(c) {
17100             
17101             if(c.row < 5){
17102                 return;
17103             }
17104             
17105             var ev = c.events;
17106             
17107             var r = 4;
17108             if(c.row != c.events.length){
17109                 r = 4 - (4 - (c.row - c.events.length));
17110             }
17111             
17112             c.events = ev.slice(0, r);
17113             c.more = ev.slice(r);
17114             
17115             if(c.more.length && c.more.length == 1){
17116                 c.events.push(c.more.pop());
17117             }
17118             
17119             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17120             
17121         });
17122             
17123         this.cells.each(function(c) {
17124             
17125             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17126             
17127             
17128             for (var e = 0; e < c.events.length; e++){
17129                 var ev = c.events[e];
17130                 var rows = ev.rows;
17131                 
17132                 for(var i = 0; i < rows.length; i++) {
17133                 
17134                     // how many rows should it span..
17135
17136                     var  cfg = {
17137                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17138                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17139
17140                         unselectable : "on",
17141                         cn : [
17142                             {
17143                                 cls: 'fc-event-inner',
17144                                 cn : [
17145     //                                {
17146     //                                  tag:'span',
17147     //                                  cls: 'fc-event-time',
17148     //                                  html : cells.length > 1 ? '' : ev.time
17149     //                                },
17150                                     {
17151                                       tag:'span',
17152                                       cls: 'fc-event-title',
17153                                       html : String.format('{0}', ev.title)
17154                                     }
17155
17156
17157                                 ]
17158                             },
17159                             {
17160                                 cls: 'ui-resizable-handle ui-resizable-e',
17161                                 html : '&nbsp;&nbsp;&nbsp'
17162                             }
17163
17164                         ]
17165                     };
17166
17167                     if (i == 0) {
17168                         cfg.cls += ' fc-event-start';
17169                     }
17170                     if ((i+1) == rows.length) {
17171                         cfg.cls += ' fc-event-end';
17172                     }
17173
17174                     var ctr = _this.el.select('.fc-event-container',true).first();
17175                     var cg = ctr.createChild(cfg);
17176
17177                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17178                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17179
17180                     var r = (c.more.length) ? 1 : 0;
17181                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17182                     cg.setWidth(ebox.right - sbox.x -2);
17183
17184                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17185                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17186                     cg.on('click', _this.onEventClick, _this, ev);
17187
17188                     ev.els.push(cg);
17189                     
17190                 }
17191                 
17192             }
17193             
17194             
17195             if(c.more.length){
17196                 var  cfg = {
17197                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17198                     style : 'position: absolute',
17199                     unselectable : "on",
17200                     cn : [
17201                         {
17202                             cls: 'fc-event-inner',
17203                             cn : [
17204                                 {
17205                                   tag:'span',
17206                                   cls: 'fc-event-title',
17207                                   html : 'More'
17208                                 }
17209
17210
17211                             ]
17212                         },
17213                         {
17214                             cls: 'ui-resizable-handle ui-resizable-e',
17215                             html : '&nbsp;&nbsp;&nbsp'
17216                         }
17217
17218                     ]
17219                 };
17220
17221                 var ctr = _this.el.select('.fc-event-container',true).first();
17222                 var cg = ctr.createChild(cfg);
17223
17224                 var sbox = c.select('.fc-day-content',true).first().getBox();
17225                 var ebox = c.select('.fc-day-content',true).first().getBox();
17226                 //Roo.log(cg);
17227                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17228                 cg.setWidth(ebox.right - sbox.x -2);
17229
17230                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17231                 
17232             }
17233             
17234         });
17235         
17236         
17237         
17238     },
17239     
17240     onEventEnter: function (e, el,event,d) {
17241         this.fireEvent('evententer', this, el, event);
17242     },
17243     
17244     onEventLeave: function (e, el,event,d) {
17245         this.fireEvent('eventleave', this, el, event);
17246     },
17247     
17248     onEventClick: function (e, el,event,d) {
17249         this.fireEvent('eventclick', this, el, event);
17250     },
17251     
17252     onMonthChange: function () {
17253         this.store.load();
17254     },
17255     
17256     onMoreEventClick: function(e, el, more)
17257     {
17258         var _this = this;
17259         
17260         this.calpopover.placement = 'right';
17261         this.calpopover.setTitle('More');
17262         
17263         this.calpopover.setContent('');
17264         
17265         var ctr = this.calpopover.el.select('.popover-content', true).first();
17266         
17267         Roo.each(more, function(m){
17268             var cfg = {
17269                 cls : 'fc-event-hori fc-event-draggable',
17270                 html : m.title
17271             };
17272             var cg = ctr.createChild(cfg);
17273             
17274             cg.on('click', _this.onEventClick, _this, m);
17275         });
17276         
17277         this.calpopover.show(el);
17278         
17279         
17280     },
17281     
17282     onLoad: function () 
17283     {   
17284         this.calevents = [];
17285         var cal = this;
17286         
17287         if(this.store.getCount() > 0){
17288             this.store.data.each(function(d){
17289                cal.addItem({
17290                     id : d.data.id,
17291                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17292                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17293                     time : d.data.start_time,
17294                     title : d.data.title,
17295                     description : d.data.description,
17296                     venue : d.data.venue
17297                 });
17298             });
17299         }
17300         
17301         this.renderEvents();
17302         
17303         if(this.calevents.length && this.loadMask){
17304             this.maskEl.hide();
17305         }
17306     },
17307     
17308     onBeforeLoad: function()
17309     {
17310         this.clearEvents();
17311         if(this.loadMask){
17312             this.maskEl.show();
17313         }
17314     }
17315 });
17316
17317  
17318  /*
17319  * - LGPL
17320  *
17321  * element
17322  * 
17323  */
17324
17325 /**
17326  * @class Roo.bootstrap.Popover
17327  * @extends Roo.bootstrap.Component
17328  * Bootstrap Popover class
17329  * @cfg {String} html contents of the popover   (or false to use children..)
17330  * @cfg {String} title of popover (or false to hide)
17331  * @cfg {String} placement how it is placed
17332  * @cfg {String} trigger click || hover (or false to trigger manually)
17333  * @cfg {String} over what (parent or false to trigger manually.)
17334  * @cfg {Number} delay - delay before showing
17335  
17336  * @constructor
17337  * Create a new Popover
17338  * @param {Object} config The config object
17339  */
17340
17341 Roo.bootstrap.Popover = function(config){
17342     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17343     
17344     this.addEvents({
17345         // raw events
17346          /**
17347          * @event show
17348          * After the popover show
17349          * 
17350          * @param {Roo.bootstrap.Popover} this
17351          */
17352         "show" : true,
17353         /**
17354          * @event hide
17355          * After the popover hide
17356          * 
17357          * @param {Roo.bootstrap.Popover} this
17358          */
17359         "hide" : true
17360     });
17361 };
17362
17363 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17364     
17365     title: 'Fill in a title',
17366     html: false,
17367     
17368     placement : 'right',
17369     trigger : 'hover', // hover
17370     
17371     delay : 0,
17372     
17373     over: 'parent',
17374     
17375     can_build_overlaid : false,
17376     
17377     getChildContainer : function()
17378     {
17379         return this.el.select('.popover-content',true).first();
17380     },
17381     
17382     getAutoCreate : function(){
17383          
17384         var cfg = {
17385            cls : 'popover roo-dynamic',
17386            style: 'display:block',
17387            cn : [
17388                 {
17389                     cls : 'arrow'
17390                 },
17391                 {
17392                     cls : 'popover-inner',
17393                     cn : [
17394                         {
17395                             tag: 'h3',
17396                             cls: 'popover-title',
17397                             html : this.title
17398                         },
17399                         {
17400                             cls : 'popover-content',
17401                             html : this.html
17402                         }
17403                     ]
17404                     
17405                 }
17406            ]
17407         };
17408         
17409         return cfg;
17410     },
17411     setTitle: function(str)
17412     {
17413         this.title = str;
17414         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17415     },
17416     setContent: function(str)
17417     {
17418         this.html = str;
17419         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17420     },
17421     // as it get's added to the bottom of the page.
17422     onRender : function(ct, position)
17423     {
17424         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17425         if(!this.el){
17426             var cfg = Roo.apply({},  this.getAutoCreate());
17427             cfg.id = Roo.id();
17428             
17429             if (this.cls) {
17430                 cfg.cls += ' ' + this.cls;
17431             }
17432             if (this.style) {
17433                 cfg.style = this.style;
17434             }
17435             //Roo.log("adding to ");
17436             this.el = Roo.get(document.body).createChild(cfg, position);
17437 //            Roo.log(this.el);
17438         }
17439         this.initEvents();
17440     },
17441     
17442     initEvents : function()
17443     {
17444         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17445         this.el.enableDisplayMode('block');
17446         this.el.hide();
17447         if (this.over === false) {
17448             return; 
17449         }
17450         if (this.triggers === false) {
17451             return;
17452         }
17453         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17454         var triggers = this.trigger ? this.trigger.split(' ') : [];
17455         Roo.each(triggers, function(trigger) {
17456         
17457             if (trigger == 'click') {
17458                 on_el.on('click', this.toggle, this);
17459             } else if (trigger != 'manual') {
17460                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17461                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17462       
17463                 on_el.on(eventIn  ,this.enter, this);
17464                 on_el.on(eventOut, this.leave, this);
17465             }
17466         }, this);
17467         
17468     },
17469     
17470     
17471     // private
17472     timeout : null,
17473     hoverState : null,
17474     
17475     toggle : function () {
17476         this.hoverState == 'in' ? this.leave() : this.enter();
17477     },
17478     
17479     enter : function () {
17480         
17481         clearTimeout(this.timeout);
17482     
17483         this.hoverState = 'in';
17484     
17485         if (!this.delay || !this.delay.show) {
17486             this.show();
17487             return;
17488         }
17489         var _t = this;
17490         this.timeout = setTimeout(function () {
17491             if (_t.hoverState == 'in') {
17492                 _t.show();
17493             }
17494         }, this.delay.show)
17495     },
17496     
17497     leave : function() {
17498         clearTimeout(this.timeout);
17499     
17500         this.hoverState = 'out';
17501     
17502         if (!this.delay || !this.delay.hide) {
17503             this.hide();
17504             return;
17505         }
17506         var _t = this;
17507         this.timeout = setTimeout(function () {
17508             if (_t.hoverState == 'out') {
17509                 _t.hide();
17510             }
17511         }, this.delay.hide)
17512     },
17513     
17514     show : function (on_el)
17515     {
17516         if (!on_el) {
17517             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17518         }
17519         
17520         // set content.
17521         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17522         if (this.html !== false) {
17523             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17524         }
17525         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17526         if (!this.title.length) {
17527             this.el.select('.popover-title',true).hide();
17528         }
17529         
17530         var placement = typeof this.placement == 'function' ?
17531             this.placement.call(this, this.el, on_el) :
17532             this.placement;
17533             
17534         var autoToken = /\s?auto?\s?/i;
17535         var autoPlace = autoToken.test(placement);
17536         if (autoPlace) {
17537             placement = placement.replace(autoToken, '') || 'top';
17538         }
17539         
17540         //this.el.detach()
17541         //this.el.setXY([0,0]);
17542         this.el.show();
17543         this.el.dom.style.display='block';
17544         this.el.addClass(placement);
17545         
17546         //this.el.appendTo(on_el);
17547         
17548         var p = this.getPosition();
17549         var box = this.el.getBox();
17550         
17551         if (autoPlace) {
17552             // fixme..
17553         }
17554         var align = Roo.bootstrap.Popover.alignment[placement];
17555         
17556 //        Roo.log(align);
17557         this.el.alignTo(on_el, align[0],align[1]);
17558         //var arrow = this.el.select('.arrow',true).first();
17559         //arrow.set(align[2], 
17560         
17561         this.el.addClass('in');
17562         
17563         
17564         if (this.el.hasClass('fade')) {
17565             // fade it?
17566         }
17567         
17568         this.hoverState = 'in';
17569         
17570         this.fireEvent('show', this);
17571         
17572     },
17573     hide : function()
17574     {
17575         this.el.setXY([0,0]);
17576         this.el.removeClass('in');
17577         this.el.hide();
17578         this.hoverState = null;
17579         
17580         this.fireEvent('hide', this);
17581     }
17582     
17583 });
17584
17585 Roo.bootstrap.Popover.alignment = {
17586     'left' : ['r-l', [-10,0], 'right'],
17587     'right' : ['l-r', [10,0], 'left'],
17588     'bottom' : ['t-b', [0,10], 'top'],
17589     'top' : [ 'b-t', [0,-10], 'bottom']
17590 };
17591
17592  /*
17593  * - LGPL
17594  *
17595  * Progress
17596  * 
17597  */
17598
17599 /**
17600  * @class Roo.bootstrap.Progress
17601  * @extends Roo.bootstrap.Component
17602  * Bootstrap Progress class
17603  * @cfg {Boolean} striped striped of the progress bar
17604  * @cfg {Boolean} active animated of the progress bar
17605  * 
17606  * 
17607  * @constructor
17608  * Create a new Progress
17609  * @param {Object} config The config object
17610  */
17611
17612 Roo.bootstrap.Progress = function(config){
17613     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17614 };
17615
17616 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17617     
17618     striped : false,
17619     active: false,
17620     
17621     getAutoCreate : function(){
17622         var cfg = {
17623             tag: 'div',
17624             cls: 'progress'
17625         };
17626         
17627         
17628         if(this.striped){
17629             cfg.cls += ' progress-striped';
17630         }
17631       
17632         if(this.active){
17633             cfg.cls += ' active';
17634         }
17635         
17636         
17637         return cfg;
17638     }
17639    
17640 });
17641
17642  
17643
17644  /*
17645  * - LGPL
17646  *
17647  * ProgressBar
17648  * 
17649  */
17650
17651 /**
17652  * @class Roo.bootstrap.ProgressBar
17653  * @extends Roo.bootstrap.Component
17654  * Bootstrap ProgressBar class
17655  * @cfg {Number} aria_valuenow aria-value now
17656  * @cfg {Number} aria_valuemin aria-value min
17657  * @cfg {Number} aria_valuemax aria-value max
17658  * @cfg {String} label label for the progress bar
17659  * @cfg {String} panel (success | info | warning | danger )
17660  * @cfg {String} role role of the progress bar
17661  * @cfg {String} sr_only text
17662  * 
17663  * 
17664  * @constructor
17665  * Create a new ProgressBar
17666  * @param {Object} config The config object
17667  */
17668
17669 Roo.bootstrap.ProgressBar = function(config){
17670     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17671 };
17672
17673 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17674     
17675     aria_valuenow : 0,
17676     aria_valuemin : 0,
17677     aria_valuemax : 100,
17678     label : false,
17679     panel : false,
17680     role : false,
17681     sr_only: false,
17682     
17683     getAutoCreate : function()
17684     {
17685         
17686         var cfg = {
17687             tag: 'div',
17688             cls: 'progress-bar',
17689             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17690         };
17691         
17692         if(this.sr_only){
17693             cfg.cn = {
17694                 tag: 'span',
17695                 cls: 'sr-only',
17696                 html: this.sr_only
17697             }
17698         }
17699         
17700         if(this.role){
17701             cfg.role = this.role;
17702         }
17703         
17704         if(this.aria_valuenow){
17705             cfg['aria-valuenow'] = this.aria_valuenow;
17706         }
17707         
17708         if(this.aria_valuemin){
17709             cfg['aria-valuemin'] = this.aria_valuemin;
17710         }
17711         
17712         if(this.aria_valuemax){
17713             cfg['aria-valuemax'] = this.aria_valuemax;
17714         }
17715         
17716         if(this.label && !this.sr_only){
17717             cfg.html = this.label;
17718         }
17719         
17720         if(this.panel){
17721             cfg.cls += ' progress-bar-' + this.panel;
17722         }
17723         
17724         return cfg;
17725     },
17726     
17727     update : function(aria_valuenow)
17728     {
17729         this.aria_valuenow = aria_valuenow;
17730         
17731         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17732     }
17733    
17734 });
17735
17736  
17737
17738  /*
17739  * - LGPL
17740  *
17741  * column
17742  * 
17743  */
17744
17745 /**
17746  * @class Roo.bootstrap.TabGroup
17747  * @extends Roo.bootstrap.Column
17748  * Bootstrap Column class
17749  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17750  * @cfg {Boolean} carousel true to make the group behave like a carousel
17751  * @cfg {Boolean} bullets show bullets for the panels
17752  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17753  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17754  * @cfg {Boolean} showarrow (true|false) show arrow default true
17755  * 
17756  * @constructor
17757  * Create a new TabGroup
17758  * @param {Object} config The config object
17759  */
17760
17761 Roo.bootstrap.TabGroup = function(config){
17762     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17763     if (!this.navId) {
17764         this.navId = Roo.id();
17765     }
17766     this.tabs = [];
17767     Roo.bootstrap.TabGroup.register(this);
17768     
17769 };
17770
17771 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17772     
17773     carousel : false,
17774     transition : false,
17775     bullets : 0,
17776     timer : 0,
17777     autoslide : false,
17778     slideFn : false,
17779     slideOnTouch : false,
17780     showarrow : true,
17781     
17782     getAutoCreate : function()
17783     {
17784         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17785         
17786         cfg.cls += ' tab-content';
17787         
17788         if (this.carousel) {
17789             cfg.cls += ' carousel slide';
17790             
17791             cfg.cn = [{
17792                cls : 'carousel-inner',
17793                cn : []
17794             }];
17795         
17796             if(this.bullets  && !Roo.isTouch){
17797                 
17798                 var bullets = {
17799                     cls : 'carousel-bullets',
17800                     cn : []
17801                 };
17802                
17803                 if(this.bullets_cls){
17804                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17805                 }
17806                 
17807                 bullets.cn.push({
17808                     cls : 'clear'
17809                 });
17810                 
17811                 cfg.cn[0].cn.push(bullets);
17812             }
17813             
17814             if(this.showarrow){
17815                 cfg.cn[0].cn.push({
17816                     tag : 'div',
17817                     class : 'carousel-arrow',
17818                     cn : [
17819                         {
17820                             tag : 'div',
17821                             class : 'carousel-prev',
17822                             cn : [
17823                                 {
17824                                     tag : 'i',
17825                                     class : 'fa fa-chevron-left'
17826                                 }
17827                             ]
17828                         },
17829                         {
17830                             tag : 'div',
17831                             class : 'carousel-next',
17832                             cn : [
17833                                 {
17834                                     tag : 'i',
17835                                     class : 'fa fa-chevron-right'
17836                                 }
17837                             ]
17838                         }
17839                     ]
17840                 });
17841             }
17842             
17843         }
17844         
17845         return cfg;
17846     },
17847     
17848     initEvents:  function()
17849     {
17850 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17851 //            this.el.on("touchstart", this.onTouchStart, this);
17852 //        }
17853         
17854         if(this.autoslide){
17855             var _this = this;
17856             
17857             this.slideFn = window.setInterval(function() {
17858                 _this.showPanelNext();
17859             }, this.timer);
17860         }
17861         
17862         if(this.showarrow){
17863             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17864             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17865         }
17866         
17867         
17868     },
17869     
17870 //    onTouchStart : function(e, el, o)
17871 //    {
17872 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17873 //            return;
17874 //        }
17875 //        
17876 //        this.showPanelNext();
17877 //    },
17878     
17879     
17880     getChildContainer : function()
17881     {
17882         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17883     },
17884     
17885     /**
17886     * register a Navigation item
17887     * @param {Roo.bootstrap.NavItem} the navitem to add
17888     */
17889     register : function(item)
17890     {
17891         this.tabs.push( item);
17892         item.navId = this.navId; // not really needed..
17893         this.addBullet();
17894     
17895     },
17896     
17897     getActivePanel : function()
17898     {
17899         var r = false;
17900         Roo.each(this.tabs, function(t) {
17901             if (t.active) {
17902                 r = t;
17903                 return false;
17904             }
17905             return null;
17906         });
17907         return r;
17908         
17909     },
17910     getPanelByName : function(n)
17911     {
17912         var r = false;
17913         Roo.each(this.tabs, function(t) {
17914             if (t.tabId == n) {
17915                 r = t;
17916                 return false;
17917             }
17918             return null;
17919         });
17920         return r;
17921     },
17922     indexOfPanel : function(p)
17923     {
17924         var r = false;
17925         Roo.each(this.tabs, function(t,i) {
17926             if (t.tabId == p.tabId) {
17927                 r = i;
17928                 return false;
17929             }
17930             return null;
17931         });
17932         return r;
17933     },
17934     /**
17935      * show a specific panel
17936      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17937      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17938      */
17939     showPanel : function (pan)
17940     {
17941         if(this.transition || typeof(pan) == 'undefined'){
17942             Roo.log("waiting for the transitionend");
17943             return;
17944         }
17945         
17946         if (typeof(pan) == 'number') {
17947             pan = this.tabs[pan];
17948         }
17949         
17950         if (typeof(pan) == 'string') {
17951             pan = this.getPanelByName(pan);
17952         }
17953         
17954         var cur = this.getActivePanel();
17955         
17956         if(!pan || !cur){
17957             Roo.log('pan or acitve pan is undefined');
17958             return false;
17959         }
17960         
17961         if (pan.tabId == this.getActivePanel().tabId) {
17962             return true;
17963         }
17964         
17965         if (false === cur.fireEvent('beforedeactivate')) {
17966             return false;
17967         }
17968         
17969         if(this.bullets > 0 && !Roo.isTouch){
17970             this.setActiveBullet(this.indexOfPanel(pan));
17971         }
17972         
17973         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17974             
17975             this.transition = true;
17976             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17977             var lr = dir == 'next' ? 'left' : 'right';
17978             pan.el.addClass(dir); // or prev
17979             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17980             cur.el.addClass(lr); // or right
17981             pan.el.addClass(lr);
17982             
17983             var _this = this;
17984             cur.el.on('transitionend', function() {
17985                 Roo.log("trans end?");
17986                 
17987                 pan.el.removeClass([lr,dir]);
17988                 pan.setActive(true);
17989                 
17990                 cur.el.removeClass([lr]);
17991                 cur.setActive(false);
17992                 
17993                 _this.transition = false;
17994                 
17995             }, this, { single:  true } );
17996             
17997             return true;
17998         }
17999         
18000         cur.setActive(false);
18001         pan.setActive(true);
18002         
18003         return true;
18004         
18005     },
18006     showPanelNext : function()
18007     {
18008         var i = this.indexOfPanel(this.getActivePanel());
18009         
18010         if (i >= this.tabs.length - 1 && !this.autoslide) {
18011             return;
18012         }
18013         
18014         if (i >= this.tabs.length - 1 && this.autoslide) {
18015             i = -1;
18016         }
18017         
18018         this.showPanel(this.tabs[i+1]);
18019     },
18020     
18021     showPanelPrev : function()
18022     {
18023         var i = this.indexOfPanel(this.getActivePanel());
18024         
18025         if (i  < 1 && !this.autoslide) {
18026             return;
18027         }
18028         
18029         if (i < 1 && this.autoslide) {
18030             i = this.tabs.length;
18031         }
18032         
18033         this.showPanel(this.tabs[i-1]);
18034     },
18035     
18036     
18037     addBullet: function()
18038     {
18039         if(!this.bullets || Roo.isTouch){
18040             return;
18041         }
18042         var ctr = this.el.select('.carousel-bullets',true).first();
18043         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18044         var bullet = ctr.createChild({
18045             cls : 'bullet bullet-' + i
18046         },ctr.dom.lastChild);
18047         
18048         
18049         var _this = this;
18050         
18051         bullet.on('click', (function(e, el, o, ii, t){
18052
18053             e.preventDefault();
18054
18055             this.showPanel(ii);
18056
18057             if(this.autoslide && this.slideFn){
18058                 clearInterval(this.slideFn);
18059                 this.slideFn = window.setInterval(function() {
18060                     _this.showPanelNext();
18061                 }, this.timer);
18062             }
18063
18064         }).createDelegate(this, [i, bullet], true));
18065                 
18066         
18067     },
18068      
18069     setActiveBullet : function(i)
18070     {
18071         if(Roo.isTouch){
18072             return;
18073         }
18074         
18075         Roo.each(this.el.select('.bullet', true).elements, function(el){
18076             el.removeClass('selected');
18077         });
18078
18079         var bullet = this.el.select('.bullet-' + i, true).first();
18080         
18081         if(!bullet){
18082             return;
18083         }
18084         
18085         bullet.addClass('selected');
18086     }
18087     
18088     
18089   
18090 });
18091
18092  
18093
18094  
18095  
18096 Roo.apply(Roo.bootstrap.TabGroup, {
18097     
18098     groups: {},
18099      /**
18100     * register a Navigation Group
18101     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18102     */
18103     register : function(navgrp)
18104     {
18105         this.groups[navgrp.navId] = navgrp;
18106         
18107     },
18108     /**
18109     * fetch a Navigation Group based on the navigation ID
18110     * if one does not exist , it will get created.
18111     * @param {string} the navgroup to add
18112     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18113     */
18114     get: function(navId) {
18115         if (typeof(this.groups[navId]) == 'undefined') {
18116             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18117         }
18118         return this.groups[navId] ;
18119     }
18120     
18121     
18122     
18123 });
18124
18125  /*
18126  * - LGPL
18127  *
18128  * TabPanel
18129  * 
18130  */
18131
18132 /**
18133  * @class Roo.bootstrap.TabPanel
18134  * @extends Roo.bootstrap.Component
18135  * Bootstrap TabPanel class
18136  * @cfg {Boolean} active panel active
18137  * @cfg {String} html panel content
18138  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18139  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18140  * @cfg {String} href click to link..
18141  * 
18142  * 
18143  * @constructor
18144  * Create a new TabPanel
18145  * @param {Object} config The config object
18146  */
18147
18148 Roo.bootstrap.TabPanel = function(config){
18149     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18150     this.addEvents({
18151         /**
18152              * @event changed
18153              * Fires when the active status changes
18154              * @param {Roo.bootstrap.TabPanel} this
18155              * @param {Boolean} state the new state
18156             
18157          */
18158         'changed': true,
18159         /**
18160              * @event beforedeactivate
18161              * Fires before a tab is de-activated - can be used to do validation on a form.
18162              * @param {Roo.bootstrap.TabPanel} this
18163              * @return {Boolean} false if there is an error
18164             
18165          */
18166         'beforedeactivate': true
18167      });
18168     
18169     this.tabId = this.tabId || Roo.id();
18170   
18171 };
18172
18173 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18174     
18175     active: false,
18176     html: false,
18177     tabId: false,
18178     navId : false,
18179     href : '',
18180     
18181     getAutoCreate : function(){
18182         var cfg = {
18183             tag: 'div',
18184             // item is needed for carousel - not sure if it has any effect otherwise
18185             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18186             html: this.html || ''
18187         };
18188         
18189         if(this.active){
18190             cfg.cls += ' active';
18191         }
18192         
18193         if(this.tabId){
18194             cfg.tabId = this.tabId;
18195         }
18196         
18197         
18198         return cfg;
18199     },
18200     
18201     initEvents:  function()
18202     {
18203         var p = this.parent();
18204         
18205         this.navId = this.navId || p.navId;
18206         
18207         if (typeof(this.navId) != 'undefined') {
18208             // not really needed.. but just in case.. parent should be a NavGroup.
18209             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18210             
18211             tg.register(this);
18212             
18213             var i = tg.tabs.length - 1;
18214             
18215             if(this.active && tg.bullets > 0 && i < tg.bullets){
18216                 tg.setActiveBullet(i);
18217             }
18218         }
18219         
18220         this.el.on('click', this.onClick, this);
18221         
18222         if(Roo.isTouch){
18223             this.el.on("touchstart", this.onTouchStart, this);
18224             this.el.on("touchmove", this.onTouchMove, this);
18225             this.el.on("touchend", this.onTouchEnd, this);
18226         }
18227         
18228     },
18229     
18230     onRender : function(ct, position)
18231     {
18232         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18233     },
18234     
18235     setActive : function(state)
18236     {
18237         Roo.log("panel - set active " + this.tabId + "=" + state);
18238         
18239         this.active = state;
18240         if (!state) {
18241             this.el.removeClass('active');
18242             
18243         } else  if (!this.el.hasClass('active')) {
18244             this.el.addClass('active');
18245         }
18246         
18247         this.fireEvent('changed', this, state);
18248     },
18249     
18250     onClick : function(e)
18251     {
18252         e.preventDefault();
18253         
18254         if(!this.href.length){
18255             return;
18256         }
18257         
18258         window.location.href = this.href;
18259     },
18260     
18261     startX : 0,
18262     startY : 0,
18263     endX : 0,
18264     endY : 0,
18265     swiping : false,
18266     
18267     onTouchStart : function(e)
18268     {
18269         this.swiping = false;
18270         
18271         this.startX = e.browserEvent.touches[0].clientX;
18272         this.startY = e.browserEvent.touches[0].clientY;
18273     },
18274     
18275     onTouchMove : function(e)
18276     {
18277         this.swiping = true;
18278         
18279         this.endX = e.browserEvent.touches[0].clientX;
18280         this.endY = e.browserEvent.touches[0].clientY;
18281     },
18282     
18283     onTouchEnd : function(e)
18284     {
18285         if(!this.swiping){
18286             this.onClick(e);
18287             return;
18288         }
18289         
18290         var tabGroup = this.parent();
18291         
18292         if(this.endX > this.startX){ // swiping right
18293             tabGroup.showPanelPrev();
18294             return;
18295         }
18296         
18297         if(this.startX > this.endX){ // swiping left
18298             tabGroup.showPanelNext();
18299             return;
18300         }
18301     }
18302     
18303     
18304 });
18305  
18306
18307  
18308
18309  /*
18310  * - LGPL
18311  *
18312  * DateField
18313  * 
18314  */
18315
18316 /**
18317  * @class Roo.bootstrap.DateField
18318  * @extends Roo.bootstrap.Input
18319  * Bootstrap DateField class
18320  * @cfg {Number} weekStart default 0
18321  * @cfg {String} viewMode default empty, (months|years)
18322  * @cfg {String} minViewMode default empty, (months|years)
18323  * @cfg {Number} startDate default -Infinity
18324  * @cfg {Number} endDate default Infinity
18325  * @cfg {Boolean} todayHighlight default false
18326  * @cfg {Boolean} todayBtn default false
18327  * @cfg {Boolean} calendarWeeks default false
18328  * @cfg {Object} daysOfWeekDisabled default empty
18329  * @cfg {Boolean} singleMode default false (true | false)
18330  * 
18331  * @cfg {Boolean} keyboardNavigation default true
18332  * @cfg {String} language default en
18333  * 
18334  * @constructor
18335  * Create a new DateField
18336  * @param {Object} config The config object
18337  */
18338
18339 Roo.bootstrap.DateField = function(config){
18340     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18341      this.addEvents({
18342             /**
18343              * @event show
18344              * Fires when this field show.
18345              * @param {Roo.bootstrap.DateField} this
18346              * @param {Mixed} date The date value
18347              */
18348             show : true,
18349             /**
18350              * @event show
18351              * Fires when this field hide.
18352              * @param {Roo.bootstrap.DateField} this
18353              * @param {Mixed} date The date value
18354              */
18355             hide : true,
18356             /**
18357              * @event select
18358              * Fires when select a date.
18359              * @param {Roo.bootstrap.DateField} this
18360              * @param {Mixed} date The date value
18361              */
18362             select : true,
18363             /**
18364              * @event beforeselect
18365              * Fires when before select a date.
18366              * @param {Roo.bootstrap.DateField} this
18367              * @param {Mixed} date The date value
18368              */
18369             beforeselect : true
18370         });
18371 };
18372
18373 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18374     
18375     /**
18376      * @cfg {String} format
18377      * The default date format string which can be overriden for localization support.  The format must be
18378      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18379      */
18380     format : "m/d/y",
18381     /**
18382      * @cfg {String} altFormats
18383      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18384      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18385      */
18386     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18387     
18388     weekStart : 0,
18389     
18390     viewMode : '',
18391     
18392     minViewMode : '',
18393     
18394     todayHighlight : false,
18395     
18396     todayBtn: false,
18397     
18398     language: 'en',
18399     
18400     keyboardNavigation: true,
18401     
18402     calendarWeeks: false,
18403     
18404     startDate: -Infinity,
18405     
18406     endDate: Infinity,
18407     
18408     daysOfWeekDisabled: [],
18409     
18410     _events: [],
18411     
18412     singleMode : false,
18413     
18414     UTCDate: function()
18415     {
18416         return new Date(Date.UTC.apply(Date, arguments));
18417     },
18418     
18419     UTCToday: function()
18420     {
18421         var today = new Date();
18422         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18423     },
18424     
18425     getDate: function() {
18426             var d = this.getUTCDate();
18427             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18428     },
18429     
18430     getUTCDate: function() {
18431             return this.date;
18432     },
18433     
18434     setDate: function(d) {
18435             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18436     },
18437     
18438     setUTCDate: function(d) {
18439             this.date = d;
18440             this.setValue(this.formatDate(this.date));
18441     },
18442         
18443     onRender: function(ct, position)
18444     {
18445         
18446         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18447         
18448         this.language = this.language || 'en';
18449         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18450         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18451         
18452         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18453         this.format = this.format || 'm/d/y';
18454         this.isInline = false;
18455         this.isInput = true;
18456         this.component = this.el.select('.add-on', true).first() || false;
18457         this.component = (this.component && this.component.length === 0) ? false : this.component;
18458         this.hasInput = this.component && this.inputEl().length;
18459         
18460         if (typeof(this.minViewMode === 'string')) {
18461             switch (this.minViewMode) {
18462                 case 'months':
18463                     this.minViewMode = 1;
18464                     break;
18465                 case 'years':
18466                     this.minViewMode = 2;
18467                     break;
18468                 default:
18469                     this.minViewMode = 0;
18470                     break;
18471             }
18472         }
18473         
18474         if (typeof(this.viewMode === 'string')) {
18475             switch (this.viewMode) {
18476                 case 'months':
18477                     this.viewMode = 1;
18478                     break;
18479                 case 'years':
18480                     this.viewMode = 2;
18481                     break;
18482                 default:
18483                     this.viewMode = 0;
18484                     break;
18485             }
18486         }
18487                 
18488         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18489         
18490 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18491         
18492         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18493         
18494         this.picker().on('mousedown', this.onMousedown, this);
18495         this.picker().on('click', this.onClick, this);
18496         
18497         this.picker().addClass('datepicker-dropdown');
18498         
18499         this.startViewMode = this.viewMode;
18500         
18501         if(this.singleMode){
18502             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18503                 v.setVisibilityMode(Roo.Element.DISPLAY);
18504                 v.hide();
18505             });
18506             
18507             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18508                 v.setStyle('width', '189px');
18509             });
18510         }
18511         
18512         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18513             if(!this.calendarWeeks){
18514                 v.remove();
18515                 return;
18516             }
18517             
18518             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18519             v.attr('colspan', function(i, val){
18520                 return parseInt(val) + 1;
18521             });
18522         });
18523                         
18524         
18525         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18526         
18527         this.setStartDate(this.startDate);
18528         this.setEndDate(this.endDate);
18529         
18530         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18531         
18532         this.fillDow();
18533         this.fillMonths();
18534         this.update();
18535         this.showMode();
18536         
18537         if(this.isInline) {
18538             this.show();
18539         }
18540     },
18541     
18542     picker : function()
18543     {
18544         return this.pickerEl;
18545 //        return this.el.select('.datepicker', true).first();
18546     },
18547     
18548     fillDow: function()
18549     {
18550         var dowCnt = this.weekStart;
18551         
18552         var dow = {
18553             tag: 'tr',
18554             cn: [
18555                 
18556             ]
18557         };
18558         
18559         if(this.calendarWeeks){
18560             dow.cn.push({
18561                 tag: 'th',
18562                 cls: 'cw',
18563                 html: '&nbsp;'
18564             })
18565         }
18566         
18567         while (dowCnt < this.weekStart + 7) {
18568             dow.cn.push({
18569                 tag: 'th',
18570                 cls: 'dow',
18571                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18572             });
18573         }
18574         
18575         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18576     },
18577     
18578     fillMonths: function()
18579     {    
18580         var i = 0;
18581         var months = this.picker().select('>.datepicker-months td', true).first();
18582         
18583         months.dom.innerHTML = '';
18584         
18585         while (i < 12) {
18586             var month = {
18587                 tag: 'span',
18588                 cls: 'month',
18589                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18590             };
18591             
18592             months.createChild(month);
18593         }
18594         
18595     },
18596     
18597     update: function()
18598     {
18599         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;
18600         
18601         if (this.date < this.startDate) {
18602             this.viewDate = new Date(this.startDate);
18603         } else if (this.date > this.endDate) {
18604             this.viewDate = new Date(this.endDate);
18605         } else {
18606             this.viewDate = new Date(this.date);
18607         }
18608         
18609         this.fill();
18610     },
18611     
18612     fill: function() 
18613     {
18614         var d = new Date(this.viewDate),
18615                 year = d.getUTCFullYear(),
18616                 month = d.getUTCMonth(),
18617                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18618                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18619                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18620                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18621                 currentDate = this.date && this.date.valueOf(),
18622                 today = this.UTCToday();
18623         
18624         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18625         
18626 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18627         
18628 //        this.picker.select('>tfoot th.today').
18629 //                                              .text(dates[this.language].today)
18630 //                                              .toggle(this.todayBtn !== false);
18631     
18632         this.updateNavArrows();
18633         this.fillMonths();
18634                                                 
18635         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18636         
18637         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18638          
18639         prevMonth.setUTCDate(day);
18640         
18641         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18642         
18643         var nextMonth = new Date(prevMonth);
18644         
18645         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18646         
18647         nextMonth = nextMonth.valueOf();
18648         
18649         var fillMonths = false;
18650         
18651         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18652         
18653         while(prevMonth.valueOf() < nextMonth) {
18654             var clsName = '';
18655             
18656             if (prevMonth.getUTCDay() === this.weekStart) {
18657                 if(fillMonths){
18658                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18659                 }
18660                     
18661                 fillMonths = {
18662                     tag: 'tr',
18663                     cn: []
18664                 };
18665                 
18666                 if(this.calendarWeeks){
18667                     // ISO 8601: First week contains first thursday.
18668                     // ISO also states week starts on Monday, but we can be more abstract here.
18669                     var
18670                     // Start of current week: based on weekstart/current date
18671                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18672                     // Thursday of this week
18673                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18674                     // First Thursday of year, year from thursday
18675                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18676                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18677                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18678                     
18679                     fillMonths.cn.push({
18680                         tag: 'td',
18681                         cls: 'cw',
18682                         html: calWeek
18683                     });
18684                 }
18685             }
18686             
18687             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18688                 clsName += ' old';
18689             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18690                 clsName += ' new';
18691             }
18692             if (this.todayHighlight &&
18693                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18694                 prevMonth.getUTCMonth() == today.getMonth() &&
18695                 prevMonth.getUTCDate() == today.getDate()) {
18696                 clsName += ' today';
18697             }
18698             
18699             if (currentDate && prevMonth.valueOf() === currentDate) {
18700                 clsName += ' active';
18701             }
18702             
18703             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18704                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18705                     clsName += ' disabled';
18706             }
18707             
18708             fillMonths.cn.push({
18709                 tag: 'td',
18710                 cls: 'day ' + clsName,
18711                 html: prevMonth.getDate()
18712             });
18713             
18714             prevMonth.setDate(prevMonth.getDate()+1);
18715         }
18716           
18717         var currentYear = this.date && this.date.getUTCFullYear();
18718         var currentMonth = this.date && this.date.getUTCMonth();
18719         
18720         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18721         
18722         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18723             v.removeClass('active');
18724             
18725             if(currentYear === year && k === currentMonth){
18726                 v.addClass('active');
18727             }
18728             
18729             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18730                 v.addClass('disabled');
18731             }
18732             
18733         });
18734         
18735         
18736         year = parseInt(year/10, 10) * 10;
18737         
18738         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18739         
18740         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18741         
18742         year -= 1;
18743         for (var i = -1; i < 11; i++) {
18744             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18745                 tag: 'span',
18746                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18747                 html: year
18748             });
18749             
18750             year += 1;
18751         }
18752     },
18753     
18754     showMode: function(dir) 
18755     {
18756         if (dir) {
18757             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18758         }
18759         
18760         Roo.each(this.picker().select('>div',true).elements, function(v){
18761             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18762             v.hide();
18763         });
18764         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18765     },
18766     
18767     place: function()
18768     {
18769         if(this.isInline) {
18770             return;
18771         }
18772         
18773         this.picker().removeClass(['bottom', 'top']);
18774         
18775         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18776             /*
18777              * place to the top of element!
18778              *
18779              */
18780             
18781             this.picker().addClass('top');
18782             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18783             
18784             return;
18785         }
18786         
18787         this.picker().addClass('bottom');
18788         
18789         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18790     },
18791     
18792     parseDate : function(value)
18793     {
18794         if(!value || value instanceof Date){
18795             return value;
18796         }
18797         var v = Date.parseDate(value, this.format);
18798         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18799             v = Date.parseDate(value, 'Y-m-d');
18800         }
18801         if(!v && this.altFormats){
18802             if(!this.altFormatsArray){
18803                 this.altFormatsArray = this.altFormats.split("|");
18804             }
18805             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18806                 v = Date.parseDate(value, this.altFormatsArray[i]);
18807             }
18808         }
18809         return v;
18810     },
18811     
18812     formatDate : function(date, fmt)
18813     {   
18814         return (!date || !(date instanceof Date)) ?
18815         date : date.dateFormat(fmt || this.format);
18816     },
18817     
18818     onFocus : function()
18819     {
18820         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18821         this.show();
18822     },
18823     
18824     onBlur : function()
18825     {
18826         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18827         
18828         var d = this.inputEl().getValue();
18829         
18830         this.setValue(d);
18831                 
18832         this.hide();
18833     },
18834     
18835     show : function()
18836     {
18837         this.picker().show();
18838         this.update();
18839         this.place();
18840         
18841         this.fireEvent('show', this, this.date);
18842     },
18843     
18844     hide : function()
18845     {
18846         if(this.isInline) {
18847             return;
18848         }
18849         this.picker().hide();
18850         this.viewMode = this.startViewMode;
18851         this.showMode();
18852         
18853         this.fireEvent('hide', this, this.date);
18854         
18855     },
18856     
18857     onMousedown: function(e)
18858     {
18859         e.stopPropagation();
18860         e.preventDefault();
18861     },
18862     
18863     keyup: function(e)
18864     {
18865         Roo.bootstrap.DateField.superclass.keyup.call(this);
18866         this.update();
18867     },
18868
18869     setValue: function(v)
18870     {
18871         if(this.fireEvent('beforeselect', this, v) !== false){
18872             var d = new Date(this.parseDate(v) ).clearTime();
18873         
18874             if(isNaN(d.getTime())){
18875                 this.date = this.viewDate = '';
18876                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18877                 return;
18878             }
18879
18880             v = this.formatDate(d);
18881
18882             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18883
18884             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18885
18886             this.update();
18887
18888             this.fireEvent('select', this, this.date);
18889         }
18890     },
18891     
18892     getValue: function()
18893     {
18894         return this.formatDate(this.date);
18895     },
18896     
18897     fireKey: function(e)
18898     {
18899         if (!this.picker().isVisible()){
18900             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18901                 this.show();
18902             }
18903             return;
18904         }
18905         
18906         var dateChanged = false,
18907         dir, day, month,
18908         newDate, newViewDate;
18909         
18910         switch(e.keyCode){
18911             case 27: // escape
18912                 this.hide();
18913                 e.preventDefault();
18914                 break;
18915             case 37: // left
18916             case 39: // right
18917                 if (!this.keyboardNavigation) {
18918                     break;
18919                 }
18920                 dir = e.keyCode == 37 ? -1 : 1;
18921                 
18922                 if (e.ctrlKey){
18923                     newDate = this.moveYear(this.date, dir);
18924                     newViewDate = this.moveYear(this.viewDate, dir);
18925                 } else if (e.shiftKey){
18926                     newDate = this.moveMonth(this.date, dir);
18927                     newViewDate = this.moveMonth(this.viewDate, dir);
18928                 } else {
18929                     newDate = new Date(this.date);
18930                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18931                     newViewDate = new Date(this.viewDate);
18932                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18933                 }
18934                 if (this.dateWithinRange(newDate)){
18935                     this.date = newDate;
18936                     this.viewDate = newViewDate;
18937                     this.setValue(this.formatDate(this.date));
18938 //                    this.update();
18939                     e.preventDefault();
18940                     dateChanged = true;
18941                 }
18942                 break;
18943             case 38: // up
18944             case 40: // down
18945                 if (!this.keyboardNavigation) {
18946                     break;
18947                 }
18948                 dir = e.keyCode == 38 ? -1 : 1;
18949                 if (e.ctrlKey){
18950                     newDate = this.moveYear(this.date, dir);
18951                     newViewDate = this.moveYear(this.viewDate, dir);
18952                 } else if (e.shiftKey){
18953                     newDate = this.moveMonth(this.date, dir);
18954                     newViewDate = this.moveMonth(this.viewDate, dir);
18955                 } else {
18956                     newDate = new Date(this.date);
18957                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18958                     newViewDate = new Date(this.viewDate);
18959                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18960                 }
18961                 if (this.dateWithinRange(newDate)){
18962                     this.date = newDate;
18963                     this.viewDate = newViewDate;
18964                     this.setValue(this.formatDate(this.date));
18965 //                    this.update();
18966                     e.preventDefault();
18967                     dateChanged = true;
18968                 }
18969                 break;
18970             case 13: // enter
18971                 this.setValue(this.formatDate(this.date));
18972                 this.hide();
18973                 e.preventDefault();
18974                 break;
18975             case 9: // tab
18976                 this.setValue(this.formatDate(this.date));
18977                 this.hide();
18978                 break;
18979             case 16: // shift
18980             case 17: // ctrl
18981             case 18: // alt
18982                 break;
18983             default :
18984                 this.hide();
18985                 
18986         }
18987     },
18988     
18989     
18990     onClick: function(e) 
18991     {
18992         e.stopPropagation();
18993         e.preventDefault();
18994         
18995         var target = e.getTarget();
18996         
18997         if(target.nodeName.toLowerCase() === 'i'){
18998             target = Roo.get(target).dom.parentNode;
18999         }
19000         
19001         var nodeName = target.nodeName;
19002         var className = target.className;
19003         var html = target.innerHTML;
19004         //Roo.log(nodeName);
19005         
19006         switch(nodeName.toLowerCase()) {
19007             case 'th':
19008                 switch(className) {
19009                     case 'switch':
19010                         this.showMode(1);
19011                         break;
19012                     case 'prev':
19013                     case 'next':
19014                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19015                         switch(this.viewMode){
19016                                 case 0:
19017                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19018                                         break;
19019                                 case 1:
19020                                 case 2:
19021                                         this.viewDate = this.moveYear(this.viewDate, dir);
19022                                         break;
19023                         }
19024                         this.fill();
19025                         break;
19026                     case 'today':
19027                         var date = new Date();
19028                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19029 //                        this.fill()
19030                         this.setValue(this.formatDate(this.date));
19031                         
19032                         this.hide();
19033                         break;
19034                 }
19035                 break;
19036             case 'span':
19037                 if (className.indexOf('disabled') < 0) {
19038                     this.viewDate.setUTCDate(1);
19039                     if (className.indexOf('month') > -1) {
19040                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19041                     } else {
19042                         var year = parseInt(html, 10) || 0;
19043                         this.viewDate.setUTCFullYear(year);
19044                         
19045                     }
19046                     
19047                     if(this.singleMode){
19048                         this.setValue(this.formatDate(this.viewDate));
19049                         this.hide();
19050                         return;
19051                     }
19052                     
19053                     this.showMode(-1);
19054                     this.fill();
19055                 }
19056                 break;
19057                 
19058             case 'td':
19059                 //Roo.log(className);
19060                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19061                     var day = parseInt(html, 10) || 1;
19062                     var year = this.viewDate.getUTCFullYear(),
19063                         month = this.viewDate.getUTCMonth();
19064
19065                     if (className.indexOf('old') > -1) {
19066                         if(month === 0 ){
19067                             month = 11;
19068                             year -= 1;
19069                         }else{
19070                             month -= 1;
19071                         }
19072                     } else if (className.indexOf('new') > -1) {
19073                         if (month == 11) {
19074                             month = 0;
19075                             year += 1;
19076                         } else {
19077                             month += 1;
19078                         }
19079                     }
19080                     //Roo.log([year,month,day]);
19081                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19082                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19083 //                    this.fill();
19084                     //Roo.log(this.formatDate(this.date));
19085                     this.setValue(this.formatDate(this.date));
19086                     this.hide();
19087                 }
19088                 break;
19089         }
19090     },
19091     
19092     setStartDate: function(startDate)
19093     {
19094         this.startDate = startDate || -Infinity;
19095         if (this.startDate !== -Infinity) {
19096             this.startDate = this.parseDate(this.startDate);
19097         }
19098         this.update();
19099         this.updateNavArrows();
19100     },
19101
19102     setEndDate: function(endDate)
19103     {
19104         this.endDate = endDate || Infinity;
19105         if (this.endDate !== Infinity) {
19106             this.endDate = this.parseDate(this.endDate);
19107         }
19108         this.update();
19109         this.updateNavArrows();
19110     },
19111     
19112     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19113     {
19114         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19115         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19116             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19117         }
19118         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19119             return parseInt(d, 10);
19120         });
19121         this.update();
19122         this.updateNavArrows();
19123     },
19124     
19125     updateNavArrows: function() 
19126     {
19127         if(this.singleMode){
19128             return;
19129         }
19130         
19131         var d = new Date(this.viewDate),
19132         year = d.getUTCFullYear(),
19133         month = d.getUTCMonth();
19134         
19135         Roo.each(this.picker().select('.prev', true).elements, function(v){
19136             v.show();
19137             switch (this.viewMode) {
19138                 case 0:
19139
19140                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19141                         v.hide();
19142                     }
19143                     break;
19144                 case 1:
19145                 case 2:
19146                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19147                         v.hide();
19148                     }
19149                     break;
19150             }
19151         });
19152         
19153         Roo.each(this.picker().select('.next', true).elements, function(v){
19154             v.show();
19155             switch (this.viewMode) {
19156                 case 0:
19157
19158                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19159                         v.hide();
19160                     }
19161                     break;
19162                 case 1:
19163                 case 2:
19164                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19165                         v.hide();
19166                     }
19167                     break;
19168             }
19169         })
19170     },
19171     
19172     moveMonth: function(date, dir)
19173     {
19174         if (!dir) {
19175             return date;
19176         }
19177         var new_date = new Date(date.valueOf()),
19178         day = new_date.getUTCDate(),
19179         month = new_date.getUTCMonth(),
19180         mag = Math.abs(dir),
19181         new_month, test;
19182         dir = dir > 0 ? 1 : -1;
19183         if (mag == 1){
19184             test = dir == -1
19185             // If going back one month, make sure month is not current month
19186             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19187             ? function(){
19188                 return new_date.getUTCMonth() == month;
19189             }
19190             // If going forward one month, make sure month is as expected
19191             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19192             : function(){
19193                 return new_date.getUTCMonth() != new_month;
19194             };
19195             new_month = month + dir;
19196             new_date.setUTCMonth(new_month);
19197             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19198             if (new_month < 0 || new_month > 11) {
19199                 new_month = (new_month + 12) % 12;
19200             }
19201         } else {
19202             // For magnitudes >1, move one month at a time...
19203             for (var i=0; i<mag; i++) {
19204                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19205                 new_date = this.moveMonth(new_date, dir);
19206             }
19207             // ...then reset the day, keeping it in the new month
19208             new_month = new_date.getUTCMonth();
19209             new_date.setUTCDate(day);
19210             test = function(){
19211                 return new_month != new_date.getUTCMonth();
19212             };
19213         }
19214         // Common date-resetting loop -- if date is beyond end of month, make it
19215         // end of month
19216         while (test()){
19217             new_date.setUTCDate(--day);
19218             new_date.setUTCMonth(new_month);
19219         }
19220         return new_date;
19221     },
19222
19223     moveYear: function(date, dir)
19224     {
19225         return this.moveMonth(date, dir*12);
19226     },
19227
19228     dateWithinRange: function(date)
19229     {
19230         return date >= this.startDate && date <= this.endDate;
19231     },
19232
19233     
19234     remove: function() 
19235     {
19236         this.picker().remove();
19237     },
19238     
19239     validateValue : function(value)
19240     {
19241         if(this.getVisibilityEl().hasClass('hidden')){
19242             return true;
19243         }
19244         
19245         if(value.length < 1)  {
19246             if(this.allowBlank){
19247                 return true;
19248             }
19249             return false;
19250         }
19251         
19252         if(value.length < this.minLength){
19253             return false;
19254         }
19255         if(value.length > this.maxLength){
19256             return false;
19257         }
19258         if(this.vtype){
19259             var vt = Roo.form.VTypes;
19260             if(!vt[this.vtype](value, this)){
19261                 return false;
19262             }
19263         }
19264         if(typeof this.validator == "function"){
19265             var msg = this.validator(value);
19266             if(msg !== true){
19267                 return false;
19268             }
19269         }
19270         
19271         if(this.regex && !this.regex.test(value)){
19272             return false;
19273         }
19274         
19275         if(typeof(this.parseDate(value)) == 'undefined'){
19276             return false;
19277         }
19278         
19279         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19280             return false;
19281         }      
19282         
19283         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19284             return false;
19285         } 
19286         
19287         
19288         return true;
19289     },
19290     
19291     setVisible : function(visible)
19292     {
19293         if(!this.getEl()){
19294             return;
19295         }
19296         
19297         this.getEl().removeClass('hidden');
19298         
19299         if(visible){
19300             return;
19301         }
19302         
19303         this.getEl().addClass('hidden');
19304     }
19305    
19306 });
19307
19308 Roo.apply(Roo.bootstrap.DateField,  {
19309     
19310     head : {
19311         tag: 'thead',
19312         cn: [
19313         {
19314             tag: 'tr',
19315             cn: [
19316             {
19317                 tag: 'th',
19318                 cls: 'prev',
19319                 html: '<i class="fa fa-arrow-left"/>'
19320             },
19321             {
19322                 tag: 'th',
19323                 cls: 'switch',
19324                 colspan: '5'
19325             },
19326             {
19327                 tag: 'th',
19328                 cls: 'next',
19329                 html: '<i class="fa fa-arrow-right"/>'
19330             }
19331
19332             ]
19333         }
19334         ]
19335     },
19336     
19337     content : {
19338         tag: 'tbody',
19339         cn: [
19340         {
19341             tag: 'tr',
19342             cn: [
19343             {
19344                 tag: 'td',
19345                 colspan: '7'
19346             }
19347             ]
19348         }
19349         ]
19350     },
19351     
19352     footer : {
19353         tag: 'tfoot',
19354         cn: [
19355         {
19356             tag: 'tr',
19357             cn: [
19358             {
19359                 tag: 'th',
19360                 colspan: '7',
19361                 cls: 'today'
19362             }
19363                     
19364             ]
19365         }
19366         ]
19367     },
19368     
19369     dates:{
19370         en: {
19371             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19372             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19373             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19374             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19375             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19376             today: "Today"
19377         }
19378     },
19379     
19380     modes: [
19381     {
19382         clsName: 'days',
19383         navFnc: 'Month',
19384         navStep: 1
19385     },
19386     {
19387         clsName: 'months',
19388         navFnc: 'FullYear',
19389         navStep: 1
19390     },
19391     {
19392         clsName: 'years',
19393         navFnc: 'FullYear',
19394         navStep: 10
19395     }]
19396 });
19397
19398 Roo.apply(Roo.bootstrap.DateField,  {
19399   
19400     template : {
19401         tag: 'div',
19402         cls: 'datepicker dropdown-menu roo-dynamic',
19403         cn: [
19404         {
19405             tag: 'div',
19406             cls: 'datepicker-days',
19407             cn: [
19408             {
19409                 tag: 'table',
19410                 cls: 'table-condensed',
19411                 cn:[
19412                 Roo.bootstrap.DateField.head,
19413                 {
19414                     tag: 'tbody'
19415                 },
19416                 Roo.bootstrap.DateField.footer
19417                 ]
19418             }
19419             ]
19420         },
19421         {
19422             tag: 'div',
19423             cls: 'datepicker-months',
19424             cn: [
19425             {
19426                 tag: 'table',
19427                 cls: 'table-condensed',
19428                 cn:[
19429                 Roo.bootstrap.DateField.head,
19430                 Roo.bootstrap.DateField.content,
19431                 Roo.bootstrap.DateField.footer
19432                 ]
19433             }
19434             ]
19435         },
19436         {
19437             tag: 'div',
19438             cls: 'datepicker-years',
19439             cn: [
19440             {
19441                 tag: 'table',
19442                 cls: 'table-condensed',
19443                 cn:[
19444                 Roo.bootstrap.DateField.head,
19445                 Roo.bootstrap.DateField.content,
19446                 Roo.bootstrap.DateField.footer
19447                 ]
19448             }
19449             ]
19450         }
19451         ]
19452     }
19453 });
19454
19455  
19456
19457  /*
19458  * - LGPL
19459  *
19460  * TimeField
19461  * 
19462  */
19463
19464 /**
19465  * @class Roo.bootstrap.TimeField
19466  * @extends Roo.bootstrap.Input
19467  * Bootstrap DateField class
19468  * 
19469  * 
19470  * @constructor
19471  * Create a new TimeField
19472  * @param {Object} config The config object
19473  */
19474
19475 Roo.bootstrap.TimeField = function(config){
19476     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19477     this.addEvents({
19478             /**
19479              * @event show
19480              * Fires when this field show.
19481              * @param {Roo.bootstrap.DateField} thisthis
19482              * @param {Mixed} date The date value
19483              */
19484             show : true,
19485             /**
19486              * @event show
19487              * Fires when this field hide.
19488              * @param {Roo.bootstrap.DateField} this
19489              * @param {Mixed} date The date value
19490              */
19491             hide : true,
19492             /**
19493              * @event select
19494              * Fires when select a date.
19495              * @param {Roo.bootstrap.DateField} this
19496              * @param {Mixed} date The date value
19497              */
19498             select : true
19499         });
19500 };
19501
19502 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19503     
19504     /**
19505      * @cfg {String} format
19506      * The default time format string which can be overriden for localization support.  The format must be
19507      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19508      */
19509     format : "H:i",
19510        
19511     onRender: function(ct, position)
19512     {
19513         
19514         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19515                 
19516         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19517         
19518         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19519         
19520         this.pop = this.picker().select('>.datepicker-time',true).first();
19521         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19522         
19523         this.picker().on('mousedown', this.onMousedown, this);
19524         this.picker().on('click', this.onClick, this);
19525         
19526         this.picker().addClass('datepicker-dropdown');
19527     
19528         this.fillTime();
19529         this.update();
19530             
19531         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19532         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19533         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19534         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19535         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19536         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19537
19538     },
19539     
19540     fireKey: function(e){
19541         if (!this.picker().isVisible()){
19542             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19543                 this.show();
19544             }
19545             return;
19546         }
19547
19548         e.preventDefault();
19549         
19550         switch(e.keyCode){
19551             case 27: // escape
19552                 this.hide();
19553                 break;
19554             case 37: // left
19555             case 39: // right
19556                 this.onTogglePeriod();
19557                 break;
19558             case 38: // up
19559                 this.onIncrementMinutes();
19560                 break;
19561             case 40: // down
19562                 this.onDecrementMinutes();
19563                 break;
19564             case 13: // enter
19565             case 9: // tab
19566                 this.setTime();
19567                 break;
19568         }
19569     },
19570     
19571     onClick: function(e) {
19572         e.stopPropagation();
19573         e.preventDefault();
19574     },
19575     
19576     picker : function()
19577     {
19578         return this.el.select('.datepicker', true).first();
19579     },
19580     
19581     fillTime: function()
19582     {    
19583         var time = this.pop.select('tbody', true).first();
19584         
19585         time.dom.innerHTML = '';
19586         
19587         time.createChild({
19588             tag: 'tr',
19589             cn: [
19590                 {
19591                     tag: 'td',
19592                     cn: [
19593                         {
19594                             tag: 'a',
19595                             href: '#',
19596                             cls: 'btn',
19597                             cn: [
19598                                 {
19599                                     tag: 'span',
19600                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19601                                 }
19602                             ]
19603                         } 
19604                     ]
19605                 },
19606                 {
19607                     tag: 'td',
19608                     cls: 'separator'
19609                 },
19610                 {
19611                     tag: 'td',
19612                     cn: [
19613                         {
19614                             tag: 'a',
19615                             href: '#',
19616                             cls: 'btn',
19617                             cn: [
19618                                 {
19619                                     tag: 'span',
19620                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19621                                 }
19622                             ]
19623                         }
19624                     ]
19625                 },
19626                 {
19627                     tag: 'td',
19628                     cls: 'separator'
19629                 }
19630             ]
19631         });
19632         
19633         time.createChild({
19634             tag: 'tr',
19635             cn: [
19636                 {
19637                     tag: 'td',
19638                     cn: [
19639                         {
19640                             tag: 'span',
19641                             cls: 'timepicker-hour',
19642                             html: '00'
19643                         }  
19644                     ]
19645                 },
19646                 {
19647                     tag: 'td',
19648                     cls: 'separator',
19649                     html: ':'
19650                 },
19651                 {
19652                     tag: 'td',
19653                     cn: [
19654                         {
19655                             tag: 'span',
19656                             cls: 'timepicker-minute',
19657                             html: '00'
19658                         }  
19659                     ]
19660                 },
19661                 {
19662                     tag: 'td',
19663                     cls: 'separator'
19664                 },
19665                 {
19666                     tag: 'td',
19667                     cn: [
19668                         {
19669                             tag: 'button',
19670                             type: 'button',
19671                             cls: 'btn btn-primary period',
19672                             html: 'AM'
19673                             
19674                         }
19675                     ]
19676                 }
19677             ]
19678         });
19679         
19680         time.createChild({
19681             tag: 'tr',
19682             cn: [
19683                 {
19684                     tag: 'td',
19685                     cn: [
19686                         {
19687                             tag: 'a',
19688                             href: '#',
19689                             cls: 'btn',
19690                             cn: [
19691                                 {
19692                                     tag: 'span',
19693                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19694                                 }
19695                             ]
19696                         }
19697                     ]
19698                 },
19699                 {
19700                     tag: 'td',
19701                     cls: 'separator'
19702                 },
19703                 {
19704                     tag: 'td',
19705                     cn: [
19706                         {
19707                             tag: 'a',
19708                             href: '#',
19709                             cls: 'btn',
19710                             cn: [
19711                                 {
19712                                     tag: 'span',
19713                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19714                                 }
19715                             ]
19716                         }
19717                     ]
19718                 },
19719                 {
19720                     tag: 'td',
19721                     cls: 'separator'
19722                 }
19723             ]
19724         });
19725         
19726     },
19727     
19728     update: function()
19729     {
19730         
19731         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19732         
19733         this.fill();
19734     },
19735     
19736     fill: function() 
19737     {
19738         var hours = this.time.getHours();
19739         var minutes = this.time.getMinutes();
19740         var period = 'AM';
19741         
19742         if(hours > 11){
19743             period = 'PM';
19744         }
19745         
19746         if(hours == 0){
19747             hours = 12;
19748         }
19749         
19750         
19751         if(hours > 12){
19752             hours = hours - 12;
19753         }
19754         
19755         if(hours < 10){
19756             hours = '0' + hours;
19757         }
19758         
19759         if(minutes < 10){
19760             minutes = '0' + minutes;
19761         }
19762         
19763         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19764         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19765         this.pop.select('button', true).first().dom.innerHTML = period;
19766         
19767     },
19768     
19769     place: function()
19770     {   
19771         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19772         
19773         var cls = ['bottom'];
19774         
19775         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19776             cls.pop();
19777             cls.push('top');
19778         }
19779         
19780         cls.push('right');
19781         
19782         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19783             cls.pop();
19784             cls.push('left');
19785         }
19786         
19787         this.picker().addClass(cls.join('-'));
19788         
19789         var _this = this;
19790         
19791         Roo.each(cls, function(c){
19792             if(c == 'bottom'){
19793                 _this.picker().setTop(_this.inputEl().getHeight());
19794                 return;
19795             }
19796             if(c == 'top'){
19797                 _this.picker().setTop(0 - _this.picker().getHeight());
19798                 return;
19799             }
19800             
19801             if(c == 'left'){
19802                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19803                 return;
19804             }
19805             if(c == 'right'){
19806                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19807                 return;
19808             }
19809         });
19810         
19811     },
19812   
19813     onFocus : function()
19814     {
19815         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19816         this.show();
19817     },
19818     
19819     onBlur : function()
19820     {
19821         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19822         this.hide();
19823     },
19824     
19825     show : function()
19826     {
19827         this.picker().show();
19828         this.pop.show();
19829         this.update();
19830         this.place();
19831         
19832         this.fireEvent('show', this, this.date);
19833     },
19834     
19835     hide : function()
19836     {
19837         this.picker().hide();
19838         this.pop.hide();
19839         
19840         this.fireEvent('hide', this, this.date);
19841     },
19842     
19843     setTime : function()
19844     {
19845         this.hide();
19846         this.setValue(this.time.format(this.format));
19847         
19848         this.fireEvent('select', this, this.date);
19849         
19850         
19851     },
19852     
19853     onMousedown: function(e){
19854         e.stopPropagation();
19855         e.preventDefault();
19856     },
19857     
19858     onIncrementHours: function()
19859     {
19860         Roo.log('onIncrementHours');
19861         this.time = this.time.add(Date.HOUR, 1);
19862         this.update();
19863         
19864     },
19865     
19866     onDecrementHours: function()
19867     {
19868         Roo.log('onDecrementHours');
19869         this.time = this.time.add(Date.HOUR, -1);
19870         this.update();
19871     },
19872     
19873     onIncrementMinutes: function()
19874     {
19875         Roo.log('onIncrementMinutes');
19876         this.time = this.time.add(Date.MINUTE, 1);
19877         this.update();
19878     },
19879     
19880     onDecrementMinutes: function()
19881     {
19882         Roo.log('onDecrementMinutes');
19883         this.time = this.time.add(Date.MINUTE, -1);
19884         this.update();
19885     },
19886     
19887     onTogglePeriod: function()
19888     {
19889         Roo.log('onTogglePeriod');
19890         this.time = this.time.add(Date.HOUR, 12);
19891         this.update();
19892     }
19893     
19894    
19895 });
19896
19897 Roo.apply(Roo.bootstrap.TimeField,  {
19898     
19899     content : {
19900         tag: 'tbody',
19901         cn: [
19902             {
19903                 tag: 'tr',
19904                 cn: [
19905                 {
19906                     tag: 'td',
19907                     colspan: '7'
19908                 }
19909                 ]
19910             }
19911         ]
19912     },
19913     
19914     footer : {
19915         tag: 'tfoot',
19916         cn: [
19917             {
19918                 tag: 'tr',
19919                 cn: [
19920                 {
19921                     tag: 'th',
19922                     colspan: '7',
19923                     cls: '',
19924                     cn: [
19925                         {
19926                             tag: 'button',
19927                             cls: 'btn btn-info ok',
19928                             html: 'OK'
19929                         }
19930                     ]
19931                 }
19932
19933                 ]
19934             }
19935         ]
19936     }
19937 });
19938
19939 Roo.apply(Roo.bootstrap.TimeField,  {
19940   
19941     template : {
19942         tag: 'div',
19943         cls: 'datepicker dropdown-menu',
19944         cn: [
19945             {
19946                 tag: 'div',
19947                 cls: 'datepicker-time',
19948                 cn: [
19949                 {
19950                     tag: 'table',
19951                     cls: 'table-condensed',
19952                     cn:[
19953                     Roo.bootstrap.TimeField.content,
19954                     Roo.bootstrap.TimeField.footer
19955                     ]
19956                 }
19957                 ]
19958             }
19959         ]
19960     }
19961 });
19962
19963  
19964
19965  /*
19966  * - LGPL
19967  *
19968  * MonthField
19969  * 
19970  */
19971
19972 /**
19973  * @class Roo.bootstrap.MonthField
19974  * @extends Roo.bootstrap.Input
19975  * Bootstrap MonthField class
19976  * 
19977  * @cfg {String} language default en
19978  * 
19979  * @constructor
19980  * Create a new MonthField
19981  * @param {Object} config The config object
19982  */
19983
19984 Roo.bootstrap.MonthField = function(config){
19985     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19986     
19987     this.addEvents({
19988         /**
19989          * @event show
19990          * Fires when this field show.
19991          * @param {Roo.bootstrap.MonthField} this
19992          * @param {Mixed} date The date value
19993          */
19994         show : true,
19995         /**
19996          * @event show
19997          * Fires when this field hide.
19998          * @param {Roo.bootstrap.MonthField} this
19999          * @param {Mixed} date The date value
20000          */
20001         hide : true,
20002         /**
20003          * @event select
20004          * Fires when select a date.
20005          * @param {Roo.bootstrap.MonthField} this
20006          * @param {String} oldvalue The old value
20007          * @param {String} newvalue The new value
20008          */
20009         select : true
20010     });
20011 };
20012
20013 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20014     
20015     onRender: function(ct, position)
20016     {
20017         
20018         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20019         
20020         this.language = this.language || 'en';
20021         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20022         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20023         
20024         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20025         this.isInline = false;
20026         this.isInput = true;
20027         this.component = this.el.select('.add-on', true).first() || false;
20028         this.component = (this.component && this.component.length === 0) ? false : this.component;
20029         this.hasInput = this.component && this.inputEL().length;
20030         
20031         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20032         
20033         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20034         
20035         this.picker().on('mousedown', this.onMousedown, this);
20036         this.picker().on('click', this.onClick, this);
20037         
20038         this.picker().addClass('datepicker-dropdown');
20039         
20040         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20041             v.setStyle('width', '189px');
20042         });
20043         
20044         this.fillMonths();
20045         
20046         this.update();
20047         
20048         if(this.isInline) {
20049             this.show();
20050         }
20051         
20052     },
20053     
20054     setValue: function(v, suppressEvent)
20055     {   
20056         var o = this.getValue();
20057         
20058         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20059         
20060         this.update();
20061
20062         if(suppressEvent !== true){
20063             this.fireEvent('select', this, o, v);
20064         }
20065         
20066     },
20067     
20068     getValue: function()
20069     {
20070         return this.value;
20071     },
20072     
20073     onClick: function(e) 
20074     {
20075         e.stopPropagation();
20076         e.preventDefault();
20077         
20078         var target = e.getTarget();
20079         
20080         if(target.nodeName.toLowerCase() === 'i'){
20081             target = Roo.get(target).dom.parentNode;
20082         }
20083         
20084         var nodeName = target.nodeName;
20085         var className = target.className;
20086         var html = target.innerHTML;
20087         
20088         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20089             return;
20090         }
20091         
20092         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20093         
20094         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20095         
20096         this.hide();
20097                         
20098     },
20099     
20100     picker : function()
20101     {
20102         return this.pickerEl;
20103     },
20104     
20105     fillMonths: function()
20106     {    
20107         var i = 0;
20108         var months = this.picker().select('>.datepicker-months td', true).first();
20109         
20110         months.dom.innerHTML = '';
20111         
20112         while (i < 12) {
20113             var month = {
20114                 tag: 'span',
20115                 cls: 'month',
20116                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20117             };
20118             
20119             months.createChild(month);
20120         }
20121         
20122     },
20123     
20124     update: function()
20125     {
20126         var _this = this;
20127         
20128         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20129             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20130         }
20131         
20132         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20133             e.removeClass('active');
20134             
20135             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20136                 e.addClass('active');
20137             }
20138         })
20139     },
20140     
20141     place: function()
20142     {
20143         if(this.isInline) {
20144             return;
20145         }
20146         
20147         this.picker().removeClass(['bottom', 'top']);
20148         
20149         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20150             /*
20151              * place to the top of element!
20152              *
20153              */
20154             
20155             this.picker().addClass('top');
20156             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20157             
20158             return;
20159         }
20160         
20161         this.picker().addClass('bottom');
20162         
20163         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20164     },
20165     
20166     onFocus : function()
20167     {
20168         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20169         this.show();
20170     },
20171     
20172     onBlur : function()
20173     {
20174         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20175         
20176         var d = this.inputEl().getValue();
20177         
20178         this.setValue(d);
20179                 
20180         this.hide();
20181     },
20182     
20183     show : function()
20184     {
20185         this.picker().show();
20186         this.picker().select('>.datepicker-months', true).first().show();
20187         this.update();
20188         this.place();
20189         
20190         this.fireEvent('show', this, this.date);
20191     },
20192     
20193     hide : function()
20194     {
20195         if(this.isInline) {
20196             return;
20197         }
20198         this.picker().hide();
20199         this.fireEvent('hide', this, this.date);
20200         
20201     },
20202     
20203     onMousedown: function(e)
20204     {
20205         e.stopPropagation();
20206         e.preventDefault();
20207     },
20208     
20209     keyup: function(e)
20210     {
20211         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20212         this.update();
20213     },
20214
20215     fireKey: function(e)
20216     {
20217         if (!this.picker().isVisible()){
20218             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20219                 this.show();
20220             }
20221             return;
20222         }
20223         
20224         var dir;
20225         
20226         switch(e.keyCode){
20227             case 27: // escape
20228                 this.hide();
20229                 e.preventDefault();
20230                 break;
20231             case 37: // left
20232             case 39: // right
20233                 dir = e.keyCode == 37 ? -1 : 1;
20234                 
20235                 this.vIndex = this.vIndex + dir;
20236                 
20237                 if(this.vIndex < 0){
20238                     this.vIndex = 0;
20239                 }
20240                 
20241                 if(this.vIndex > 11){
20242                     this.vIndex = 11;
20243                 }
20244                 
20245                 if(isNaN(this.vIndex)){
20246                     this.vIndex = 0;
20247                 }
20248                 
20249                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20250                 
20251                 break;
20252             case 38: // up
20253             case 40: // down
20254                 
20255                 dir = e.keyCode == 38 ? -1 : 1;
20256                 
20257                 this.vIndex = this.vIndex + dir * 4;
20258                 
20259                 if(this.vIndex < 0){
20260                     this.vIndex = 0;
20261                 }
20262                 
20263                 if(this.vIndex > 11){
20264                     this.vIndex = 11;
20265                 }
20266                 
20267                 if(isNaN(this.vIndex)){
20268                     this.vIndex = 0;
20269                 }
20270                 
20271                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20272                 break;
20273                 
20274             case 13: // enter
20275                 
20276                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20277                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20278                 }
20279                 
20280                 this.hide();
20281                 e.preventDefault();
20282                 break;
20283             case 9: // tab
20284                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20285                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20286                 }
20287                 this.hide();
20288                 break;
20289             case 16: // shift
20290             case 17: // ctrl
20291             case 18: // alt
20292                 break;
20293             default :
20294                 this.hide();
20295                 
20296         }
20297     },
20298     
20299     remove: function() 
20300     {
20301         this.picker().remove();
20302     }
20303    
20304 });
20305
20306 Roo.apply(Roo.bootstrap.MonthField,  {
20307     
20308     content : {
20309         tag: 'tbody',
20310         cn: [
20311         {
20312             tag: 'tr',
20313             cn: [
20314             {
20315                 tag: 'td',
20316                 colspan: '7'
20317             }
20318             ]
20319         }
20320         ]
20321     },
20322     
20323     dates:{
20324         en: {
20325             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20326             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20327         }
20328     }
20329 });
20330
20331 Roo.apply(Roo.bootstrap.MonthField,  {
20332   
20333     template : {
20334         tag: 'div',
20335         cls: 'datepicker dropdown-menu roo-dynamic',
20336         cn: [
20337             {
20338                 tag: 'div',
20339                 cls: 'datepicker-months',
20340                 cn: [
20341                 {
20342                     tag: 'table',
20343                     cls: 'table-condensed',
20344                     cn:[
20345                         Roo.bootstrap.DateField.content
20346                     ]
20347                 }
20348                 ]
20349             }
20350         ]
20351     }
20352 });
20353
20354  
20355
20356  
20357  /*
20358  * - LGPL
20359  *
20360  * CheckBox
20361  * 
20362  */
20363
20364 /**
20365  * @class Roo.bootstrap.CheckBox
20366  * @extends Roo.bootstrap.Input
20367  * Bootstrap CheckBox class
20368  * 
20369  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20370  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20371  * @cfg {String} boxLabel The text that appears beside the checkbox
20372  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20373  * @cfg {Boolean} checked initnal the element
20374  * @cfg {Boolean} inline inline the element (default false)
20375  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20376  * @cfg {String} tooltip label tooltip
20377  * 
20378  * @constructor
20379  * Create a new CheckBox
20380  * @param {Object} config The config object
20381  */
20382
20383 Roo.bootstrap.CheckBox = function(config){
20384     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20385    
20386     this.addEvents({
20387         /**
20388         * @event check
20389         * Fires when the element is checked or unchecked.
20390         * @param {Roo.bootstrap.CheckBox} this This input
20391         * @param {Boolean} checked The new checked value
20392         */
20393        check : true,
20394        /**
20395         * @event click
20396         * Fires when the element is click.
20397         * @param {Roo.bootstrap.CheckBox} this This input
20398         */
20399        click : true
20400     });
20401     
20402 };
20403
20404 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20405   
20406     inputType: 'checkbox',
20407     inputValue: 1,
20408     valueOff: 0,
20409     boxLabel: false,
20410     checked: false,
20411     weight : false,
20412     inline: false,
20413     tooltip : '',
20414     
20415     getAutoCreate : function()
20416     {
20417         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20418         
20419         var id = Roo.id();
20420         
20421         var cfg = {};
20422         
20423         cfg.cls = 'form-group ' + this.inputType; //input-group
20424         
20425         if(this.inline){
20426             cfg.cls += ' ' + this.inputType + '-inline';
20427         }
20428         
20429         var input =  {
20430             tag: 'input',
20431             id : id,
20432             type : this.inputType,
20433             value : this.inputValue,
20434             cls : 'roo-' + this.inputType, //'form-box',
20435             placeholder : this.placeholder || ''
20436             
20437         };
20438         
20439         if(this.inputType != 'radio'){
20440             var hidden =  {
20441                 tag: 'input',
20442                 type : 'hidden',
20443                 cls : 'roo-hidden-value',
20444                 value : this.checked ? this.inputValue : this.valueOff
20445             };
20446         }
20447         
20448             
20449         if (this.weight) { // Validity check?
20450             cfg.cls += " " + this.inputType + "-" + this.weight;
20451         }
20452         
20453         if (this.disabled) {
20454             input.disabled=true;
20455         }
20456         
20457         if(this.checked){
20458             input.checked = this.checked;
20459         }
20460         
20461         if (this.name) {
20462             
20463             input.name = this.name;
20464             
20465             if(this.inputType != 'radio'){
20466                 hidden.name = this.name;
20467                 input.name = '_hidden_' + this.name;
20468             }
20469         }
20470         
20471         if (this.size) {
20472             input.cls += ' input-' + this.size;
20473         }
20474         
20475         var settings=this;
20476         
20477         ['xs','sm','md','lg'].map(function(size){
20478             if (settings[size]) {
20479                 cfg.cls += ' col-' + size + '-' + settings[size];
20480             }
20481         });
20482         
20483         var inputblock = input;
20484          
20485         if (this.before || this.after) {
20486             
20487             inputblock = {
20488                 cls : 'input-group',
20489                 cn :  [] 
20490             };
20491             
20492             if (this.before) {
20493                 inputblock.cn.push({
20494                     tag :'span',
20495                     cls : 'input-group-addon',
20496                     html : this.before
20497                 });
20498             }
20499             
20500             inputblock.cn.push(input);
20501             
20502             if(this.inputType != 'radio'){
20503                 inputblock.cn.push(hidden);
20504             }
20505             
20506             if (this.after) {
20507                 inputblock.cn.push({
20508                     tag :'span',
20509                     cls : 'input-group-addon',
20510                     html : this.after
20511                 });
20512             }
20513             
20514         }
20515         
20516         if (align ==='left' && this.fieldLabel.length) {
20517 //                Roo.log("left and has label");
20518             cfg.cn = [
20519                 {
20520                     tag: 'label',
20521                     'for' :  id,
20522                     cls : 'control-label',
20523                     html : this.fieldLabel
20524                 },
20525                 {
20526                     cls : "", 
20527                     cn: [
20528                         inputblock
20529                     ]
20530                 }
20531             ];
20532             
20533             if(this.labelWidth > 12){
20534                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20535             }
20536             
20537             if(this.labelWidth < 13 && this.labelmd == 0){
20538                 this.labelmd = this.labelWidth;
20539             }
20540             
20541             if(this.labellg > 0){
20542                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20543                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20544             }
20545             
20546             if(this.labelmd > 0){
20547                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20548                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20549             }
20550             
20551             if(this.labelsm > 0){
20552                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20553                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20554             }
20555             
20556             if(this.labelxs > 0){
20557                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20558                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20559             }
20560             
20561         } else if ( this.fieldLabel.length) {
20562 //                Roo.log(" label");
20563                 cfg.cn = [
20564                    
20565                     {
20566                         tag: this.boxLabel ? 'span' : 'label',
20567                         'for': id,
20568                         cls: 'control-label box-input-label',
20569                         //cls : 'input-group-addon',
20570                         html : this.fieldLabel
20571                     },
20572                     
20573                     inputblock
20574                     
20575                 ];
20576
20577         } else {
20578             
20579 //                Roo.log(" no label && no align");
20580                 cfg.cn = [  inputblock ] ;
20581                 
20582                 
20583         }
20584         
20585         if(this.boxLabel){
20586              var boxLabelCfg = {
20587                 tag: 'label',
20588                 //'for': id, // box label is handled by onclick - so no for...
20589                 cls: 'box-label',
20590                 html: this.boxLabel
20591             };
20592             
20593             if(this.tooltip){
20594                 boxLabelCfg.tooltip = this.tooltip;
20595             }
20596              
20597             cfg.cn.push(boxLabelCfg);
20598         }
20599         
20600         if(this.inputType != 'radio'){
20601             cfg.cn.push(hidden);
20602         }
20603         
20604         return cfg;
20605         
20606     },
20607     
20608     /**
20609      * return the real input element.
20610      */
20611     inputEl: function ()
20612     {
20613         return this.el.select('input.roo-' + this.inputType,true).first();
20614     },
20615     hiddenEl: function ()
20616     {
20617         return this.el.select('input.roo-hidden-value',true).first();
20618     },
20619     
20620     labelEl: function()
20621     {
20622         return this.el.select('label.control-label',true).first();
20623     },
20624     /* depricated... */
20625     
20626     label: function()
20627     {
20628         return this.labelEl();
20629     },
20630     
20631     boxLabelEl: function()
20632     {
20633         return this.el.select('label.box-label',true).first();
20634     },
20635     
20636     initEvents : function()
20637     {
20638 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20639         
20640         this.inputEl().on('click', this.onClick,  this);
20641         
20642         if (this.boxLabel) { 
20643             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20644         }
20645         
20646         this.startValue = this.getValue();
20647         
20648         if(this.groupId){
20649             Roo.bootstrap.CheckBox.register(this);
20650         }
20651     },
20652     
20653     onClick : function(e)
20654     {   
20655         if(this.fireEvent('click', this, e) !== false){
20656             this.setChecked(!this.checked);
20657         }
20658         
20659     },
20660     
20661     setChecked : function(state,suppressEvent)
20662     {
20663         this.startValue = this.getValue();
20664
20665         if(this.inputType == 'radio'){
20666             
20667             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20668                 e.dom.checked = false;
20669             });
20670             
20671             this.inputEl().dom.checked = true;
20672             
20673             this.inputEl().dom.value = this.inputValue;
20674             
20675             if(suppressEvent !== true){
20676                 this.fireEvent('check', this, true);
20677             }
20678             
20679             this.validate();
20680             
20681             return;
20682         }
20683         
20684         this.checked = state;
20685         
20686         this.inputEl().dom.checked = state;
20687         
20688         
20689         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20690         
20691         if(suppressEvent !== true){
20692             this.fireEvent('check', this, state);
20693         }
20694         
20695         this.validate();
20696     },
20697     
20698     getValue : function()
20699     {
20700         if(this.inputType == 'radio'){
20701             return this.getGroupValue();
20702         }
20703         
20704         return this.hiddenEl().dom.value;
20705         
20706     },
20707     
20708     getGroupValue : function()
20709     {
20710         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20711             return '';
20712         }
20713         
20714         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20715     },
20716     
20717     setValue : function(v,suppressEvent)
20718     {
20719         if(this.inputType == 'radio'){
20720             this.setGroupValue(v, suppressEvent);
20721             return;
20722         }
20723         
20724         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20725         
20726         this.validate();
20727     },
20728     
20729     setGroupValue : function(v, suppressEvent)
20730     {
20731         this.startValue = this.getValue();
20732         
20733         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20734             e.dom.checked = false;
20735             
20736             if(e.dom.value == v){
20737                 e.dom.checked = true;
20738             }
20739         });
20740         
20741         if(suppressEvent !== true){
20742             this.fireEvent('check', this, true);
20743         }
20744
20745         this.validate();
20746         
20747         return;
20748     },
20749     
20750     validate : function()
20751     {
20752         if(this.getVisibilityEl().hasClass('hidden')){
20753             return true;
20754         }
20755         
20756         if(
20757                 this.disabled || 
20758                 (this.inputType == 'radio' && this.validateRadio()) ||
20759                 (this.inputType == 'checkbox' && this.validateCheckbox())
20760         ){
20761             this.markValid();
20762             return true;
20763         }
20764         
20765         this.markInvalid();
20766         return false;
20767     },
20768     
20769     validateRadio : function()
20770     {
20771         if(this.getVisibilityEl().hasClass('hidden')){
20772             return true;
20773         }
20774         
20775         if(this.allowBlank){
20776             return true;
20777         }
20778         
20779         var valid = false;
20780         
20781         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20782             if(!e.dom.checked){
20783                 return;
20784             }
20785             
20786             valid = true;
20787             
20788             return false;
20789         });
20790         
20791         return valid;
20792     },
20793     
20794     validateCheckbox : function()
20795     {
20796         if(!this.groupId){
20797             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20798             //return (this.getValue() == this.inputValue) ? true : false;
20799         }
20800         
20801         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20802         
20803         if(!group){
20804             return false;
20805         }
20806         
20807         var r = false;
20808         
20809         for(var i in group){
20810             if(group[i].el.isVisible(true)){
20811                 r = false;
20812                 break;
20813             }
20814             
20815             r = true;
20816         }
20817         
20818         for(var i in group){
20819             if(r){
20820                 break;
20821             }
20822             
20823             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20824         }
20825         
20826         return r;
20827     },
20828     
20829     /**
20830      * Mark this field as valid
20831      */
20832     markValid : function()
20833     {
20834         var _this = this;
20835         
20836         this.fireEvent('valid', this);
20837         
20838         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20839         
20840         if(this.groupId){
20841             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20842         }
20843         
20844         if(label){
20845             label.markValid();
20846         }
20847
20848         if(this.inputType == 'radio'){
20849             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20850                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20851                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20852             });
20853             
20854             return;
20855         }
20856
20857         if(!this.groupId){
20858             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20859             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20860             return;
20861         }
20862         
20863         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20864         
20865         if(!group){
20866             return;
20867         }
20868         
20869         for(var i in group){
20870             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20871             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20872         }
20873     },
20874     
20875      /**
20876      * Mark this field as invalid
20877      * @param {String} msg The validation message
20878      */
20879     markInvalid : function(msg)
20880     {
20881         if(this.allowBlank){
20882             return;
20883         }
20884         
20885         var _this = this;
20886         
20887         this.fireEvent('invalid', this, msg);
20888         
20889         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20890         
20891         if(this.groupId){
20892             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20893         }
20894         
20895         if(label){
20896             label.markInvalid();
20897         }
20898             
20899         if(this.inputType == 'radio'){
20900             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20901                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20902                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20903             });
20904             
20905             return;
20906         }
20907         
20908         if(!this.groupId){
20909             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20910             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20911             return;
20912         }
20913         
20914         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20915         
20916         if(!group){
20917             return;
20918         }
20919         
20920         for(var i in group){
20921             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20922             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20923         }
20924         
20925     },
20926     
20927     clearInvalid : function()
20928     {
20929         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20930         
20931         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20932         
20933         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20934         
20935         if (label && label.iconEl) {
20936             label.iconEl.removeClass(label.validClass);
20937             label.iconEl.removeClass(label.invalidClass);
20938         }
20939     },
20940     
20941     disable : function()
20942     {
20943         if(this.inputType != 'radio'){
20944             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20945             return;
20946         }
20947         
20948         var _this = this;
20949         
20950         if(this.rendered){
20951             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20952                 _this.getActionEl().addClass(this.disabledClass);
20953                 e.dom.disabled = true;
20954             });
20955         }
20956         
20957         this.disabled = true;
20958         this.fireEvent("disable", this);
20959         return this;
20960     },
20961
20962     enable : function()
20963     {
20964         if(this.inputType != 'radio'){
20965             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20966             return;
20967         }
20968         
20969         var _this = this;
20970         
20971         if(this.rendered){
20972             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20973                 _this.getActionEl().removeClass(this.disabledClass);
20974                 e.dom.disabled = false;
20975             });
20976         }
20977         
20978         this.disabled = false;
20979         this.fireEvent("enable", this);
20980         return this;
20981     },
20982     
20983     setBoxLabel : function(v)
20984     {
20985         this.boxLabel = v;
20986         
20987         if(this.rendered){
20988             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20989         }
20990     }
20991
20992 });
20993
20994 Roo.apply(Roo.bootstrap.CheckBox, {
20995     
20996     groups: {},
20997     
20998      /**
20999     * register a CheckBox Group
21000     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21001     */
21002     register : function(checkbox)
21003     {
21004         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21005             this.groups[checkbox.groupId] = {};
21006         }
21007         
21008         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21009             return;
21010         }
21011         
21012         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21013         
21014     },
21015     /**
21016     * fetch a CheckBox Group based on the group ID
21017     * @param {string} the group ID
21018     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21019     */
21020     get: function(groupId) {
21021         if (typeof(this.groups[groupId]) == 'undefined') {
21022             return false;
21023         }
21024         
21025         return this.groups[groupId] ;
21026     }
21027     
21028     
21029 });
21030 /*
21031  * - LGPL
21032  *
21033  * RadioItem
21034  * 
21035  */
21036
21037 /**
21038  * @class Roo.bootstrap.Radio
21039  * @extends Roo.bootstrap.Component
21040  * Bootstrap Radio class
21041  * @cfg {String} boxLabel - the label associated
21042  * @cfg {String} value - the value of radio
21043  * 
21044  * @constructor
21045  * Create a new Radio
21046  * @param {Object} config The config object
21047  */
21048 Roo.bootstrap.Radio = function(config){
21049     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21050     
21051 };
21052
21053 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21054     
21055     boxLabel : '',
21056     
21057     value : '',
21058     
21059     getAutoCreate : function()
21060     {
21061         var cfg = {
21062             tag : 'div',
21063             cls : 'form-group radio',
21064             cn : [
21065                 {
21066                     tag : 'label',
21067                     cls : 'box-label',
21068                     html : this.boxLabel
21069                 }
21070             ]
21071         };
21072         
21073         return cfg;
21074     },
21075     
21076     initEvents : function() 
21077     {
21078         this.parent().register(this);
21079         
21080         this.el.on('click', this.onClick, this);
21081         
21082     },
21083     
21084     onClick : function(e)
21085     {
21086         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21087             this.setChecked(true);
21088         }
21089     },
21090     
21091     setChecked : function(state, suppressEvent)
21092     {
21093         this.parent().setValue(this.value, suppressEvent);
21094         
21095     },
21096     
21097     setBoxLabel : function(v)
21098     {
21099         this.boxLabel = v;
21100         
21101         if(this.rendered){
21102             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21103         }
21104     }
21105     
21106 });
21107  
21108
21109  /*
21110  * - LGPL
21111  *
21112  * Input
21113  * 
21114  */
21115
21116 /**
21117  * @class Roo.bootstrap.SecurePass
21118  * @extends Roo.bootstrap.Input
21119  * Bootstrap SecurePass class
21120  *
21121  * 
21122  * @constructor
21123  * Create a new SecurePass
21124  * @param {Object} config The config object
21125  */
21126  
21127 Roo.bootstrap.SecurePass = function (config) {
21128     // these go here, so the translation tool can replace them..
21129     this.errors = {
21130         PwdEmpty: "Please type a password, and then retype it to confirm.",
21131         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21132         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21133         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21134         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21135         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21136         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21137         TooWeak: "Your password is Too Weak."
21138     },
21139     this.meterLabel = "Password strength:";
21140     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21141     this.meterClass = [
21142         "roo-password-meter-tooweak", 
21143         "roo-password-meter-weak", 
21144         "roo-password-meter-medium", 
21145         "roo-password-meter-strong", 
21146         "roo-password-meter-grey"
21147     ];
21148     
21149     this.errors = {};
21150     
21151     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21152 }
21153
21154 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21155     /**
21156      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21157      * {
21158      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21159      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21160      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21161      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21162      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21163      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21164      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21165      * })
21166      */
21167     // private
21168     
21169     meterWidth: 300,
21170     errorMsg :'',    
21171     errors: false,
21172     imageRoot: '/',
21173     /**
21174      * @cfg {String/Object} Label for the strength meter (defaults to
21175      * 'Password strength:')
21176      */
21177     // private
21178     meterLabel: '',
21179     /**
21180      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21181      * ['Weak', 'Medium', 'Strong'])
21182      */
21183     // private    
21184     pwdStrengths: false,    
21185     // private
21186     strength: 0,
21187     // private
21188     _lastPwd: null,
21189     // private
21190     kCapitalLetter: 0,
21191     kSmallLetter: 1,
21192     kDigit: 2,
21193     kPunctuation: 3,
21194     
21195     insecure: false,
21196     // private
21197     initEvents: function ()
21198     {
21199         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21200
21201         if (this.el.is('input[type=password]') && Roo.isSafari) {
21202             this.el.on('keydown', this.SafariOnKeyDown, this);
21203         }
21204
21205         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21206     },
21207     // private
21208     onRender: function (ct, position)
21209     {
21210         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21211         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21212         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21213
21214         this.trigger.createChild({
21215                    cn: [
21216                     {
21217                     //id: 'PwdMeter',
21218                     tag: 'div',
21219                     cls: 'roo-password-meter-grey col-xs-12',
21220                     style: {
21221                         //width: 0,
21222                         //width: this.meterWidth + 'px'                                                
21223                         }
21224                     },
21225                     {                            
21226                          cls: 'roo-password-meter-text'                          
21227                     }
21228                 ]            
21229         });
21230
21231          
21232         if (this.hideTrigger) {
21233             this.trigger.setDisplayed(false);
21234         }
21235         this.setSize(this.width || '', this.height || '');
21236     },
21237     // private
21238     onDestroy: function ()
21239     {
21240         if (this.trigger) {
21241             this.trigger.removeAllListeners();
21242             this.trigger.remove();
21243         }
21244         if (this.wrap) {
21245             this.wrap.remove();
21246         }
21247         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21248     },
21249     // private
21250     checkStrength: function ()
21251     {
21252         var pwd = this.inputEl().getValue();
21253         if (pwd == this._lastPwd) {
21254             return;
21255         }
21256
21257         var strength;
21258         if (this.ClientSideStrongPassword(pwd)) {
21259             strength = 3;
21260         } else if (this.ClientSideMediumPassword(pwd)) {
21261             strength = 2;
21262         } else if (this.ClientSideWeakPassword(pwd)) {
21263             strength = 1;
21264         } else {
21265             strength = 0;
21266         }
21267         
21268         Roo.log('strength1: ' + strength);
21269         
21270         //var pm = this.trigger.child('div/div/div').dom;
21271         var pm = this.trigger.child('div/div');
21272         pm.removeClass(this.meterClass);
21273         pm.addClass(this.meterClass[strength]);
21274                 
21275         
21276         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21277                 
21278         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21279         
21280         this._lastPwd = pwd;
21281     },
21282     reset: function ()
21283     {
21284         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21285         
21286         this._lastPwd = '';
21287         
21288         var pm = this.trigger.child('div/div');
21289         pm.removeClass(this.meterClass);
21290         pm.addClass('roo-password-meter-grey');        
21291         
21292         
21293         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21294         
21295         pt.innerHTML = '';
21296         this.inputEl().dom.type='password';
21297     },
21298     // private
21299     validateValue: function (value)
21300     {
21301         
21302         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21303             return false;
21304         }
21305         if (value.length == 0) {
21306             if (this.allowBlank) {
21307                 this.clearInvalid();
21308                 return true;
21309             }
21310
21311             this.markInvalid(this.errors.PwdEmpty);
21312             this.errorMsg = this.errors.PwdEmpty;
21313             return false;
21314         }
21315         
21316         if(this.insecure){
21317             return true;
21318         }
21319         
21320         if ('[\x21-\x7e]*'.match(value)) {
21321             this.markInvalid(this.errors.PwdBadChar);
21322             this.errorMsg = this.errors.PwdBadChar;
21323             return false;
21324         }
21325         if (value.length < 6) {
21326             this.markInvalid(this.errors.PwdShort);
21327             this.errorMsg = this.errors.PwdShort;
21328             return false;
21329         }
21330         if (value.length > 16) {
21331             this.markInvalid(this.errors.PwdLong);
21332             this.errorMsg = this.errors.PwdLong;
21333             return false;
21334         }
21335         var strength;
21336         if (this.ClientSideStrongPassword(value)) {
21337             strength = 3;
21338         } else if (this.ClientSideMediumPassword(value)) {
21339             strength = 2;
21340         } else if (this.ClientSideWeakPassword(value)) {
21341             strength = 1;
21342         } else {
21343             strength = 0;
21344         }
21345
21346         
21347         if (strength < 2) {
21348             //this.markInvalid(this.errors.TooWeak);
21349             this.errorMsg = this.errors.TooWeak;
21350             //return false;
21351         }
21352         
21353         
21354         console.log('strength2: ' + strength);
21355         
21356         //var pm = this.trigger.child('div/div/div').dom;
21357         
21358         var pm = this.trigger.child('div/div');
21359         pm.removeClass(this.meterClass);
21360         pm.addClass(this.meterClass[strength]);
21361                 
21362         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21363                 
21364         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21365         
21366         this.errorMsg = ''; 
21367         return true;
21368     },
21369     // private
21370     CharacterSetChecks: function (type)
21371     {
21372         this.type = type;
21373         this.fResult = false;
21374     },
21375     // private
21376     isctype: function (character, type)
21377     {
21378         switch (type) {  
21379             case this.kCapitalLetter:
21380                 if (character >= 'A' && character <= 'Z') {
21381                     return true;
21382                 }
21383                 break;
21384             
21385             case this.kSmallLetter:
21386                 if (character >= 'a' && character <= 'z') {
21387                     return true;
21388                 }
21389                 break;
21390             
21391             case this.kDigit:
21392                 if (character >= '0' && character <= '9') {
21393                     return true;
21394                 }
21395                 break;
21396             
21397             case this.kPunctuation:
21398                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21399                     return true;
21400                 }
21401                 break;
21402             
21403             default:
21404                 return false;
21405         }
21406
21407     },
21408     // private
21409     IsLongEnough: function (pwd, size)
21410     {
21411         return !(pwd == null || isNaN(size) || pwd.length < size);
21412     },
21413     // private
21414     SpansEnoughCharacterSets: function (word, nb)
21415     {
21416         if (!this.IsLongEnough(word, nb))
21417         {
21418             return false;
21419         }
21420
21421         var characterSetChecks = new Array(
21422             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21423             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21424         );
21425         
21426         for (var index = 0; index < word.length; ++index) {
21427             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21428                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21429                     characterSetChecks[nCharSet].fResult = true;
21430                     break;
21431                 }
21432             }
21433         }
21434
21435         var nCharSets = 0;
21436         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21437             if (characterSetChecks[nCharSet].fResult) {
21438                 ++nCharSets;
21439             }
21440         }
21441
21442         if (nCharSets < nb) {
21443             return false;
21444         }
21445         return true;
21446     },
21447     // private
21448     ClientSideStrongPassword: function (pwd)
21449     {
21450         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21451     },
21452     // private
21453     ClientSideMediumPassword: function (pwd)
21454     {
21455         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21456     },
21457     // private
21458     ClientSideWeakPassword: function (pwd)
21459     {
21460         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21461     }
21462           
21463 })//<script type="text/javascript">
21464
21465 /*
21466  * Based  Ext JS Library 1.1.1
21467  * Copyright(c) 2006-2007, Ext JS, LLC.
21468  * LGPL
21469  *
21470  */
21471  
21472 /**
21473  * @class Roo.HtmlEditorCore
21474  * @extends Roo.Component
21475  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21476  *
21477  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21478  */
21479
21480 Roo.HtmlEditorCore = function(config){
21481     
21482     
21483     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21484     
21485     
21486     this.addEvents({
21487         /**
21488          * @event initialize
21489          * Fires when the editor is fully initialized (including the iframe)
21490          * @param {Roo.HtmlEditorCore} this
21491          */
21492         initialize: true,
21493         /**
21494          * @event activate
21495          * Fires when the editor is first receives the focus. Any insertion must wait
21496          * until after this event.
21497          * @param {Roo.HtmlEditorCore} this
21498          */
21499         activate: true,
21500          /**
21501          * @event beforesync
21502          * Fires before the textarea is updated with content from the editor iframe. Return false
21503          * to cancel the sync.
21504          * @param {Roo.HtmlEditorCore} this
21505          * @param {String} html
21506          */
21507         beforesync: true,
21508          /**
21509          * @event beforepush
21510          * Fires before the iframe editor is updated with content from the textarea. Return false
21511          * to cancel the push.
21512          * @param {Roo.HtmlEditorCore} this
21513          * @param {String} html
21514          */
21515         beforepush: true,
21516          /**
21517          * @event sync
21518          * Fires when the textarea is updated with content from the editor iframe.
21519          * @param {Roo.HtmlEditorCore} this
21520          * @param {String} html
21521          */
21522         sync: true,
21523          /**
21524          * @event push
21525          * Fires when the iframe editor is updated with content from the textarea.
21526          * @param {Roo.HtmlEditorCore} this
21527          * @param {String} html
21528          */
21529         push: true,
21530         
21531         /**
21532          * @event editorevent
21533          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21534          * @param {Roo.HtmlEditorCore} this
21535          */
21536         editorevent: true
21537         
21538     });
21539     
21540     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21541     
21542     // defaults : white / black...
21543     this.applyBlacklists();
21544     
21545     
21546     
21547 };
21548
21549
21550 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21551
21552
21553      /**
21554      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21555      */
21556     
21557     owner : false,
21558     
21559      /**
21560      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21561      *                        Roo.resizable.
21562      */
21563     resizable : false,
21564      /**
21565      * @cfg {Number} height (in pixels)
21566      */   
21567     height: 300,
21568    /**
21569      * @cfg {Number} width (in pixels)
21570      */   
21571     width: 500,
21572     
21573     /**
21574      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21575      * 
21576      */
21577     stylesheets: false,
21578     
21579     // id of frame..
21580     frameId: false,
21581     
21582     // private properties
21583     validationEvent : false,
21584     deferHeight: true,
21585     initialized : false,
21586     activated : false,
21587     sourceEditMode : false,
21588     onFocus : Roo.emptyFn,
21589     iframePad:3,
21590     hideMode:'offsets',
21591     
21592     clearUp: true,
21593     
21594     // blacklist + whitelisted elements..
21595     black: false,
21596     white: false,
21597      
21598     bodyCls : '',
21599
21600     /**
21601      * Protected method that will not generally be called directly. It
21602      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21603      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21604      */
21605     getDocMarkup : function(){
21606         // body styles..
21607         var st = '';
21608         
21609         // inherit styels from page...?? 
21610         if (this.stylesheets === false) {
21611             
21612             Roo.get(document.head).select('style').each(function(node) {
21613                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21614             });
21615             
21616             Roo.get(document.head).select('link').each(function(node) { 
21617                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21618             });
21619             
21620         } else if (!this.stylesheets.length) {
21621                 // simple..
21622                 st = '<style type="text/css">' +
21623                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21624                    '</style>';
21625         } else { 
21626             st = '<style type="text/css">' +
21627                     this.stylesheets +
21628                 '</style>';
21629         }
21630         
21631         st +=  '<style type="text/css">' +
21632             'IMG { cursor: pointer } ' +
21633         '</style>';
21634
21635         var cls = 'roo-htmleditor-body';
21636         
21637         if(this.bodyCls.length){
21638             cls += ' ' + this.bodyCls;
21639         }
21640         
21641         return '<html><head>' + st  +
21642             //<style type="text/css">' +
21643             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21644             //'</style>' +
21645             ' </head><body class="' +  cls + '"></body></html>';
21646     },
21647
21648     // private
21649     onRender : function(ct, position)
21650     {
21651         var _t = this;
21652         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21653         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21654         
21655         
21656         this.el.dom.style.border = '0 none';
21657         this.el.dom.setAttribute('tabIndex', -1);
21658         this.el.addClass('x-hidden hide');
21659         
21660         
21661         
21662         if(Roo.isIE){ // fix IE 1px bogus margin
21663             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21664         }
21665        
21666         
21667         this.frameId = Roo.id();
21668         
21669          
21670         
21671         var iframe = this.owner.wrap.createChild({
21672             tag: 'iframe',
21673             cls: 'form-control', // bootstrap..
21674             id: this.frameId,
21675             name: this.frameId,
21676             frameBorder : 'no',
21677             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21678         }, this.el
21679         );
21680         
21681         
21682         this.iframe = iframe.dom;
21683
21684          this.assignDocWin();
21685         
21686         this.doc.designMode = 'on';
21687        
21688         this.doc.open();
21689         this.doc.write(this.getDocMarkup());
21690         this.doc.close();
21691
21692         
21693         var task = { // must defer to wait for browser to be ready
21694             run : function(){
21695                 //console.log("run task?" + this.doc.readyState);
21696                 this.assignDocWin();
21697                 if(this.doc.body || this.doc.readyState == 'complete'){
21698                     try {
21699                         this.doc.designMode="on";
21700                     } catch (e) {
21701                         return;
21702                     }
21703                     Roo.TaskMgr.stop(task);
21704                     this.initEditor.defer(10, this);
21705                 }
21706             },
21707             interval : 10,
21708             duration: 10000,
21709             scope: this
21710         };
21711         Roo.TaskMgr.start(task);
21712
21713     },
21714
21715     // private
21716     onResize : function(w, h)
21717     {
21718          Roo.log('resize: ' +w + ',' + h );
21719         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21720         if(!this.iframe){
21721             return;
21722         }
21723         if(typeof w == 'number'){
21724             
21725             this.iframe.style.width = w + 'px';
21726         }
21727         if(typeof h == 'number'){
21728             
21729             this.iframe.style.height = h + 'px';
21730             if(this.doc){
21731                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21732             }
21733         }
21734         
21735     },
21736
21737     /**
21738      * Toggles the editor between standard and source edit mode.
21739      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21740      */
21741     toggleSourceEdit : function(sourceEditMode){
21742         
21743         this.sourceEditMode = sourceEditMode === true;
21744         
21745         if(this.sourceEditMode){
21746  
21747             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21748             
21749         }else{
21750             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21751             //this.iframe.className = '';
21752             this.deferFocus();
21753         }
21754         //this.setSize(this.owner.wrap.getSize());
21755         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21756     },
21757
21758     
21759   
21760
21761     /**
21762      * Protected method that will not generally be called directly. If you need/want
21763      * custom HTML cleanup, this is the method you should override.
21764      * @param {String} html The HTML to be cleaned
21765      * return {String} The cleaned HTML
21766      */
21767     cleanHtml : function(html){
21768         html = String(html);
21769         if(html.length > 5){
21770             if(Roo.isSafari){ // strip safari nonsense
21771                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21772             }
21773         }
21774         if(html == '&nbsp;'){
21775             html = '';
21776         }
21777         return html;
21778     },
21779
21780     /**
21781      * HTML Editor -> Textarea
21782      * Protected method that will not generally be called directly. Syncs the contents
21783      * of the editor iframe with the textarea.
21784      */
21785     syncValue : function(){
21786         if(this.initialized){
21787             var bd = (this.doc.body || this.doc.documentElement);
21788             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21789             var html = bd.innerHTML;
21790             if(Roo.isSafari){
21791                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21792                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21793                 if(m && m[1]){
21794                     html = '<div style="'+m[0]+'">' + html + '</div>';
21795                 }
21796             }
21797             html = this.cleanHtml(html);
21798             // fix up the special chars.. normaly like back quotes in word...
21799             // however we do not want to do this with chinese..
21800             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21801                 var cc = b.charCodeAt();
21802                 if (
21803                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21804                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21805                     (cc >= 0xf900 && cc < 0xfb00 )
21806                 ) {
21807                         return b;
21808                 }
21809                 return "&#"+cc+";" 
21810             });
21811             if(this.owner.fireEvent('beforesync', this, html) !== false){
21812                 this.el.dom.value = html;
21813                 this.owner.fireEvent('sync', this, html);
21814             }
21815         }
21816     },
21817
21818     /**
21819      * Protected method that will not generally be called directly. Pushes the value of the textarea
21820      * into the iframe editor.
21821      */
21822     pushValue : function(){
21823         if(this.initialized){
21824             var v = this.el.dom.value.trim();
21825             
21826 //            if(v.length < 1){
21827 //                v = '&#160;';
21828 //            }
21829             
21830             if(this.owner.fireEvent('beforepush', this, v) !== false){
21831                 var d = (this.doc.body || this.doc.documentElement);
21832                 d.innerHTML = v;
21833                 this.cleanUpPaste();
21834                 this.el.dom.value = d.innerHTML;
21835                 this.owner.fireEvent('push', this, v);
21836             }
21837         }
21838     },
21839
21840     // private
21841     deferFocus : function(){
21842         this.focus.defer(10, this);
21843     },
21844
21845     // doc'ed in Field
21846     focus : function(){
21847         if(this.win && !this.sourceEditMode){
21848             this.win.focus();
21849         }else{
21850             this.el.focus();
21851         }
21852     },
21853     
21854     assignDocWin: function()
21855     {
21856         var iframe = this.iframe;
21857         
21858          if(Roo.isIE){
21859             this.doc = iframe.contentWindow.document;
21860             this.win = iframe.contentWindow;
21861         } else {
21862 //            if (!Roo.get(this.frameId)) {
21863 //                return;
21864 //            }
21865 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21866 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21867             
21868             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21869                 return;
21870             }
21871             
21872             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21873             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21874         }
21875     },
21876     
21877     // private
21878     initEditor : function(){
21879         //console.log("INIT EDITOR");
21880         this.assignDocWin();
21881         
21882         
21883         
21884         this.doc.designMode="on";
21885         this.doc.open();
21886         this.doc.write(this.getDocMarkup());
21887         this.doc.close();
21888         
21889         var dbody = (this.doc.body || this.doc.documentElement);
21890         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21891         // this copies styles from the containing element into thsi one..
21892         // not sure why we need all of this..
21893         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21894         
21895         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21896         //ss['background-attachment'] = 'fixed'; // w3c
21897         dbody.bgProperties = 'fixed'; // ie
21898         //Roo.DomHelper.applyStyles(dbody, ss);
21899         Roo.EventManager.on(this.doc, {
21900             //'mousedown': this.onEditorEvent,
21901             'mouseup': this.onEditorEvent,
21902             'dblclick': this.onEditorEvent,
21903             'click': this.onEditorEvent,
21904             'keyup': this.onEditorEvent,
21905             buffer:100,
21906             scope: this
21907         });
21908         if(Roo.isGecko){
21909             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21910         }
21911         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21912             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21913         }
21914         this.initialized = true;
21915
21916         this.owner.fireEvent('initialize', this);
21917         this.pushValue();
21918     },
21919
21920     // private
21921     onDestroy : function(){
21922         
21923         
21924         
21925         if(this.rendered){
21926             
21927             //for (var i =0; i < this.toolbars.length;i++) {
21928             //    // fixme - ask toolbars for heights?
21929             //    this.toolbars[i].onDestroy();
21930            // }
21931             
21932             //this.wrap.dom.innerHTML = '';
21933             //this.wrap.remove();
21934         }
21935     },
21936
21937     // private
21938     onFirstFocus : function(){
21939         
21940         this.assignDocWin();
21941         
21942         
21943         this.activated = true;
21944          
21945     
21946         if(Roo.isGecko){ // prevent silly gecko errors
21947             this.win.focus();
21948             var s = this.win.getSelection();
21949             if(!s.focusNode || s.focusNode.nodeType != 3){
21950                 var r = s.getRangeAt(0);
21951                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21952                 r.collapse(true);
21953                 this.deferFocus();
21954             }
21955             try{
21956                 this.execCmd('useCSS', true);
21957                 this.execCmd('styleWithCSS', false);
21958             }catch(e){}
21959         }
21960         this.owner.fireEvent('activate', this);
21961     },
21962
21963     // private
21964     adjustFont: function(btn){
21965         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21966         //if(Roo.isSafari){ // safari
21967         //    adjust *= 2;
21968        // }
21969         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21970         if(Roo.isSafari){ // safari
21971             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21972             v =  (v < 10) ? 10 : v;
21973             v =  (v > 48) ? 48 : v;
21974             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21975             
21976         }
21977         
21978         
21979         v = Math.max(1, v+adjust);
21980         
21981         this.execCmd('FontSize', v  );
21982     },
21983
21984     onEditorEvent : function(e)
21985     {
21986         this.owner.fireEvent('editorevent', this, e);
21987       //  this.updateToolbar();
21988         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21989     },
21990
21991     insertTag : function(tg)
21992     {
21993         // could be a bit smarter... -> wrap the current selected tRoo..
21994         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21995             
21996             range = this.createRange(this.getSelection());
21997             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21998             wrappingNode.appendChild(range.extractContents());
21999             range.insertNode(wrappingNode);
22000
22001             return;
22002             
22003             
22004             
22005         }
22006         this.execCmd("formatblock",   tg);
22007         
22008     },
22009     
22010     insertText : function(txt)
22011     {
22012         
22013         
22014         var range = this.createRange();
22015         range.deleteContents();
22016                //alert(Sender.getAttribute('label'));
22017                
22018         range.insertNode(this.doc.createTextNode(txt));
22019     } ,
22020     
22021      
22022
22023     /**
22024      * Executes a Midas editor command on the editor document and performs necessary focus and
22025      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22026      * @param {String} cmd The Midas command
22027      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22028      */
22029     relayCmd : function(cmd, value){
22030         this.win.focus();
22031         this.execCmd(cmd, value);
22032         this.owner.fireEvent('editorevent', this);
22033         //this.updateToolbar();
22034         this.owner.deferFocus();
22035     },
22036
22037     /**
22038      * Executes a Midas editor command directly on the editor document.
22039      * For visual commands, you should use {@link #relayCmd} instead.
22040      * <b>This should only be called after the editor is initialized.</b>
22041      * @param {String} cmd The Midas command
22042      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22043      */
22044     execCmd : function(cmd, value){
22045         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22046         this.syncValue();
22047     },
22048  
22049  
22050    
22051     /**
22052      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22053      * to insert tRoo.
22054      * @param {String} text | dom node.. 
22055      */
22056     insertAtCursor : function(text)
22057     {
22058         
22059         if(!this.activated){
22060             return;
22061         }
22062         /*
22063         if(Roo.isIE){
22064             this.win.focus();
22065             var r = this.doc.selection.createRange();
22066             if(r){
22067                 r.collapse(true);
22068                 r.pasteHTML(text);
22069                 this.syncValue();
22070                 this.deferFocus();
22071             
22072             }
22073             return;
22074         }
22075         */
22076         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22077             this.win.focus();
22078             
22079             
22080             // from jquery ui (MIT licenced)
22081             var range, node;
22082             var win = this.win;
22083             
22084             if (win.getSelection && win.getSelection().getRangeAt) {
22085                 range = win.getSelection().getRangeAt(0);
22086                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22087                 range.insertNode(node);
22088             } else if (win.document.selection && win.document.selection.createRange) {
22089                 // no firefox support
22090                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22091                 win.document.selection.createRange().pasteHTML(txt);
22092             } else {
22093                 // no firefox support
22094                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22095                 this.execCmd('InsertHTML', txt);
22096             } 
22097             
22098             this.syncValue();
22099             
22100             this.deferFocus();
22101         }
22102     },
22103  // private
22104     mozKeyPress : function(e){
22105         if(e.ctrlKey){
22106             var c = e.getCharCode(), cmd;
22107           
22108             if(c > 0){
22109                 c = String.fromCharCode(c).toLowerCase();
22110                 switch(c){
22111                     case 'b':
22112                         cmd = 'bold';
22113                         break;
22114                     case 'i':
22115                         cmd = 'italic';
22116                         break;
22117                     
22118                     case 'u':
22119                         cmd = 'underline';
22120                         break;
22121                     
22122                     case 'v':
22123                         this.cleanUpPaste.defer(100, this);
22124                         return;
22125                         
22126                 }
22127                 if(cmd){
22128                     this.win.focus();
22129                     this.execCmd(cmd);
22130                     this.deferFocus();
22131                     e.preventDefault();
22132                 }
22133                 
22134             }
22135         }
22136     },
22137
22138     // private
22139     fixKeys : function(){ // load time branching for fastest keydown performance
22140         if(Roo.isIE){
22141             return function(e){
22142                 var k = e.getKey(), r;
22143                 if(k == e.TAB){
22144                     e.stopEvent();
22145                     r = this.doc.selection.createRange();
22146                     if(r){
22147                         r.collapse(true);
22148                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22149                         this.deferFocus();
22150                     }
22151                     return;
22152                 }
22153                 
22154                 if(k == e.ENTER){
22155                     r = this.doc.selection.createRange();
22156                     if(r){
22157                         var target = r.parentElement();
22158                         if(!target || target.tagName.toLowerCase() != 'li'){
22159                             e.stopEvent();
22160                             r.pasteHTML('<br />');
22161                             r.collapse(false);
22162                             r.select();
22163                         }
22164                     }
22165                 }
22166                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22167                     this.cleanUpPaste.defer(100, this);
22168                     return;
22169                 }
22170                 
22171                 
22172             };
22173         }else if(Roo.isOpera){
22174             return function(e){
22175                 var k = e.getKey();
22176                 if(k == e.TAB){
22177                     e.stopEvent();
22178                     this.win.focus();
22179                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22180                     this.deferFocus();
22181                 }
22182                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22183                     this.cleanUpPaste.defer(100, this);
22184                     return;
22185                 }
22186                 
22187             };
22188         }else if(Roo.isSafari){
22189             return function(e){
22190                 var k = e.getKey();
22191                 
22192                 if(k == e.TAB){
22193                     e.stopEvent();
22194                     this.execCmd('InsertText','\t');
22195                     this.deferFocus();
22196                     return;
22197                 }
22198                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22199                     this.cleanUpPaste.defer(100, this);
22200                     return;
22201                 }
22202                 
22203              };
22204         }
22205     }(),
22206     
22207     getAllAncestors: function()
22208     {
22209         var p = this.getSelectedNode();
22210         var a = [];
22211         if (!p) {
22212             a.push(p); // push blank onto stack..
22213             p = this.getParentElement();
22214         }
22215         
22216         
22217         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22218             a.push(p);
22219             p = p.parentNode;
22220         }
22221         a.push(this.doc.body);
22222         return a;
22223     },
22224     lastSel : false,
22225     lastSelNode : false,
22226     
22227     
22228     getSelection : function() 
22229     {
22230         this.assignDocWin();
22231         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22232     },
22233     
22234     getSelectedNode: function() 
22235     {
22236         // this may only work on Gecko!!!
22237         
22238         // should we cache this!!!!
22239         
22240         
22241         
22242          
22243         var range = this.createRange(this.getSelection()).cloneRange();
22244         
22245         if (Roo.isIE) {
22246             var parent = range.parentElement();
22247             while (true) {
22248                 var testRange = range.duplicate();
22249                 testRange.moveToElementText(parent);
22250                 if (testRange.inRange(range)) {
22251                     break;
22252                 }
22253                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22254                     break;
22255                 }
22256                 parent = parent.parentElement;
22257             }
22258             return parent;
22259         }
22260         
22261         // is ancestor a text element.
22262         var ac =  range.commonAncestorContainer;
22263         if (ac.nodeType == 3) {
22264             ac = ac.parentNode;
22265         }
22266         
22267         var ar = ac.childNodes;
22268          
22269         var nodes = [];
22270         var other_nodes = [];
22271         var has_other_nodes = false;
22272         for (var i=0;i<ar.length;i++) {
22273             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22274                 continue;
22275             }
22276             // fullly contained node.
22277             
22278             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22279                 nodes.push(ar[i]);
22280                 continue;
22281             }
22282             
22283             // probably selected..
22284             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22285                 other_nodes.push(ar[i]);
22286                 continue;
22287             }
22288             // outer..
22289             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22290                 continue;
22291             }
22292             
22293             
22294             has_other_nodes = true;
22295         }
22296         if (!nodes.length && other_nodes.length) {
22297             nodes= other_nodes;
22298         }
22299         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22300             return false;
22301         }
22302         
22303         return nodes[0];
22304     },
22305     createRange: function(sel)
22306     {
22307         // this has strange effects when using with 
22308         // top toolbar - not sure if it's a great idea.
22309         //this.editor.contentWindow.focus();
22310         if (typeof sel != "undefined") {
22311             try {
22312                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22313             } catch(e) {
22314                 return this.doc.createRange();
22315             }
22316         } else {
22317             return this.doc.createRange();
22318         }
22319     },
22320     getParentElement: function()
22321     {
22322         
22323         this.assignDocWin();
22324         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22325         
22326         var range = this.createRange(sel);
22327          
22328         try {
22329             var p = range.commonAncestorContainer;
22330             while (p.nodeType == 3) { // text node
22331                 p = p.parentNode;
22332             }
22333             return p;
22334         } catch (e) {
22335             return null;
22336         }
22337     
22338     },
22339     /***
22340      *
22341      * Range intersection.. the hard stuff...
22342      *  '-1' = before
22343      *  '0' = hits..
22344      *  '1' = after.
22345      *         [ -- selected range --- ]
22346      *   [fail]                        [fail]
22347      *
22348      *    basically..
22349      *      if end is before start or  hits it. fail.
22350      *      if start is after end or hits it fail.
22351      *
22352      *   if either hits (but other is outside. - then it's not 
22353      *   
22354      *    
22355      **/
22356     
22357     
22358     // @see http://www.thismuchiknow.co.uk/?p=64.
22359     rangeIntersectsNode : function(range, node)
22360     {
22361         var nodeRange = node.ownerDocument.createRange();
22362         try {
22363             nodeRange.selectNode(node);
22364         } catch (e) {
22365             nodeRange.selectNodeContents(node);
22366         }
22367     
22368         var rangeStartRange = range.cloneRange();
22369         rangeStartRange.collapse(true);
22370     
22371         var rangeEndRange = range.cloneRange();
22372         rangeEndRange.collapse(false);
22373     
22374         var nodeStartRange = nodeRange.cloneRange();
22375         nodeStartRange.collapse(true);
22376     
22377         var nodeEndRange = nodeRange.cloneRange();
22378         nodeEndRange.collapse(false);
22379     
22380         return rangeStartRange.compareBoundaryPoints(
22381                  Range.START_TO_START, nodeEndRange) == -1 &&
22382                rangeEndRange.compareBoundaryPoints(
22383                  Range.START_TO_START, nodeStartRange) == 1;
22384         
22385          
22386     },
22387     rangeCompareNode : function(range, node)
22388     {
22389         var nodeRange = node.ownerDocument.createRange();
22390         try {
22391             nodeRange.selectNode(node);
22392         } catch (e) {
22393             nodeRange.selectNodeContents(node);
22394         }
22395         
22396         
22397         range.collapse(true);
22398     
22399         nodeRange.collapse(true);
22400      
22401         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22402         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22403          
22404         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22405         
22406         var nodeIsBefore   =  ss == 1;
22407         var nodeIsAfter    = ee == -1;
22408         
22409         if (nodeIsBefore && nodeIsAfter) {
22410             return 0; // outer
22411         }
22412         if (!nodeIsBefore && nodeIsAfter) {
22413             return 1; //right trailed.
22414         }
22415         
22416         if (nodeIsBefore && !nodeIsAfter) {
22417             return 2;  // left trailed.
22418         }
22419         // fully contined.
22420         return 3;
22421     },
22422
22423     // private? - in a new class?
22424     cleanUpPaste :  function()
22425     {
22426         // cleans up the whole document..
22427         Roo.log('cleanuppaste');
22428         
22429         this.cleanUpChildren(this.doc.body);
22430         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22431         if (clean != this.doc.body.innerHTML) {
22432             this.doc.body.innerHTML = clean;
22433         }
22434         
22435     },
22436     
22437     cleanWordChars : function(input) {// change the chars to hex code
22438         var he = Roo.HtmlEditorCore;
22439         
22440         var output = input;
22441         Roo.each(he.swapCodes, function(sw) { 
22442             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22443             
22444             output = output.replace(swapper, sw[1]);
22445         });
22446         
22447         return output;
22448     },
22449     
22450     
22451     cleanUpChildren : function (n)
22452     {
22453         if (!n.childNodes.length) {
22454             return;
22455         }
22456         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22457            this.cleanUpChild(n.childNodes[i]);
22458         }
22459     },
22460     
22461     
22462         
22463     
22464     cleanUpChild : function (node)
22465     {
22466         var ed = this;
22467         //console.log(node);
22468         if (node.nodeName == "#text") {
22469             // clean up silly Windows -- stuff?
22470             return; 
22471         }
22472         if (node.nodeName == "#comment") {
22473             node.parentNode.removeChild(node);
22474             // clean up silly Windows -- stuff?
22475             return; 
22476         }
22477         var lcname = node.tagName.toLowerCase();
22478         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22479         // whitelist of tags..
22480         
22481         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22482             // remove node.
22483             node.parentNode.removeChild(node);
22484             return;
22485             
22486         }
22487         
22488         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22489         
22490         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22491         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22492         
22493         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22494         //    remove_keep_children = true;
22495         //}
22496         
22497         if (remove_keep_children) {
22498             this.cleanUpChildren(node);
22499             // inserts everything just before this node...
22500             while (node.childNodes.length) {
22501                 var cn = node.childNodes[0];
22502                 node.removeChild(cn);
22503                 node.parentNode.insertBefore(cn, node);
22504             }
22505             node.parentNode.removeChild(node);
22506             return;
22507         }
22508         
22509         if (!node.attributes || !node.attributes.length) {
22510             this.cleanUpChildren(node);
22511             return;
22512         }
22513         
22514         function cleanAttr(n,v)
22515         {
22516             
22517             if (v.match(/^\./) || v.match(/^\//)) {
22518                 return;
22519             }
22520             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22521                 return;
22522             }
22523             if (v.match(/^#/)) {
22524                 return;
22525             }
22526 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22527             node.removeAttribute(n);
22528             
22529         }
22530         
22531         var cwhite = this.cwhite;
22532         var cblack = this.cblack;
22533             
22534         function cleanStyle(n,v)
22535         {
22536             if (v.match(/expression/)) { //XSS?? should we even bother..
22537                 node.removeAttribute(n);
22538                 return;
22539             }
22540             
22541             var parts = v.split(/;/);
22542             var clean = [];
22543             
22544             Roo.each(parts, function(p) {
22545                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22546                 if (!p.length) {
22547                     return true;
22548                 }
22549                 var l = p.split(':').shift().replace(/\s+/g,'');
22550                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22551                 
22552                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22553 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22554                     //node.removeAttribute(n);
22555                     return true;
22556                 }
22557                 //Roo.log()
22558                 // only allow 'c whitelisted system attributes'
22559                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22560 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22561                     //node.removeAttribute(n);
22562                     return true;
22563                 }
22564                 
22565                 
22566                  
22567                 
22568                 clean.push(p);
22569                 return true;
22570             });
22571             if (clean.length) { 
22572                 node.setAttribute(n, clean.join(';'));
22573             } else {
22574                 node.removeAttribute(n);
22575             }
22576             
22577         }
22578         
22579         
22580         for (var i = node.attributes.length-1; i > -1 ; i--) {
22581             var a = node.attributes[i];
22582             //console.log(a);
22583             
22584             if (a.name.toLowerCase().substr(0,2)=='on')  {
22585                 node.removeAttribute(a.name);
22586                 continue;
22587             }
22588             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22589                 node.removeAttribute(a.name);
22590                 continue;
22591             }
22592             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22593                 cleanAttr(a.name,a.value); // fixme..
22594                 continue;
22595             }
22596             if (a.name == 'style') {
22597                 cleanStyle(a.name,a.value);
22598                 continue;
22599             }
22600             /// clean up MS crap..
22601             // tecnically this should be a list of valid class'es..
22602             
22603             
22604             if (a.name == 'class') {
22605                 if (a.value.match(/^Mso/)) {
22606                     node.className = '';
22607                 }
22608                 
22609                 if (a.value.match(/^body$/)) {
22610                     node.className = '';
22611                 }
22612                 continue;
22613             }
22614             
22615             // style cleanup!?
22616             // class cleanup?
22617             
22618         }
22619         
22620         
22621         this.cleanUpChildren(node);
22622         
22623         
22624     },
22625     
22626     /**
22627      * Clean up MS wordisms...
22628      */
22629     cleanWord : function(node)
22630     {
22631         
22632         
22633         if (!node) {
22634             this.cleanWord(this.doc.body);
22635             return;
22636         }
22637         if (node.nodeName == "#text") {
22638             // clean up silly Windows -- stuff?
22639             return; 
22640         }
22641         if (node.nodeName == "#comment") {
22642             node.parentNode.removeChild(node);
22643             // clean up silly Windows -- stuff?
22644             return; 
22645         }
22646         
22647         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22648             node.parentNode.removeChild(node);
22649             return;
22650         }
22651         
22652         // remove - but keep children..
22653         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22654             while (node.childNodes.length) {
22655                 var cn = node.childNodes[0];
22656                 node.removeChild(cn);
22657                 node.parentNode.insertBefore(cn, node);
22658             }
22659             node.parentNode.removeChild(node);
22660             this.iterateChildren(node, this.cleanWord);
22661             return;
22662         }
22663         // clean styles
22664         if (node.className.length) {
22665             
22666             var cn = node.className.split(/\W+/);
22667             var cna = [];
22668             Roo.each(cn, function(cls) {
22669                 if (cls.match(/Mso[a-zA-Z]+/)) {
22670                     return;
22671                 }
22672                 cna.push(cls);
22673             });
22674             node.className = cna.length ? cna.join(' ') : '';
22675             if (!cna.length) {
22676                 node.removeAttribute("class");
22677             }
22678         }
22679         
22680         if (node.hasAttribute("lang")) {
22681             node.removeAttribute("lang");
22682         }
22683         
22684         if (node.hasAttribute("style")) {
22685             
22686             var styles = node.getAttribute("style").split(";");
22687             var nstyle = [];
22688             Roo.each(styles, function(s) {
22689                 if (!s.match(/:/)) {
22690                     return;
22691                 }
22692                 var kv = s.split(":");
22693                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22694                     return;
22695                 }
22696                 // what ever is left... we allow.
22697                 nstyle.push(s);
22698             });
22699             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22700             if (!nstyle.length) {
22701                 node.removeAttribute('style');
22702             }
22703         }
22704         this.iterateChildren(node, this.cleanWord);
22705         
22706         
22707         
22708     },
22709     /**
22710      * iterateChildren of a Node, calling fn each time, using this as the scole..
22711      * @param {DomNode} node node to iterate children of.
22712      * @param {Function} fn method of this class to call on each item.
22713      */
22714     iterateChildren : function(node, fn)
22715     {
22716         if (!node.childNodes.length) {
22717                 return;
22718         }
22719         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22720            fn.call(this, node.childNodes[i])
22721         }
22722     },
22723     
22724     
22725     /**
22726      * cleanTableWidths.
22727      *
22728      * Quite often pasting from word etc.. results in tables with column and widths.
22729      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22730      *
22731      */
22732     cleanTableWidths : function(node)
22733     {
22734          
22735          
22736         if (!node) {
22737             this.cleanTableWidths(this.doc.body);
22738             return;
22739         }
22740         
22741         // ignore list...
22742         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22743             return; 
22744         }
22745         Roo.log(node.tagName);
22746         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22747             this.iterateChildren(node, this.cleanTableWidths);
22748             return;
22749         }
22750         if (node.hasAttribute('width')) {
22751             node.removeAttribute('width');
22752         }
22753         
22754          
22755         if (node.hasAttribute("style")) {
22756             // pretty basic...
22757             
22758             var styles = node.getAttribute("style").split(";");
22759             var nstyle = [];
22760             Roo.each(styles, function(s) {
22761                 if (!s.match(/:/)) {
22762                     return;
22763                 }
22764                 var kv = s.split(":");
22765                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22766                     return;
22767                 }
22768                 // what ever is left... we allow.
22769                 nstyle.push(s);
22770             });
22771             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22772             if (!nstyle.length) {
22773                 node.removeAttribute('style');
22774             }
22775         }
22776         
22777         this.iterateChildren(node, this.cleanTableWidths);
22778         
22779         
22780     },
22781     
22782     
22783     
22784     
22785     domToHTML : function(currentElement, depth, nopadtext) {
22786         
22787         depth = depth || 0;
22788         nopadtext = nopadtext || false;
22789     
22790         if (!currentElement) {
22791             return this.domToHTML(this.doc.body);
22792         }
22793         
22794         //Roo.log(currentElement);
22795         var j;
22796         var allText = false;
22797         var nodeName = currentElement.nodeName;
22798         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22799         
22800         if  (nodeName == '#text') {
22801             
22802             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22803         }
22804         
22805         
22806         var ret = '';
22807         if (nodeName != 'BODY') {
22808              
22809             var i = 0;
22810             // Prints the node tagName, such as <A>, <IMG>, etc
22811             if (tagName) {
22812                 var attr = [];
22813                 for(i = 0; i < currentElement.attributes.length;i++) {
22814                     // quoting?
22815                     var aname = currentElement.attributes.item(i).name;
22816                     if (!currentElement.attributes.item(i).value.length) {
22817                         continue;
22818                     }
22819                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22820                 }
22821                 
22822                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22823             } 
22824             else {
22825                 
22826                 // eack
22827             }
22828         } else {
22829             tagName = false;
22830         }
22831         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22832             return ret;
22833         }
22834         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22835             nopadtext = true;
22836         }
22837         
22838         
22839         // Traverse the tree
22840         i = 0;
22841         var currentElementChild = currentElement.childNodes.item(i);
22842         var allText = true;
22843         var innerHTML  = '';
22844         lastnode = '';
22845         while (currentElementChild) {
22846             // Formatting code (indent the tree so it looks nice on the screen)
22847             var nopad = nopadtext;
22848             if (lastnode == 'SPAN') {
22849                 nopad  = true;
22850             }
22851             // text
22852             if  (currentElementChild.nodeName == '#text') {
22853                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22854                 toadd = nopadtext ? toadd : toadd.trim();
22855                 if (!nopad && toadd.length > 80) {
22856                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22857                 }
22858                 innerHTML  += toadd;
22859                 
22860                 i++;
22861                 currentElementChild = currentElement.childNodes.item(i);
22862                 lastNode = '';
22863                 continue;
22864             }
22865             allText = false;
22866             
22867             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22868                 
22869             // Recursively traverse the tree structure of the child node
22870             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22871             lastnode = currentElementChild.nodeName;
22872             i++;
22873             currentElementChild=currentElement.childNodes.item(i);
22874         }
22875         
22876         ret += innerHTML;
22877         
22878         if (!allText) {
22879                 // The remaining code is mostly for formatting the tree
22880             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22881         }
22882         
22883         
22884         if (tagName) {
22885             ret+= "</"+tagName+">";
22886         }
22887         return ret;
22888         
22889     },
22890         
22891     applyBlacklists : function()
22892     {
22893         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22894         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22895         
22896         this.white = [];
22897         this.black = [];
22898         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22899             if (b.indexOf(tag) > -1) {
22900                 return;
22901             }
22902             this.white.push(tag);
22903             
22904         }, this);
22905         
22906         Roo.each(w, function(tag) {
22907             if (b.indexOf(tag) > -1) {
22908                 return;
22909             }
22910             if (this.white.indexOf(tag) > -1) {
22911                 return;
22912             }
22913             this.white.push(tag);
22914             
22915         }, this);
22916         
22917         
22918         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22919             if (w.indexOf(tag) > -1) {
22920                 return;
22921             }
22922             this.black.push(tag);
22923             
22924         }, this);
22925         
22926         Roo.each(b, function(tag) {
22927             if (w.indexOf(tag) > -1) {
22928                 return;
22929             }
22930             if (this.black.indexOf(tag) > -1) {
22931                 return;
22932             }
22933             this.black.push(tag);
22934             
22935         }, this);
22936         
22937         
22938         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22939         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22940         
22941         this.cwhite = [];
22942         this.cblack = [];
22943         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22944             if (b.indexOf(tag) > -1) {
22945                 return;
22946             }
22947             this.cwhite.push(tag);
22948             
22949         }, this);
22950         
22951         Roo.each(w, function(tag) {
22952             if (b.indexOf(tag) > -1) {
22953                 return;
22954             }
22955             if (this.cwhite.indexOf(tag) > -1) {
22956                 return;
22957             }
22958             this.cwhite.push(tag);
22959             
22960         }, this);
22961         
22962         
22963         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22964             if (w.indexOf(tag) > -1) {
22965                 return;
22966             }
22967             this.cblack.push(tag);
22968             
22969         }, this);
22970         
22971         Roo.each(b, function(tag) {
22972             if (w.indexOf(tag) > -1) {
22973                 return;
22974             }
22975             if (this.cblack.indexOf(tag) > -1) {
22976                 return;
22977             }
22978             this.cblack.push(tag);
22979             
22980         }, this);
22981     },
22982     
22983     setStylesheets : function(stylesheets)
22984     {
22985         if(typeof(stylesheets) == 'string'){
22986             Roo.get(this.iframe.contentDocument.head).createChild({
22987                 tag : 'link',
22988                 rel : 'stylesheet',
22989                 type : 'text/css',
22990                 href : stylesheets
22991             });
22992             
22993             return;
22994         }
22995         var _this = this;
22996      
22997         Roo.each(stylesheets, function(s) {
22998             if(!s.length){
22999                 return;
23000             }
23001             
23002             Roo.get(_this.iframe.contentDocument.head).createChild({
23003                 tag : 'link',
23004                 rel : 'stylesheet',
23005                 type : 'text/css',
23006                 href : s
23007             });
23008         });
23009
23010         
23011     },
23012     
23013     removeStylesheets : function()
23014     {
23015         var _this = this;
23016         
23017         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23018             s.remove();
23019         });
23020     },
23021     
23022     setStyle : function(style)
23023     {
23024         Roo.get(this.iframe.contentDocument.head).createChild({
23025             tag : 'style',
23026             type : 'text/css',
23027             html : style
23028         });
23029
23030         return;
23031     }
23032     
23033     // hide stuff that is not compatible
23034     /**
23035      * @event blur
23036      * @hide
23037      */
23038     /**
23039      * @event change
23040      * @hide
23041      */
23042     /**
23043      * @event focus
23044      * @hide
23045      */
23046     /**
23047      * @event specialkey
23048      * @hide
23049      */
23050     /**
23051      * @cfg {String} fieldClass @hide
23052      */
23053     /**
23054      * @cfg {String} focusClass @hide
23055      */
23056     /**
23057      * @cfg {String} autoCreate @hide
23058      */
23059     /**
23060      * @cfg {String} inputType @hide
23061      */
23062     /**
23063      * @cfg {String} invalidClass @hide
23064      */
23065     /**
23066      * @cfg {String} invalidText @hide
23067      */
23068     /**
23069      * @cfg {String} msgFx @hide
23070      */
23071     /**
23072      * @cfg {String} validateOnBlur @hide
23073      */
23074 });
23075
23076 Roo.HtmlEditorCore.white = [
23077         'area', 'br', 'img', 'input', 'hr', 'wbr',
23078         
23079        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23080        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23081        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23082        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23083        'table',   'ul',         'xmp', 
23084        
23085        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23086       'thead',   'tr', 
23087      
23088       'dir', 'menu', 'ol', 'ul', 'dl',
23089        
23090       'embed',  'object'
23091 ];
23092
23093
23094 Roo.HtmlEditorCore.black = [
23095     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23096         'applet', // 
23097         'base',   'basefont', 'bgsound', 'blink',  'body', 
23098         'frame',  'frameset', 'head',    'html',   'ilayer', 
23099         'iframe', 'layer',  'link',     'meta',    'object',   
23100         'script', 'style' ,'title',  'xml' // clean later..
23101 ];
23102 Roo.HtmlEditorCore.clean = [
23103     'script', 'style', 'title', 'xml'
23104 ];
23105 Roo.HtmlEditorCore.remove = [
23106     'font'
23107 ];
23108 // attributes..
23109
23110 Roo.HtmlEditorCore.ablack = [
23111     'on'
23112 ];
23113     
23114 Roo.HtmlEditorCore.aclean = [ 
23115     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23116 ];
23117
23118 // protocols..
23119 Roo.HtmlEditorCore.pwhite= [
23120         'http',  'https',  'mailto'
23121 ];
23122
23123 // white listed style attributes.
23124 Roo.HtmlEditorCore.cwhite= [
23125       //  'text-align', /// default is to allow most things..
23126       
23127          
23128 //        'font-size'//??
23129 ];
23130
23131 // black listed style attributes.
23132 Roo.HtmlEditorCore.cblack= [
23133       //  'font-size' -- this can be set by the project 
23134 ];
23135
23136
23137 Roo.HtmlEditorCore.swapCodes   =[ 
23138     [    8211, "--" ], 
23139     [    8212, "--" ], 
23140     [    8216,  "'" ],  
23141     [    8217, "'" ],  
23142     [    8220, '"' ],  
23143     [    8221, '"' ],  
23144     [    8226, "*" ],  
23145     [    8230, "..." ]
23146 ]; 
23147
23148     /*
23149  * - LGPL
23150  *
23151  * HtmlEditor
23152  * 
23153  */
23154
23155 /**
23156  * @class Roo.bootstrap.HtmlEditor
23157  * @extends Roo.bootstrap.TextArea
23158  * Bootstrap HtmlEditor class
23159
23160  * @constructor
23161  * Create a new HtmlEditor
23162  * @param {Object} config The config object
23163  */
23164
23165 Roo.bootstrap.HtmlEditor = function(config){
23166     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23167     if (!this.toolbars) {
23168         this.toolbars = [];
23169     }
23170     
23171     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23172     this.addEvents({
23173             /**
23174              * @event initialize
23175              * Fires when the editor is fully initialized (including the iframe)
23176              * @param {HtmlEditor} this
23177              */
23178             initialize: true,
23179             /**
23180              * @event activate
23181              * Fires when the editor is first receives the focus. Any insertion must wait
23182              * until after this event.
23183              * @param {HtmlEditor} this
23184              */
23185             activate: true,
23186              /**
23187              * @event beforesync
23188              * Fires before the textarea is updated with content from the editor iframe. Return false
23189              * to cancel the sync.
23190              * @param {HtmlEditor} this
23191              * @param {String} html
23192              */
23193             beforesync: true,
23194              /**
23195              * @event beforepush
23196              * Fires before the iframe editor is updated with content from the textarea. Return false
23197              * to cancel the push.
23198              * @param {HtmlEditor} this
23199              * @param {String} html
23200              */
23201             beforepush: true,
23202              /**
23203              * @event sync
23204              * Fires when the textarea is updated with content from the editor iframe.
23205              * @param {HtmlEditor} this
23206              * @param {String} html
23207              */
23208             sync: true,
23209              /**
23210              * @event push
23211              * Fires when the iframe editor is updated with content from the textarea.
23212              * @param {HtmlEditor} this
23213              * @param {String} html
23214              */
23215             push: true,
23216              /**
23217              * @event editmodechange
23218              * Fires when the editor switches edit modes
23219              * @param {HtmlEditor} this
23220              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23221              */
23222             editmodechange: true,
23223             /**
23224              * @event editorevent
23225              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23226              * @param {HtmlEditor} this
23227              */
23228             editorevent: true,
23229             /**
23230              * @event firstfocus
23231              * Fires when on first focus - needed by toolbars..
23232              * @param {HtmlEditor} this
23233              */
23234             firstfocus: true,
23235             /**
23236              * @event autosave
23237              * Auto save the htmlEditor value as a file into Events
23238              * @param {HtmlEditor} this
23239              */
23240             autosave: true,
23241             /**
23242              * @event savedpreview
23243              * preview the saved version of htmlEditor
23244              * @param {HtmlEditor} this
23245              */
23246             savedpreview: true
23247         });
23248 };
23249
23250
23251 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23252     
23253     
23254       /**
23255      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23256      */
23257     toolbars : false,
23258     
23259      /**
23260     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23261     */
23262     btns : [],
23263    
23264      /**
23265      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23266      *                        Roo.resizable.
23267      */
23268     resizable : false,
23269      /**
23270      * @cfg {Number} height (in pixels)
23271      */   
23272     height: 300,
23273    /**
23274      * @cfg {Number} width (in pixels)
23275      */   
23276     width: false,
23277     
23278     /**
23279      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23280      * 
23281      */
23282     stylesheets: false,
23283     
23284     // id of frame..
23285     frameId: false,
23286     
23287     // private properties
23288     validationEvent : false,
23289     deferHeight: true,
23290     initialized : false,
23291     activated : false,
23292     
23293     onFocus : Roo.emptyFn,
23294     iframePad:3,
23295     hideMode:'offsets',
23296     
23297     tbContainer : false,
23298     
23299     bodyCls : '',
23300     
23301     toolbarContainer :function() {
23302         return this.wrap.select('.x-html-editor-tb',true).first();
23303     },
23304
23305     /**
23306      * Protected method that will not generally be called directly. It
23307      * is called when the editor creates its toolbar. Override this method if you need to
23308      * add custom toolbar buttons.
23309      * @param {HtmlEditor} editor
23310      */
23311     createToolbar : function(){
23312         Roo.log('renewing');
23313         Roo.log("create toolbars");
23314         
23315         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23316         this.toolbars[0].render(this.toolbarContainer());
23317         
23318         return;
23319         
23320 //        if (!editor.toolbars || !editor.toolbars.length) {
23321 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23322 //        }
23323 //        
23324 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23325 //            editor.toolbars[i] = Roo.factory(
23326 //                    typeof(editor.toolbars[i]) == 'string' ?
23327 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23328 //                Roo.bootstrap.HtmlEditor);
23329 //            editor.toolbars[i].init(editor);
23330 //        }
23331     },
23332
23333      
23334     // private
23335     onRender : function(ct, position)
23336     {
23337        // Roo.log("Call onRender: " + this.xtype);
23338         var _t = this;
23339         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23340       
23341         this.wrap = this.inputEl().wrap({
23342             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23343         });
23344         
23345         this.editorcore.onRender(ct, position);
23346          
23347         if (this.resizable) {
23348             this.resizeEl = new Roo.Resizable(this.wrap, {
23349                 pinned : true,
23350                 wrap: true,
23351                 dynamic : true,
23352                 minHeight : this.height,
23353                 height: this.height,
23354                 handles : this.resizable,
23355                 width: this.width,
23356                 listeners : {
23357                     resize : function(r, w, h) {
23358                         _t.onResize(w,h); // -something
23359                     }
23360                 }
23361             });
23362             
23363         }
23364         this.createToolbar(this);
23365        
23366         
23367         if(!this.width && this.resizable){
23368             this.setSize(this.wrap.getSize());
23369         }
23370         if (this.resizeEl) {
23371             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23372             // should trigger onReize..
23373         }
23374         
23375     },
23376
23377     // private
23378     onResize : function(w, h)
23379     {
23380         Roo.log('resize: ' +w + ',' + h );
23381         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23382         var ew = false;
23383         var eh = false;
23384         
23385         if(this.inputEl() ){
23386             if(typeof w == 'number'){
23387                 var aw = w - this.wrap.getFrameWidth('lr');
23388                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23389                 ew = aw;
23390             }
23391             if(typeof h == 'number'){
23392                  var tbh = -11;  // fixme it needs to tool bar size!
23393                 for (var i =0; i < this.toolbars.length;i++) {
23394                     // fixme - ask toolbars for heights?
23395                     tbh += this.toolbars[i].el.getHeight();
23396                     //if (this.toolbars[i].footer) {
23397                     //    tbh += this.toolbars[i].footer.el.getHeight();
23398                     //}
23399                 }
23400               
23401                 
23402                 
23403                 
23404                 
23405                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23406                 ah -= 5; // knock a few pixes off for look..
23407                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23408                 var eh = ah;
23409             }
23410         }
23411         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23412         this.editorcore.onResize(ew,eh);
23413         
23414     },
23415
23416     /**
23417      * Toggles the editor between standard and source edit mode.
23418      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23419      */
23420     toggleSourceEdit : function(sourceEditMode)
23421     {
23422         this.editorcore.toggleSourceEdit(sourceEditMode);
23423         
23424         if(this.editorcore.sourceEditMode){
23425             Roo.log('editor - showing textarea');
23426             
23427 //            Roo.log('in');
23428 //            Roo.log(this.syncValue());
23429             this.syncValue();
23430             this.inputEl().removeClass(['hide', 'x-hidden']);
23431             this.inputEl().dom.removeAttribute('tabIndex');
23432             this.inputEl().focus();
23433         }else{
23434             Roo.log('editor - hiding textarea');
23435 //            Roo.log('out')
23436 //            Roo.log(this.pushValue()); 
23437             this.pushValue();
23438             
23439             this.inputEl().addClass(['hide', 'x-hidden']);
23440             this.inputEl().dom.setAttribute('tabIndex', -1);
23441             //this.deferFocus();
23442         }
23443          
23444         if(this.resizable){
23445             this.setSize(this.wrap.getSize());
23446         }
23447         
23448         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23449     },
23450  
23451     // private (for BoxComponent)
23452     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23453
23454     // private (for BoxComponent)
23455     getResizeEl : function(){
23456         return this.wrap;
23457     },
23458
23459     // private (for BoxComponent)
23460     getPositionEl : function(){
23461         return this.wrap;
23462     },
23463
23464     // private
23465     initEvents : function(){
23466         this.originalValue = this.getValue();
23467     },
23468
23469 //    /**
23470 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23471 //     * @method
23472 //     */
23473 //    markInvalid : Roo.emptyFn,
23474 //    /**
23475 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23476 //     * @method
23477 //     */
23478 //    clearInvalid : Roo.emptyFn,
23479
23480     setValue : function(v){
23481         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23482         this.editorcore.pushValue();
23483     },
23484
23485      
23486     // private
23487     deferFocus : function(){
23488         this.focus.defer(10, this);
23489     },
23490
23491     // doc'ed in Field
23492     focus : function(){
23493         this.editorcore.focus();
23494         
23495     },
23496       
23497
23498     // private
23499     onDestroy : function(){
23500         
23501         
23502         
23503         if(this.rendered){
23504             
23505             for (var i =0; i < this.toolbars.length;i++) {
23506                 // fixme - ask toolbars for heights?
23507                 this.toolbars[i].onDestroy();
23508             }
23509             
23510             this.wrap.dom.innerHTML = '';
23511             this.wrap.remove();
23512         }
23513     },
23514
23515     // private
23516     onFirstFocus : function(){
23517         //Roo.log("onFirstFocus");
23518         this.editorcore.onFirstFocus();
23519          for (var i =0; i < this.toolbars.length;i++) {
23520             this.toolbars[i].onFirstFocus();
23521         }
23522         
23523     },
23524     
23525     // private
23526     syncValue : function()
23527     {   
23528         this.editorcore.syncValue();
23529     },
23530     
23531     pushValue : function()
23532     {   
23533         this.editorcore.pushValue();
23534     }
23535      
23536     
23537     // hide stuff that is not compatible
23538     /**
23539      * @event blur
23540      * @hide
23541      */
23542     /**
23543      * @event change
23544      * @hide
23545      */
23546     /**
23547      * @event focus
23548      * @hide
23549      */
23550     /**
23551      * @event specialkey
23552      * @hide
23553      */
23554     /**
23555      * @cfg {String} fieldClass @hide
23556      */
23557     /**
23558      * @cfg {String} focusClass @hide
23559      */
23560     /**
23561      * @cfg {String} autoCreate @hide
23562      */
23563     /**
23564      * @cfg {String} inputType @hide
23565      */
23566     /**
23567      * @cfg {String} invalidClass @hide
23568      */
23569     /**
23570      * @cfg {String} invalidText @hide
23571      */
23572     /**
23573      * @cfg {String} msgFx @hide
23574      */
23575     /**
23576      * @cfg {String} validateOnBlur @hide
23577      */
23578 });
23579  
23580     
23581    
23582    
23583    
23584       
23585 Roo.namespace('Roo.bootstrap.htmleditor');
23586 /**
23587  * @class Roo.bootstrap.HtmlEditorToolbar1
23588  * Basic Toolbar
23589  * 
23590  * Usage:
23591  *
23592  new Roo.bootstrap.HtmlEditor({
23593     ....
23594     toolbars : [
23595         new Roo.bootstrap.HtmlEditorToolbar1({
23596             disable : { fonts: 1 , format: 1, ..., ... , ...],
23597             btns : [ .... ]
23598         })
23599     }
23600      
23601  * 
23602  * @cfg {Object} disable List of elements to disable..
23603  * @cfg {Array} btns List of additional buttons.
23604  * 
23605  * 
23606  * NEEDS Extra CSS? 
23607  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23608  */
23609  
23610 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23611 {
23612     
23613     Roo.apply(this, config);
23614     
23615     // default disabled, based on 'good practice'..
23616     this.disable = this.disable || {};
23617     Roo.applyIf(this.disable, {
23618         fontSize : true,
23619         colors : true,
23620         specialElements : true
23621     });
23622     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23623     
23624     this.editor = config.editor;
23625     this.editorcore = config.editor.editorcore;
23626     
23627     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23628     
23629     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23630     // dont call parent... till later.
23631 }
23632 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23633      
23634     bar : true,
23635     
23636     editor : false,
23637     editorcore : false,
23638     
23639     
23640     formats : [
23641         "p" ,  
23642         "h1","h2","h3","h4","h5","h6", 
23643         "pre", "code", 
23644         "abbr", "acronym", "address", "cite", "samp", "var",
23645         'div','span'
23646     ],
23647     
23648     onRender : function(ct, position)
23649     {
23650        // Roo.log("Call onRender: " + this.xtype);
23651         
23652        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23653        Roo.log(this.el);
23654        this.el.dom.style.marginBottom = '0';
23655        var _this = this;
23656        var editorcore = this.editorcore;
23657        var editor= this.editor;
23658        
23659        var children = [];
23660        var btn = function(id,cmd , toggle, handler, html){
23661        
23662             var  event = toggle ? 'toggle' : 'click';
23663        
23664             var a = {
23665                 size : 'sm',
23666                 xtype: 'Button',
23667                 xns: Roo.bootstrap,
23668                 glyphicon : id,
23669                 cmd : id || cmd,
23670                 enableToggle:toggle !== false,
23671                 html : html || '',
23672                 pressed : toggle ? false : null,
23673                 listeners : {}
23674             };
23675             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23676                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23677             };
23678             children.push(a);
23679             return a;
23680        }
23681        
23682     //    var cb_box = function...
23683         
23684         var style = {
23685                 xtype: 'Button',
23686                 size : 'sm',
23687                 xns: Roo.bootstrap,
23688                 glyphicon : 'font',
23689                 //html : 'submit'
23690                 menu : {
23691                     xtype: 'Menu',
23692                     xns: Roo.bootstrap,
23693                     items:  []
23694                 }
23695         };
23696         Roo.each(this.formats, function(f) {
23697             style.menu.items.push({
23698                 xtype :'MenuItem',
23699                 xns: Roo.bootstrap,
23700                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23701                 tagname : f,
23702                 listeners : {
23703                     click : function()
23704                     {
23705                         editorcore.insertTag(this.tagname);
23706                         editor.focus();
23707                     }
23708                 }
23709                 
23710             });
23711         });
23712         children.push(style);   
23713         
23714         btn('bold',false,true);
23715         btn('italic',false,true);
23716         btn('align-left', 'justifyleft',true);
23717         btn('align-center', 'justifycenter',true);
23718         btn('align-right' , 'justifyright',true);
23719         btn('link', false, false, function(btn) {
23720             //Roo.log("create link?");
23721             var url = prompt(this.createLinkText, this.defaultLinkValue);
23722             if(url && url != 'http:/'+'/'){
23723                 this.editorcore.relayCmd('createlink', url);
23724             }
23725         }),
23726         btn('list','insertunorderedlist',true);
23727         btn('pencil', false,true, function(btn){
23728                 Roo.log(this);
23729                 this.toggleSourceEdit(btn.pressed);
23730         });
23731         
23732         if (this.editor.btns.length > 0) {
23733             for (var i = 0; i<this.editor.btns.length; i++) {
23734                 children.push(this.editor.btns[i]);
23735             }
23736         }
23737         
23738         /*
23739         var cog = {
23740                 xtype: 'Button',
23741                 size : 'sm',
23742                 xns: Roo.bootstrap,
23743                 glyphicon : 'cog',
23744                 //html : 'submit'
23745                 menu : {
23746                     xtype: 'Menu',
23747                     xns: Roo.bootstrap,
23748                     items:  []
23749                 }
23750         };
23751         
23752         cog.menu.items.push({
23753             xtype :'MenuItem',
23754             xns: Roo.bootstrap,
23755             html : Clean styles,
23756             tagname : f,
23757             listeners : {
23758                 click : function()
23759                 {
23760                     editorcore.insertTag(this.tagname);
23761                     editor.focus();
23762                 }
23763             }
23764             
23765         });
23766        */
23767         
23768          
23769        this.xtype = 'NavSimplebar';
23770         
23771         for(var i=0;i< children.length;i++) {
23772             
23773             this.buttons.add(this.addxtypeChild(children[i]));
23774             
23775         }
23776         
23777         editor.on('editorevent', this.updateToolbar, this);
23778     },
23779     onBtnClick : function(id)
23780     {
23781        this.editorcore.relayCmd(id);
23782        this.editorcore.focus();
23783     },
23784     
23785     /**
23786      * Protected method that will not generally be called directly. It triggers
23787      * a toolbar update by reading the markup state of the current selection in the editor.
23788      */
23789     updateToolbar: function(){
23790
23791         if(!this.editorcore.activated){
23792             this.editor.onFirstFocus(); // is this neeed?
23793             return;
23794         }
23795
23796         var btns = this.buttons; 
23797         var doc = this.editorcore.doc;
23798         btns.get('bold').setActive(doc.queryCommandState('bold'));
23799         btns.get('italic').setActive(doc.queryCommandState('italic'));
23800         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23801         
23802         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23803         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23804         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23805         
23806         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23807         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23808          /*
23809         
23810         var ans = this.editorcore.getAllAncestors();
23811         if (this.formatCombo) {
23812             
23813             
23814             var store = this.formatCombo.store;
23815             this.formatCombo.setValue("");
23816             for (var i =0; i < ans.length;i++) {
23817                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23818                     // select it..
23819                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23820                     break;
23821                 }
23822             }
23823         }
23824         
23825         
23826         
23827         // hides menus... - so this cant be on a menu...
23828         Roo.bootstrap.MenuMgr.hideAll();
23829         */
23830         Roo.bootstrap.MenuMgr.hideAll();
23831         //this.editorsyncValue();
23832     },
23833     onFirstFocus: function() {
23834         this.buttons.each(function(item){
23835            item.enable();
23836         });
23837     },
23838     toggleSourceEdit : function(sourceEditMode){
23839         
23840           
23841         if(sourceEditMode){
23842             Roo.log("disabling buttons");
23843            this.buttons.each( function(item){
23844                 if(item.cmd != 'pencil'){
23845                     item.disable();
23846                 }
23847             });
23848           
23849         }else{
23850             Roo.log("enabling buttons");
23851             if(this.editorcore.initialized){
23852                 this.buttons.each( function(item){
23853                     item.enable();
23854                 });
23855             }
23856             
23857         }
23858         Roo.log("calling toggole on editor");
23859         // tell the editor that it's been pressed..
23860         this.editor.toggleSourceEdit(sourceEditMode);
23861        
23862     }
23863 });
23864
23865
23866
23867
23868
23869 /**
23870  * @class Roo.bootstrap.Table.AbstractSelectionModel
23871  * @extends Roo.util.Observable
23872  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23873  * implemented by descendant classes.  This class should not be directly instantiated.
23874  * @constructor
23875  */
23876 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23877     this.locked = false;
23878     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23879 };
23880
23881
23882 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23883     /** @ignore Called by the grid automatically. Do not call directly. */
23884     init : function(grid){
23885         this.grid = grid;
23886         this.initEvents();
23887     },
23888
23889     /**
23890      * Locks the selections.
23891      */
23892     lock : function(){
23893         this.locked = true;
23894     },
23895
23896     /**
23897      * Unlocks the selections.
23898      */
23899     unlock : function(){
23900         this.locked = false;
23901     },
23902
23903     /**
23904      * Returns true if the selections are locked.
23905      * @return {Boolean}
23906      */
23907     isLocked : function(){
23908         return this.locked;
23909     }
23910 });
23911 /**
23912  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23913  * @class Roo.bootstrap.Table.RowSelectionModel
23914  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23915  * It supports multiple selections and keyboard selection/navigation. 
23916  * @constructor
23917  * @param {Object} config
23918  */
23919
23920 Roo.bootstrap.Table.RowSelectionModel = function(config){
23921     Roo.apply(this, config);
23922     this.selections = new Roo.util.MixedCollection(false, function(o){
23923         return o.id;
23924     });
23925
23926     this.last = false;
23927     this.lastActive = false;
23928
23929     this.addEvents({
23930         /**
23931              * @event selectionchange
23932              * Fires when the selection changes
23933              * @param {SelectionModel} this
23934              */
23935             "selectionchange" : true,
23936         /**
23937              * @event afterselectionchange
23938              * Fires after the selection changes (eg. by key press or clicking)
23939              * @param {SelectionModel} this
23940              */
23941             "afterselectionchange" : true,
23942         /**
23943              * @event beforerowselect
23944              * Fires when a row is selected being selected, return false to cancel.
23945              * @param {SelectionModel} this
23946              * @param {Number} rowIndex The selected index
23947              * @param {Boolean} keepExisting False if other selections will be cleared
23948              */
23949             "beforerowselect" : true,
23950         /**
23951              * @event rowselect
23952              * Fires when a row is selected.
23953              * @param {SelectionModel} this
23954              * @param {Number} rowIndex The selected index
23955              * @param {Roo.data.Record} r The record
23956              */
23957             "rowselect" : true,
23958         /**
23959              * @event rowdeselect
23960              * Fires when a row is deselected.
23961              * @param {SelectionModel} this
23962              * @param {Number} rowIndex The selected index
23963              */
23964         "rowdeselect" : true
23965     });
23966     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23967     this.locked = false;
23968  };
23969
23970 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23971     /**
23972      * @cfg {Boolean} singleSelect
23973      * True to allow selection of only one row at a time (defaults to false)
23974      */
23975     singleSelect : false,
23976
23977     // private
23978     initEvents : function()
23979     {
23980
23981         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23982         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23983         //}else{ // allow click to work like normal
23984          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23985         //}
23986         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23987         this.grid.on("rowclick", this.handleMouseDown, this);
23988         
23989         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23990             "up" : function(e){
23991                 if(!e.shiftKey){
23992                     this.selectPrevious(e.shiftKey);
23993                 }else if(this.last !== false && this.lastActive !== false){
23994                     var last = this.last;
23995                     this.selectRange(this.last,  this.lastActive-1);
23996                     this.grid.getView().focusRow(this.lastActive);
23997                     if(last !== false){
23998                         this.last = last;
23999                     }
24000                 }else{
24001                     this.selectFirstRow();
24002                 }
24003                 this.fireEvent("afterselectionchange", this);
24004             },
24005             "down" : function(e){
24006                 if(!e.shiftKey){
24007                     this.selectNext(e.shiftKey);
24008                 }else if(this.last !== false && this.lastActive !== false){
24009                     var last = this.last;
24010                     this.selectRange(this.last,  this.lastActive+1);
24011                     this.grid.getView().focusRow(this.lastActive);
24012                     if(last !== false){
24013                         this.last = last;
24014                     }
24015                 }else{
24016                     this.selectFirstRow();
24017                 }
24018                 this.fireEvent("afterselectionchange", this);
24019             },
24020             scope: this
24021         });
24022         this.grid.store.on('load', function(){
24023             this.selections.clear();
24024         },this);
24025         /*
24026         var view = this.grid.view;
24027         view.on("refresh", this.onRefresh, this);
24028         view.on("rowupdated", this.onRowUpdated, this);
24029         view.on("rowremoved", this.onRemove, this);
24030         */
24031     },
24032
24033     // private
24034     onRefresh : function()
24035     {
24036         var ds = this.grid.store, i, v = this.grid.view;
24037         var s = this.selections;
24038         s.each(function(r){
24039             if((i = ds.indexOfId(r.id)) != -1){
24040                 v.onRowSelect(i);
24041             }else{
24042                 s.remove(r);
24043             }
24044         });
24045     },
24046
24047     // private
24048     onRemove : function(v, index, r){
24049         this.selections.remove(r);
24050     },
24051
24052     // private
24053     onRowUpdated : function(v, index, r){
24054         if(this.isSelected(r)){
24055             v.onRowSelect(index);
24056         }
24057     },
24058
24059     /**
24060      * Select records.
24061      * @param {Array} records The records to select
24062      * @param {Boolean} keepExisting (optional) True to keep existing selections
24063      */
24064     selectRecords : function(records, keepExisting)
24065     {
24066         if(!keepExisting){
24067             this.clearSelections();
24068         }
24069             var ds = this.grid.store;
24070         for(var i = 0, len = records.length; i < len; i++){
24071             this.selectRow(ds.indexOf(records[i]), true);
24072         }
24073     },
24074
24075     /**
24076      * Gets the number of selected rows.
24077      * @return {Number}
24078      */
24079     getCount : function(){
24080         return this.selections.length;
24081     },
24082
24083     /**
24084      * Selects the first row in the grid.
24085      */
24086     selectFirstRow : function(){
24087         this.selectRow(0);
24088     },
24089
24090     /**
24091      * Select the last row.
24092      * @param {Boolean} keepExisting (optional) True to keep existing selections
24093      */
24094     selectLastRow : function(keepExisting){
24095         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24096         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24097     },
24098
24099     /**
24100      * Selects the row immediately following the last selected row.
24101      * @param {Boolean} keepExisting (optional) True to keep existing selections
24102      */
24103     selectNext : function(keepExisting)
24104     {
24105             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24106             this.selectRow(this.last+1, keepExisting);
24107             this.grid.getView().focusRow(this.last);
24108         }
24109     },
24110
24111     /**
24112      * Selects the row that precedes the last selected row.
24113      * @param {Boolean} keepExisting (optional) True to keep existing selections
24114      */
24115     selectPrevious : function(keepExisting){
24116         if(this.last){
24117             this.selectRow(this.last-1, keepExisting);
24118             this.grid.getView().focusRow(this.last);
24119         }
24120     },
24121
24122     /**
24123      * Returns the selected records
24124      * @return {Array} Array of selected records
24125      */
24126     getSelections : function(){
24127         return [].concat(this.selections.items);
24128     },
24129
24130     /**
24131      * Returns the first selected record.
24132      * @return {Record}
24133      */
24134     getSelected : function(){
24135         return this.selections.itemAt(0);
24136     },
24137
24138
24139     /**
24140      * Clears all selections.
24141      */
24142     clearSelections : function(fast)
24143     {
24144         if(this.locked) {
24145             return;
24146         }
24147         if(fast !== true){
24148                 var ds = this.grid.store;
24149             var s = this.selections;
24150             s.each(function(r){
24151                 this.deselectRow(ds.indexOfId(r.id));
24152             }, this);
24153             s.clear();
24154         }else{
24155             this.selections.clear();
24156         }
24157         this.last = false;
24158     },
24159
24160
24161     /**
24162      * Selects all rows.
24163      */
24164     selectAll : function(){
24165         if(this.locked) {
24166             return;
24167         }
24168         this.selections.clear();
24169         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24170             this.selectRow(i, true);
24171         }
24172     },
24173
24174     /**
24175      * Returns True if there is a selection.
24176      * @return {Boolean}
24177      */
24178     hasSelection : function(){
24179         return this.selections.length > 0;
24180     },
24181
24182     /**
24183      * Returns True if the specified row is selected.
24184      * @param {Number/Record} record The record or index of the record to check
24185      * @return {Boolean}
24186      */
24187     isSelected : function(index){
24188             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24189         return (r && this.selections.key(r.id) ? true : false);
24190     },
24191
24192     /**
24193      * Returns True if the specified record id is selected.
24194      * @param {String} id The id of record to check
24195      * @return {Boolean}
24196      */
24197     isIdSelected : function(id){
24198         return (this.selections.key(id) ? true : false);
24199     },
24200
24201
24202     // private
24203     handleMouseDBClick : function(e, t){
24204         
24205     },
24206     // private
24207     handleMouseDown : function(e, t)
24208     {
24209             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24210         if(this.isLocked() || rowIndex < 0 ){
24211             return;
24212         };
24213         if(e.shiftKey && this.last !== false){
24214             var last = this.last;
24215             this.selectRange(last, rowIndex, e.ctrlKey);
24216             this.last = last; // reset the last
24217             t.focus();
24218     
24219         }else{
24220             var isSelected = this.isSelected(rowIndex);
24221             //Roo.log("select row:" + rowIndex);
24222             if(isSelected){
24223                 this.deselectRow(rowIndex);
24224             } else {
24225                         this.selectRow(rowIndex, true);
24226             }
24227     
24228             /*
24229                 if(e.button !== 0 && isSelected){
24230                 alert('rowIndex 2: ' + rowIndex);
24231                     view.focusRow(rowIndex);
24232                 }else if(e.ctrlKey && isSelected){
24233                     this.deselectRow(rowIndex);
24234                 }else if(!isSelected){
24235                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24236                     view.focusRow(rowIndex);
24237                 }
24238             */
24239         }
24240         this.fireEvent("afterselectionchange", this);
24241     },
24242     // private
24243     handleDragableRowClick :  function(grid, rowIndex, e) 
24244     {
24245         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24246             this.selectRow(rowIndex, false);
24247             grid.view.focusRow(rowIndex);
24248              this.fireEvent("afterselectionchange", this);
24249         }
24250     },
24251     
24252     /**
24253      * Selects multiple rows.
24254      * @param {Array} rows Array of the indexes of the row to select
24255      * @param {Boolean} keepExisting (optional) True to keep existing selections
24256      */
24257     selectRows : function(rows, keepExisting){
24258         if(!keepExisting){
24259             this.clearSelections();
24260         }
24261         for(var i = 0, len = rows.length; i < len; i++){
24262             this.selectRow(rows[i], true);
24263         }
24264     },
24265
24266     /**
24267      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24268      * @param {Number} startRow The index of the first row in the range
24269      * @param {Number} endRow The index of the last row in the range
24270      * @param {Boolean} keepExisting (optional) True to retain existing selections
24271      */
24272     selectRange : function(startRow, endRow, keepExisting){
24273         if(this.locked) {
24274             return;
24275         }
24276         if(!keepExisting){
24277             this.clearSelections();
24278         }
24279         if(startRow <= endRow){
24280             for(var i = startRow; i <= endRow; i++){
24281                 this.selectRow(i, true);
24282             }
24283         }else{
24284             for(var i = startRow; i >= endRow; i--){
24285                 this.selectRow(i, true);
24286             }
24287         }
24288     },
24289
24290     /**
24291      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24292      * @param {Number} startRow The index of the first row in the range
24293      * @param {Number} endRow The index of the last row in the range
24294      */
24295     deselectRange : function(startRow, endRow, preventViewNotify){
24296         if(this.locked) {
24297             return;
24298         }
24299         for(var i = startRow; i <= endRow; i++){
24300             this.deselectRow(i, preventViewNotify);
24301         }
24302     },
24303
24304     /**
24305      * Selects a row.
24306      * @param {Number} row The index of the row to select
24307      * @param {Boolean} keepExisting (optional) True to keep existing selections
24308      */
24309     selectRow : function(index, keepExisting, preventViewNotify)
24310     {
24311             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24312             return;
24313         }
24314         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24315             if(!keepExisting || this.singleSelect){
24316                 this.clearSelections();
24317             }
24318             
24319             var r = this.grid.store.getAt(index);
24320             //console.log('selectRow - record id :' + r.id);
24321             
24322             this.selections.add(r);
24323             this.last = this.lastActive = index;
24324             if(!preventViewNotify){
24325                 var proxy = new Roo.Element(
24326                                 this.grid.getRowDom(index)
24327                 );
24328                 proxy.addClass('bg-info info');
24329             }
24330             this.fireEvent("rowselect", this, index, r);
24331             this.fireEvent("selectionchange", this);
24332         }
24333     },
24334
24335     /**
24336      * Deselects a row.
24337      * @param {Number} row The index of the row to deselect
24338      */
24339     deselectRow : function(index, preventViewNotify)
24340     {
24341         if(this.locked) {
24342             return;
24343         }
24344         if(this.last == index){
24345             this.last = false;
24346         }
24347         if(this.lastActive == index){
24348             this.lastActive = false;
24349         }
24350         
24351         var r = this.grid.store.getAt(index);
24352         if (!r) {
24353             return;
24354         }
24355         
24356         this.selections.remove(r);
24357         //.console.log('deselectRow - record id :' + r.id);
24358         if(!preventViewNotify){
24359         
24360             var proxy = new Roo.Element(
24361                 this.grid.getRowDom(index)
24362             );
24363             proxy.removeClass('bg-info info');
24364         }
24365         this.fireEvent("rowdeselect", this, index);
24366         this.fireEvent("selectionchange", this);
24367     },
24368
24369     // private
24370     restoreLast : function(){
24371         if(this._last){
24372             this.last = this._last;
24373         }
24374     },
24375
24376     // private
24377     acceptsNav : function(row, col, cm){
24378         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24379     },
24380
24381     // private
24382     onEditorKey : function(field, e){
24383         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24384         if(k == e.TAB){
24385             e.stopEvent();
24386             ed.completeEdit();
24387             if(e.shiftKey){
24388                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24389             }else{
24390                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24391             }
24392         }else if(k == e.ENTER && !e.ctrlKey){
24393             e.stopEvent();
24394             ed.completeEdit();
24395             if(e.shiftKey){
24396                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24397             }else{
24398                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24399             }
24400         }else if(k == e.ESC){
24401             ed.cancelEdit();
24402         }
24403         if(newCell){
24404             g.startEditing(newCell[0], newCell[1]);
24405         }
24406     }
24407 });
24408 /*
24409  * Based on:
24410  * Ext JS Library 1.1.1
24411  * Copyright(c) 2006-2007, Ext JS, LLC.
24412  *
24413  * Originally Released Under LGPL - original licence link has changed is not relivant.
24414  *
24415  * Fork - LGPL
24416  * <script type="text/javascript">
24417  */
24418  
24419 /**
24420  * @class Roo.bootstrap.PagingToolbar
24421  * @extends Roo.bootstrap.NavSimplebar
24422  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24423  * @constructor
24424  * Create a new PagingToolbar
24425  * @param {Object} config The config object
24426  * @param {Roo.data.Store} store
24427  */
24428 Roo.bootstrap.PagingToolbar = function(config)
24429 {
24430     // old args format still supported... - xtype is prefered..
24431         // created from xtype...
24432     
24433     this.ds = config.dataSource;
24434     
24435     if (config.store && !this.ds) {
24436         this.store= Roo.factory(config.store, Roo.data);
24437         this.ds = this.store;
24438         this.ds.xmodule = this.xmodule || false;
24439     }
24440     
24441     this.toolbarItems = [];
24442     if (config.items) {
24443         this.toolbarItems = config.items;
24444     }
24445     
24446     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24447     
24448     this.cursor = 0;
24449     
24450     if (this.ds) { 
24451         this.bind(this.ds);
24452     }
24453     
24454     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24455     
24456 };
24457
24458 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24459     /**
24460      * @cfg {Roo.data.Store} dataSource
24461      * The underlying data store providing the paged data
24462      */
24463     /**
24464      * @cfg {String/HTMLElement/Element} container
24465      * container The id or element that will contain the toolbar
24466      */
24467     /**
24468      * @cfg {Boolean} displayInfo
24469      * True to display the displayMsg (defaults to false)
24470      */
24471     /**
24472      * @cfg {Number} pageSize
24473      * The number of records to display per page (defaults to 20)
24474      */
24475     pageSize: 20,
24476     /**
24477      * @cfg {String} displayMsg
24478      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24479      */
24480     displayMsg : 'Displaying {0} - {1} of {2}',
24481     /**
24482      * @cfg {String} emptyMsg
24483      * The message to display when no records are found (defaults to "No data to display")
24484      */
24485     emptyMsg : 'No data to display',
24486     /**
24487      * Customizable piece of the default paging text (defaults to "Page")
24488      * @type String
24489      */
24490     beforePageText : "Page",
24491     /**
24492      * Customizable piece of the default paging text (defaults to "of %0")
24493      * @type String
24494      */
24495     afterPageText : "of {0}",
24496     /**
24497      * Customizable piece of the default paging text (defaults to "First Page")
24498      * @type String
24499      */
24500     firstText : "First Page",
24501     /**
24502      * Customizable piece of the default paging text (defaults to "Previous Page")
24503      * @type String
24504      */
24505     prevText : "Previous Page",
24506     /**
24507      * Customizable piece of the default paging text (defaults to "Next Page")
24508      * @type String
24509      */
24510     nextText : "Next Page",
24511     /**
24512      * Customizable piece of the default paging text (defaults to "Last Page")
24513      * @type String
24514      */
24515     lastText : "Last Page",
24516     /**
24517      * Customizable piece of the default paging text (defaults to "Refresh")
24518      * @type String
24519      */
24520     refreshText : "Refresh",
24521
24522     buttons : false,
24523     // private
24524     onRender : function(ct, position) 
24525     {
24526         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24527         this.navgroup.parentId = this.id;
24528         this.navgroup.onRender(this.el, null);
24529         // add the buttons to the navgroup
24530         
24531         if(this.displayInfo){
24532             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24533             this.displayEl = this.el.select('.x-paging-info', true).first();
24534 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24535 //            this.displayEl = navel.el.select('span',true).first();
24536         }
24537         
24538         var _this = this;
24539         
24540         if(this.buttons){
24541             Roo.each(_this.buttons, function(e){ // this might need to use render????
24542                Roo.factory(e).render(_this.el);
24543             });
24544         }
24545             
24546         Roo.each(_this.toolbarItems, function(e) {
24547             _this.navgroup.addItem(e);
24548         });
24549         
24550         
24551         this.first = this.navgroup.addItem({
24552             tooltip: this.firstText,
24553             cls: "prev",
24554             icon : 'fa fa-backward',
24555             disabled: true,
24556             preventDefault: true,
24557             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24558         });
24559         
24560         this.prev =  this.navgroup.addItem({
24561             tooltip: this.prevText,
24562             cls: "prev",
24563             icon : 'fa fa-step-backward',
24564             disabled: true,
24565             preventDefault: true,
24566             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24567         });
24568     //this.addSeparator();
24569         
24570         
24571         var field = this.navgroup.addItem( {
24572             tagtype : 'span',
24573             cls : 'x-paging-position',
24574             
24575             html : this.beforePageText  +
24576                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24577                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24578          } ); //?? escaped?
24579         
24580         this.field = field.el.select('input', true).first();
24581         this.field.on("keydown", this.onPagingKeydown, this);
24582         this.field.on("focus", function(){this.dom.select();});
24583     
24584     
24585         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24586         //this.field.setHeight(18);
24587         //this.addSeparator();
24588         this.next = this.navgroup.addItem({
24589             tooltip: this.nextText,
24590             cls: "next",
24591             html : ' <i class="fa fa-step-forward">',
24592             disabled: true,
24593             preventDefault: true,
24594             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24595         });
24596         this.last = this.navgroup.addItem({
24597             tooltip: this.lastText,
24598             icon : 'fa fa-forward',
24599             cls: "next",
24600             disabled: true,
24601             preventDefault: true,
24602             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24603         });
24604     //this.addSeparator();
24605         this.loading = this.navgroup.addItem({
24606             tooltip: this.refreshText,
24607             icon: 'fa fa-refresh',
24608             preventDefault: true,
24609             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24610         });
24611         
24612     },
24613
24614     // private
24615     updateInfo : function(){
24616         if(this.displayEl){
24617             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24618             var msg = count == 0 ?
24619                 this.emptyMsg :
24620                 String.format(
24621                     this.displayMsg,
24622                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24623                 );
24624             this.displayEl.update(msg);
24625         }
24626     },
24627
24628     // private
24629     onLoad : function(ds, r, o)
24630     {
24631         this.cursor = o.params.start ? o.params.start : 0;
24632         
24633         var d = this.getPageData(),
24634             ap = d.activePage,
24635             ps = d.pages;
24636         
24637         
24638         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24639         this.field.dom.value = ap;
24640         this.first.setDisabled(ap == 1);
24641         this.prev.setDisabled(ap == 1);
24642         this.next.setDisabled(ap == ps);
24643         this.last.setDisabled(ap == ps);
24644         this.loading.enable();
24645         this.updateInfo();
24646     },
24647
24648     // private
24649     getPageData : function(){
24650         var total = this.ds.getTotalCount();
24651         return {
24652             total : total,
24653             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24654             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24655         };
24656     },
24657
24658     // private
24659     onLoadError : function(){
24660         this.loading.enable();
24661     },
24662
24663     // private
24664     onPagingKeydown : function(e){
24665         var k = e.getKey();
24666         var d = this.getPageData();
24667         if(k == e.RETURN){
24668             var v = this.field.dom.value, pageNum;
24669             if(!v || isNaN(pageNum = parseInt(v, 10))){
24670                 this.field.dom.value = d.activePage;
24671                 return;
24672             }
24673             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24674             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24675             e.stopEvent();
24676         }
24677         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))
24678         {
24679           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24680           this.field.dom.value = pageNum;
24681           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24682           e.stopEvent();
24683         }
24684         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24685         {
24686           var v = this.field.dom.value, pageNum; 
24687           var increment = (e.shiftKey) ? 10 : 1;
24688           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24689                 increment *= -1;
24690           }
24691           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24692             this.field.dom.value = d.activePage;
24693             return;
24694           }
24695           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24696           {
24697             this.field.dom.value = parseInt(v, 10) + increment;
24698             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24699             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24700           }
24701           e.stopEvent();
24702         }
24703     },
24704
24705     // private
24706     beforeLoad : function(){
24707         if(this.loading){
24708             this.loading.disable();
24709         }
24710     },
24711
24712     // private
24713     onClick : function(which){
24714         
24715         var ds = this.ds;
24716         if (!ds) {
24717             return;
24718         }
24719         
24720         switch(which){
24721             case "first":
24722                 ds.load({params:{start: 0, limit: this.pageSize}});
24723             break;
24724             case "prev":
24725                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24726             break;
24727             case "next":
24728                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24729             break;
24730             case "last":
24731                 var total = ds.getTotalCount();
24732                 var extra = total % this.pageSize;
24733                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24734                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24735             break;
24736             case "refresh":
24737                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24738             break;
24739         }
24740     },
24741
24742     /**
24743      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24744      * @param {Roo.data.Store} store The data store to unbind
24745      */
24746     unbind : function(ds){
24747         ds.un("beforeload", this.beforeLoad, this);
24748         ds.un("load", this.onLoad, this);
24749         ds.un("loadexception", this.onLoadError, this);
24750         ds.un("remove", this.updateInfo, this);
24751         ds.un("add", this.updateInfo, this);
24752         this.ds = undefined;
24753     },
24754
24755     /**
24756      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24757      * @param {Roo.data.Store} store The data store to bind
24758      */
24759     bind : function(ds){
24760         ds.on("beforeload", this.beforeLoad, this);
24761         ds.on("load", this.onLoad, this);
24762         ds.on("loadexception", this.onLoadError, this);
24763         ds.on("remove", this.updateInfo, this);
24764         ds.on("add", this.updateInfo, this);
24765         this.ds = ds;
24766     }
24767 });/*
24768  * - LGPL
24769  *
24770  * element
24771  * 
24772  */
24773
24774 /**
24775  * @class Roo.bootstrap.MessageBar
24776  * @extends Roo.bootstrap.Component
24777  * Bootstrap MessageBar class
24778  * @cfg {String} html contents of the MessageBar
24779  * @cfg {String} weight (info | success | warning | danger) default info
24780  * @cfg {String} beforeClass insert the bar before the given class
24781  * @cfg {Boolean} closable (true | false) default false
24782  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24783  * 
24784  * @constructor
24785  * Create a new Element
24786  * @param {Object} config The config object
24787  */
24788
24789 Roo.bootstrap.MessageBar = function(config){
24790     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24791 };
24792
24793 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24794     
24795     html: '',
24796     weight: 'info',
24797     closable: false,
24798     fixed: false,
24799     beforeClass: 'bootstrap-sticky-wrap',
24800     
24801     getAutoCreate : function(){
24802         
24803         var cfg = {
24804             tag: 'div',
24805             cls: 'alert alert-dismissable alert-' + this.weight,
24806             cn: [
24807                 {
24808                     tag: 'span',
24809                     cls: 'message',
24810                     html: this.html || ''
24811                 }
24812             ]
24813         };
24814         
24815         if(this.fixed){
24816             cfg.cls += ' alert-messages-fixed';
24817         }
24818         
24819         if(this.closable){
24820             cfg.cn.push({
24821                 tag: 'button',
24822                 cls: 'close',
24823                 html: 'x'
24824             });
24825         }
24826         
24827         return cfg;
24828     },
24829     
24830     onRender : function(ct, position)
24831     {
24832         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24833         
24834         if(!this.el){
24835             var cfg = Roo.apply({},  this.getAutoCreate());
24836             cfg.id = Roo.id();
24837             
24838             if (this.cls) {
24839                 cfg.cls += ' ' + this.cls;
24840             }
24841             if (this.style) {
24842                 cfg.style = this.style;
24843             }
24844             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24845             
24846             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24847         }
24848         
24849         this.el.select('>button.close').on('click', this.hide, this);
24850         
24851     },
24852     
24853     show : function()
24854     {
24855         if (!this.rendered) {
24856             this.render();
24857         }
24858         
24859         this.el.show();
24860         
24861         this.fireEvent('show', this);
24862         
24863     },
24864     
24865     hide : function()
24866     {
24867         if (!this.rendered) {
24868             this.render();
24869         }
24870         
24871         this.el.hide();
24872         
24873         this.fireEvent('hide', this);
24874     },
24875     
24876     update : function()
24877     {
24878 //        var e = this.el.dom.firstChild;
24879 //        
24880 //        if(this.closable){
24881 //            e = e.nextSibling;
24882 //        }
24883 //        
24884 //        e.data = this.html || '';
24885
24886         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24887     }
24888    
24889 });
24890
24891  
24892
24893      /*
24894  * - LGPL
24895  *
24896  * Graph
24897  * 
24898  */
24899
24900
24901 /**
24902  * @class Roo.bootstrap.Graph
24903  * @extends Roo.bootstrap.Component
24904  * Bootstrap Graph class
24905 > Prameters
24906  -sm {number} sm 4
24907  -md {number} md 5
24908  @cfg {String} graphtype  bar | vbar | pie
24909  @cfg {number} g_x coodinator | centre x (pie)
24910  @cfg {number} g_y coodinator | centre y (pie)
24911  @cfg {number} g_r radius (pie)
24912  @cfg {number} g_height height of the chart (respected by all elements in the set)
24913  @cfg {number} g_width width of the chart (respected by all elements in the set)
24914  @cfg {Object} title The title of the chart
24915     
24916  -{Array}  values
24917  -opts (object) options for the chart 
24918      o {
24919      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24920      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24921      o vgutter (number)
24922      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.
24923      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24924      o to
24925      o stretch (boolean)
24926      o }
24927  -opts (object) options for the pie
24928      o{
24929      o cut
24930      o startAngle (number)
24931      o endAngle (number)
24932      } 
24933  *
24934  * @constructor
24935  * Create a new Input
24936  * @param {Object} config The config object
24937  */
24938
24939 Roo.bootstrap.Graph = function(config){
24940     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24941     
24942     this.addEvents({
24943         // img events
24944         /**
24945          * @event click
24946          * The img click event for the img.
24947          * @param {Roo.EventObject} e
24948          */
24949         "click" : true
24950     });
24951 };
24952
24953 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24954     
24955     sm: 4,
24956     md: 5,
24957     graphtype: 'bar',
24958     g_height: 250,
24959     g_width: 400,
24960     g_x: 50,
24961     g_y: 50,
24962     g_r: 30,
24963     opts:{
24964         //g_colors: this.colors,
24965         g_type: 'soft',
24966         g_gutter: '20%'
24967
24968     },
24969     title : false,
24970
24971     getAutoCreate : function(){
24972         
24973         var cfg = {
24974             tag: 'div',
24975             html : null
24976         };
24977         
24978         
24979         return  cfg;
24980     },
24981
24982     onRender : function(ct,position){
24983         
24984         
24985         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24986         
24987         if (typeof(Raphael) == 'undefined') {
24988             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24989             return;
24990         }
24991         
24992         this.raphael = Raphael(this.el.dom);
24993         
24994                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24995                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24996                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24997                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24998                 /*
24999                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25000                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25001                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25002                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25003                 
25004                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25005                 r.barchart(330, 10, 300, 220, data1);
25006                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25007                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25008                 */
25009                 
25010                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25011                 // r.barchart(30, 30, 560, 250,  xdata, {
25012                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25013                 //     axis : "0 0 1 1",
25014                 //     axisxlabels :  xdata
25015                 //     //yvalues : cols,
25016                    
25017                 // });
25018 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25019 //        
25020 //        this.load(null,xdata,{
25021 //                axis : "0 0 1 1",
25022 //                axisxlabels :  xdata
25023 //                });
25024
25025     },
25026
25027     load : function(graphtype,xdata,opts)
25028     {
25029         this.raphael.clear();
25030         if(!graphtype) {
25031             graphtype = this.graphtype;
25032         }
25033         if(!opts){
25034             opts = this.opts;
25035         }
25036         var r = this.raphael,
25037             fin = function () {
25038                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25039             },
25040             fout = function () {
25041                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25042             },
25043             pfin = function() {
25044                 this.sector.stop();
25045                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25046
25047                 if (this.label) {
25048                     this.label[0].stop();
25049                     this.label[0].attr({ r: 7.5 });
25050                     this.label[1].attr({ "font-weight": 800 });
25051                 }
25052             },
25053             pfout = function() {
25054                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25055
25056                 if (this.label) {
25057                     this.label[0].animate({ r: 5 }, 500, "bounce");
25058                     this.label[1].attr({ "font-weight": 400 });
25059                 }
25060             };
25061
25062         switch(graphtype){
25063             case 'bar':
25064                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25065                 break;
25066             case 'hbar':
25067                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25068                 break;
25069             case 'pie':
25070 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25071 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25072 //            
25073                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25074                 
25075                 break;
25076
25077         }
25078         
25079         if(this.title){
25080             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25081         }
25082         
25083     },
25084     
25085     setTitle: function(o)
25086     {
25087         this.title = o;
25088     },
25089     
25090     initEvents: function() {
25091         
25092         if(!this.href){
25093             this.el.on('click', this.onClick, this);
25094         }
25095     },
25096     
25097     onClick : function(e)
25098     {
25099         Roo.log('img onclick');
25100         this.fireEvent('click', this, e);
25101     }
25102    
25103 });
25104
25105  
25106 /*
25107  * - LGPL
25108  *
25109  * numberBox
25110  * 
25111  */
25112 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25113
25114 /**
25115  * @class Roo.bootstrap.dash.NumberBox
25116  * @extends Roo.bootstrap.Component
25117  * Bootstrap NumberBox class
25118  * @cfg {String} headline Box headline
25119  * @cfg {String} content Box content
25120  * @cfg {String} icon Box icon
25121  * @cfg {String} footer Footer text
25122  * @cfg {String} fhref Footer href
25123  * 
25124  * @constructor
25125  * Create a new NumberBox
25126  * @param {Object} config The config object
25127  */
25128
25129
25130 Roo.bootstrap.dash.NumberBox = function(config){
25131     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25132     
25133 };
25134
25135 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25136     
25137     headline : '',
25138     content : '',
25139     icon : '',
25140     footer : '',
25141     fhref : '',
25142     ficon : '',
25143     
25144     getAutoCreate : function(){
25145         
25146         var cfg = {
25147             tag : 'div',
25148             cls : 'small-box ',
25149             cn : [
25150                 {
25151                     tag : 'div',
25152                     cls : 'inner',
25153                     cn :[
25154                         {
25155                             tag : 'h3',
25156                             cls : 'roo-headline',
25157                             html : this.headline
25158                         },
25159                         {
25160                             tag : 'p',
25161                             cls : 'roo-content',
25162                             html : this.content
25163                         }
25164                     ]
25165                 }
25166             ]
25167         };
25168         
25169         if(this.icon){
25170             cfg.cn.push({
25171                 tag : 'div',
25172                 cls : 'icon',
25173                 cn :[
25174                     {
25175                         tag : 'i',
25176                         cls : 'ion ' + this.icon
25177                     }
25178                 ]
25179             });
25180         }
25181         
25182         if(this.footer){
25183             var footer = {
25184                 tag : 'a',
25185                 cls : 'small-box-footer',
25186                 href : this.fhref || '#',
25187                 html : this.footer
25188             };
25189             
25190             cfg.cn.push(footer);
25191             
25192         }
25193         
25194         return  cfg;
25195     },
25196
25197     onRender : function(ct,position){
25198         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25199
25200
25201        
25202                 
25203     },
25204
25205     setHeadline: function (value)
25206     {
25207         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25208     },
25209     
25210     setFooter: function (value, href)
25211     {
25212         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25213         
25214         if(href){
25215             this.el.select('a.small-box-footer',true).first().attr('href', href);
25216         }
25217         
25218     },
25219
25220     setContent: function (value)
25221     {
25222         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25223     },
25224
25225     initEvents: function() 
25226     {   
25227         
25228     }
25229     
25230 });
25231
25232  
25233 /*
25234  * - LGPL
25235  *
25236  * TabBox
25237  * 
25238  */
25239 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25240
25241 /**
25242  * @class Roo.bootstrap.dash.TabBox
25243  * @extends Roo.bootstrap.Component
25244  * Bootstrap TabBox class
25245  * @cfg {String} title Title of the TabBox
25246  * @cfg {String} icon Icon of the TabBox
25247  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25248  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25249  * 
25250  * @constructor
25251  * Create a new TabBox
25252  * @param {Object} config The config object
25253  */
25254
25255
25256 Roo.bootstrap.dash.TabBox = function(config){
25257     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25258     this.addEvents({
25259         // raw events
25260         /**
25261          * @event addpane
25262          * When a pane is added
25263          * @param {Roo.bootstrap.dash.TabPane} pane
25264          */
25265         "addpane" : true,
25266         /**
25267          * @event activatepane
25268          * When a pane is activated
25269          * @param {Roo.bootstrap.dash.TabPane} pane
25270          */
25271         "activatepane" : true
25272         
25273          
25274     });
25275     
25276     this.panes = [];
25277 };
25278
25279 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25280
25281     title : '',
25282     icon : false,
25283     showtabs : true,
25284     tabScrollable : false,
25285     
25286     getChildContainer : function()
25287     {
25288         return this.el.select('.tab-content', true).first();
25289     },
25290     
25291     getAutoCreate : function(){
25292         
25293         var header = {
25294             tag: 'li',
25295             cls: 'pull-left header',
25296             html: this.title,
25297             cn : []
25298         };
25299         
25300         if(this.icon){
25301             header.cn.push({
25302                 tag: 'i',
25303                 cls: 'fa ' + this.icon
25304             });
25305         }
25306         
25307         var h = {
25308             tag: 'ul',
25309             cls: 'nav nav-tabs pull-right',
25310             cn: [
25311                 header
25312             ]
25313         };
25314         
25315         if(this.tabScrollable){
25316             h = {
25317                 tag: 'div',
25318                 cls: 'tab-header',
25319                 cn: [
25320                     {
25321                         tag: 'ul',
25322                         cls: 'nav nav-tabs pull-right',
25323                         cn: [
25324                             header
25325                         ]
25326                     }
25327                 ]
25328             };
25329         }
25330         
25331         var cfg = {
25332             tag: 'div',
25333             cls: 'nav-tabs-custom',
25334             cn: [
25335                 h,
25336                 {
25337                     tag: 'div',
25338                     cls: 'tab-content no-padding',
25339                     cn: []
25340                 }
25341             ]
25342         };
25343
25344         return  cfg;
25345     },
25346     initEvents : function()
25347     {
25348         //Roo.log('add add pane handler');
25349         this.on('addpane', this.onAddPane, this);
25350     },
25351      /**
25352      * Updates the box title
25353      * @param {String} html to set the title to.
25354      */
25355     setTitle : function(value)
25356     {
25357         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25358     },
25359     onAddPane : function(pane)
25360     {
25361         this.panes.push(pane);
25362         //Roo.log('addpane');
25363         //Roo.log(pane);
25364         // tabs are rendere left to right..
25365         if(!this.showtabs){
25366             return;
25367         }
25368         
25369         var ctr = this.el.select('.nav-tabs', true).first();
25370          
25371          
25372         var existing = ctr.select('.nav-tab',true);
25373         var qty = existing.getCount();;
25374         
25375         
25376         var tab = ctr.createChild({
25377             tag : 'li',
25378             cls : 'nav-tab' + (qty ? '' : ' active'),
25379             cn : [
25380                 {
25381                     tag : 'a',
25382                     href:'#',
25383                     html : pane.title
25384                 }
25385             ]
25386         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25387         pane.tab = tab;
25388         
25389         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25390         if (!qty) {
25391             pane.el.addClass('active');
25392         }
25393         
25394                 
25395     },
25396     onTabClick : function(ev,un,ob,pane)
25397     {
25398         //Roo.log('tab - prev default');
25399         ev.preventDefault();
25400         
25401         
25402         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25403         pane.tab.addClass('active');
25404         //Roo.log(pane.title);
25405         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25406         // technically we should have a deactivate event.. but maybe add later.
25407         // and it should not de-activate the selected tab...
25408         this.fireEvent('activatepane', pane);
25409         pane.el.addClass('active');
25410         pane.fireEvent('activate');
25411         
25412         
25413     },
25414     
25415     getActivePane : function()
25416     {
25417         var r = false;
25418         Roo.each(this.panes, function(p) {
25419             if(p.el.hasClass('active')){
25420                 r = p;
25421                 return false;
25422             }
25423             
25424             return;
25425         });
25426         
25427         return r;
25428     }
25429     
25430     
25431 });
25432
25433  
25434 /*
25435  * - LGPL
25436  *
25437  * Tab pane
25438  * 
25439  */
25440 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25441 /**
25442  * @class Roo.bootstrap.TabPane
25443  * @extends Roo.bootstrap.Component
25444  * Bootstrap TabPane class
25445  * @cfg {Boolean} active (false | true) Default false
25446  * @cfg {String} title title of panel
25447
25448  * 
25449  * @constructor
25450  * Create a new TabPane
25451  * @param {Object} config The config object
25452  */
25453
25454 Roo.bootstrap.dash.TabPane = function(config){
25455     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25456     
25457     this.addEvents({
25458         // raw events
25459         /**
25460          * @event activate
25461          * When a pane is activated
25462          * @param {Roo.bootstrap.dash.TabPane} pane
25463          */
25464         "activate" : true
25465          
25466     });
25467 };
25468
25469 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25470     
25471     active : false,
25472     title : '',
25473     
25474     // the tabBox that this is attached to.
25475     tab : false,
25476      
25477     getAutoCreate : function() 
25478     {
25479         var cfg = {
25480             tag: 'div',
25481             cls: 'tab-pane'
25482         };
25483         
25484         if(this.active){
25485             cfg.cls += ' active';
25486         }
25487         
25488         return cfg;
25489     },
25490     initEvents  : function()
25491     {
25492         //Roo.log('trigger add pane handler');
25493         this.parent().fireEvent('addpane', this)
25494     },
25495     
25496      /**
25497      * Updates the tab title 
25498      * @param {String} html to set the title to.
25499      */
25500     setTitle: function(str)
25501     {
25502         if (!this.tab) {
25503             return;
25504         }
25505         this.title = str;
25506         this.tab.select('a', true).first().dom.innerHTML = str;
25507         
25508     }
25509     
25510     
25511     
25512 });
25513
25514  
25515
25516
25517  /*
25518  * - LGPL
25519  *
25520  * menu
25521  * 
25522  */
25523 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25524
25525 /**
25526  * @class Roo.bootstrap.menu.Menu
25527  * @extends Roo.bootstrap.Component
25528  * Bootstrap Menu class - container for Menu
25529  * @cfg {String} html Text of the menu
25530  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25531  * @cfg {String} icon Font awesome icon
25532  * @cfg {String} pos Menu align to (top | bottom) default bottom
25533  * 
25534  * 
25535  * @constructor
25536  * Create a new Menu
25537  * @param {Object} config The config object
25538  */
25539
25540
25541 Roo.bootstrap.menu.Menu = function(config){
25542     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25543     
25544     this.addEvents({
25545         /**
25546          * @event beforeshow
25547          * Fires before this menu is displayed
25548          * @param {Roo.bootstrap.menu.Menu} this
25549          */
25550         beforeshow : true,
25551         /**
25552          * @event beforehide
25553          * Fires before this menu is hidden
25554          * @param {Roo.bootstrap.menu.Menu} this
25555          */
25556         beforehide : true,
25557         /**
25558          * @event show
25559          * Fires after this menu is displayed
25560          * @param {Roo.bootstrap.menu.Menu} this
25561          */
25562         show : true,
25563         /**
25564          * @event hide
25565          * Fires after this menu is hidden
25566          * @param {Roo.bootstrap.menu.Menu} this
25567          */
25568         hide : true,
25569         /**
25570          * @event click
25571          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25572          * @param {Roo.bootstrap.menu.Menu} this
25573          * @param {Roo.EventObject} e
25574          */
25575         click : true
25576     });
25577     
25578 };
25579
25580 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25581     
25582     submenu : false,
25583     html : '',
25584     weight : 'default',
25585     icon : false,
25586     pos : 'bottom',
25587     
25588     
25589     getChildContainer : function() {
25590         if(this.isSubMenu){
25591             return this.el;
25592         }
25593         
25594         return this.el.select('ul.dropdown-menu', true).first();  
25595     },
25596     
25597     getAutoCreate : function()
25598     {
25599         var text = [
25600             {
25601                 tag : 'span',
25602                 cls : 'roo-menu-text',
25603                 html : this.html
25604             }
25605         ];
25606         
25607         if(this.icon){
25608             text.unshift({
25609                 tag : 'i',
25610                 cls : 'fa ' + this.icon
25611             })
25612         }
25613         
25614         
25615         var cfg = {
25616             tag : 'div',
25617             cls : 'btn-group',
25618             cn : [
25619                 {
25620                     tag : 'button',
25621                     cls : 'dropdown-button btn btn-' + this.weight,
25622                     cn : text
25623                 },
25624                 {
25625                     tag : 'button',
25626                     cls : 'dropdown-toggle btn btn-' + this.weight,
25627                     cn : [
25628                         {
25629                             tag : 'span',
25630                             cls : 'caret'
25631                         }
25632                     ]
25633                 },
25634                 {
25635                     tag : 'ul',
25636                     cls : 'dropdown-menu'
25637                 }
25638             ]
25639             
25640         };
25641         
25642         if(this.pos == 'top'){
25643             cfg.cls += ' dropup';
25644         }
25645         
25646         if(this.isSubMenu){
25647             cfg = {
25648                 tag : 'ul',
25649                 cls : 'dropdown-menu'
25650             }
25651         }
25652         
25653         return cfg;
25654     },
25655     
25656     onRender : function(ct, position)
25657     {
25658         this.isSubMenu = ct.hasClass('dropdown-submenu');
25659         
25660         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25661     },
25662     
25663     initEvents : function() 
25664     {
25665         if(this.isSubMenu){
25666             return;
25667         }
25668         
25669         this.hidden = true;
25670         
25671         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25672         this.triggerEl.on('click', this.onTriggerPress, this);
25673         
25674         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25675         this.buttonEl.on('click', this.onClick, this);
25676         
25677     },
25678     
25679     list : function()
25680     {
25681         if(this.isSubMenu){
25682             return this.el;
25683         }
25684         
25685         return this.el.select('ul.dropdown-menu', true).first();
25686     },
25687     
25688     onClick : function(e)
25689     {
25690         this.fireEvent("click", this, e);
25691     },
25692     
25693     onTriggerPress  : function(e)
25694     {   
25695         if (this.isVisible()) {
25696             this.hide();
25697         } else {
25698             this.show();
25699         }
25700     },
25701     
25702     isVisible : function(){
25703         return !this.hidden;
25704     },
25705     
25706     show : function()
25707     {
25708         this.fireEvent("beforeshow", this);
25709         
25710         this.hidden = false;
25711         this.el.addClass('open');
25712         
25713         Roo.get(document).on("mouseup", this.onMouseUp, this);
25714         
25715         this.fireEvent("show", this);
25716         
25717         
25718     },
25719     
25720     hide : function()
25721     {
25722         this.fireEvent("beforehide", this);
25723         
25724         this.hidden = true;
25725         this.el.removeClass('open');
25726         
25727         Roo.get(document).un("mouseup", this.onMouseUp);
25728         
25729         this.fireEvent("hide", this);
25730     },
25731     
25732     onMouseUp : function()
25733     {
25734         this.hide();
25735     }
25736     
25737 });
25738
25739  
25740  /*
25741  * - LGPL
25742  *
25743  * menu item
25744  * 
25745  */
25746 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25747
25748 /**
25749  * @class Roo.bootstrap.menu.Item
25750  * @extends Roo.bootstrap.Component
25751  * Bootstrap MenuItem class
25752  * @cfg {Boolean} submenu (true | false) default false
25753  * @cfg {String} html text of the item
25754  * @cfg {String} href the link
25755  * @cfg {Boolean} disable (true | false) default false
25756  * @cfg {Boolean} preventDefault (true | false) default true
25757  * @cfg {String} icon Font awesome icon
25758  * @cfg {String} pos Submenu align to (left | right) default right 
25759  * 
25760  * 
25761  * @constructor
25762  * Create a new Item
25763  * @param {Object} config The config object
25764  */
25765
25766
25767 Roo.bootstrap.menu.Item = function(config){
25768     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25769     this.addEvents({
25770         /**
25771          * @event mouseover
25772          * Fires when the mouse is hovering over this menu
25773          * @param {Roo.bootstrap.menu.Item} this
25774          * @param {Roo.EventObject} e
25775          */
25776         mouseover : true,
25777         /**
25778          * @event mouseout
25779          * Fires when the mouse exits this menu
25780          * @param {Roo.bootstrap.menu.Item} this
25781          * @param {Roo.EventObject} e
25782          */
25783         mouseout : true,
25784         // raw events
25785         /**
25786          * @event click
25787          * The raw click event for the entire grid.
25788          * @param {Roo.EventObject} e
25789          */
25790         click : true
25791     });
25792 };
25793
25794 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25795     
25796     submenu : false,
25797     href : '',
25798     html : '',
25799     preventDefault: true,
25800     disable : false,
25801     icon : false,
25802     pos : 'right',
25803     
25804     getAutoCreate : function()
25805     {
25806         var text = [
25807             {
25808                 tag : 'span',
25809                 cls : 'roo-menu-item-text',
25810                 html : this.html
25811             }
25812         ];
25813         
25814         if(this.icon){
25815             text.unshift({
25816                 tag : 'i',
25817                 cls : 'fa ' + this.icon
25818             })
25819         }
25820         
25821         var cfg = {
25822             tag : 'li',
25823             cn : [
25824                 {
25825                     tag : 'a',
25826                     href : this.href || '#',
25827                     cn : text
25828                 }
25829             ]
25830         };
25831         
25832         if(this.disable){
25833             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25834         }
25835         
25836         if(this.submenu){
25837             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25838             
25839             if(this.pos == 'left'){
25840                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25841             }
25842         }
25843         
25844         return cfg;
25845     },
25846     
25847     initEvents : function() 
25848     {
25849         this.el.on('mouseover', this.onMouseOver, this);
25850         this.el.on('mouseout', this.onMouseOut, this);
25851         
25852         this.el.select('a', true).first().on('click', this.onClick, this);
25853         
25854     },
25855     
25856     onClick : function(e)
25857     {
25858         if(this.preventDefault){
25859             e.preventDefault();
25860         }
25861         
25862         this.fireEvent("click", this, e);
25863     },
25864     
25865     onMouseOver : function(e)
25866     {
25867         if(this.submenu && this.pos == 'left'){
25868             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25869         }
25870         
25871         this.fireEvent("mouseover", this, e);
25872     },
25873     
25874     onMouseOut : function(e)
25875     {
25876         this.fireEvent("mouseout", this, e);
25877     }
25878 });
25879
25880  
25881
25882  /*
25883  * - LGPL
25884  *
25885  * menu separator
25886  * 
25887  */
25888 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25889
25890 /**
25891  * @class Roo.bootstrap.menu.Separator
25892  * @extends Roo.bootstrap.Component
25893  * Bootstrap Separator class
25894  * 
25895  * @constructor
25896  * Create a new Separator
25897  * @param {Object} config The config object
25898  */
25899
25900
25901 Roo.bootstrap.menu.Separator = function(config){
25902     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25903 };
25904
25905 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25906     
25907     getAutoCreate : function(){
25908         var cfg = {
25909             tag : 'li',
25910             cls: 'divider'
25911         };
25912         
25913         return cfg;
25914     }
25915    
25916 });
25917
25918  
25919
25920  /*
25921  * - LGPL
25922  *
25923  * Tooltip
25924  * 
25925  */
25926
25927 /**
25928  * @class Roo.bootstrap.Tooltip
25929  * Bootstrap Tooltip class
25930  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25931  * to determine which dom element triggers the tooltip.
25932  * 
25933  * It needs to add support for additional attributes like tooltip-position
25934  * 
25935  * @constructor
25936  * Create a new Toolti
25937  * @param {Object} config The config object
25938  */
25939
25940 Roo.bootstrap.Tooltip = function(config){
25941     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25942     
25943     this.alignment = Roo.bootstrap.Tooltip.alignment;
25944     
25945     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25946         this.alignment = config.alignment;
25947     }
25948     
25949 };
25950
25951 Roo.apply(Roo.bootstrap.Tooltip, {
25952     /**
25953      * @function init initialize tooltip monitoring.
25954      * @static
25955      */
25956     currentEl : false,
25957     currentTip : false,
25958     currentRegion : false,
25959     
25960     //  init : delay?
25961     
25962     init : function()
25963     {
25964         Roo.get(document).on('mouseover', this.enter ,this);
25965         Roo.get(document).on('mouseout', this.leave, this);
25966          
25967         
25968         this.currentTip = new Roo.bootstrap.Tooltip();
25969     },
25970     
25971     enter : function(ev)
25972     {
25973         var dom = ev.getTarget();
25974         
25975         //Roo.log(['enter',dom]);
25976         var el = Roo.fly(dom);
25977         if (this.currentEl) {
25978             //Roo.log(dom);
25979             //Roo.log(this.currentEl);
25980             //Roo.log(this.currentEl.contains(dom));
25981             if (this.currentEl == el) {
25982                 return;
25983             }
25984             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25985                 return;
25986             }
25987
25988         }
25989         
25990         if (this.currentTip.el) {
25991             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25992         }    
25993         //Roo.log(ev);
25994         
25995         if(!el || el.dom == document){
25996             return;
25997         }
25998         
25999         var bindEl = el;
26000         
26001         // you can not look for children, as if el is the body.. then everythign is the child..
26002         if (!el.attr('tooltip')) { //
26003             if (!el.select("[tooltip]").elements.length) {
26004                 return;
26005             }
26006             // is the mouse over this child...?
26007             bindEl = el.select("[tooltip]").first();
26008             var xy = ev.getXY();
26009             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26010                 //Roo.log("not in region.");
26011                 return;
26012             }
26013             //Roo.log("child element over..");
26014             
26015         }
26016         this.currentEl = bindEl;
26017         this.currentTip.bind(bindEl);
26018         this.currentRegion = Roo.lib.Region.getRegion(dom);
26019         this.currentTip.enter();
26020         
26021     },
26022     leave : function(ev)
26023     {
26024         var dom = ev.getTarget();
26025         //Roo.log(['leave',dom]);
26026         if (!this.currentEl) {
26027             return;
26028         }
26029         
26030         
26031         if (dom != this.currentEl.dom) {
26032             return;
26033         }
26034         var xy = ev.getXY();
26035         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26036             return;
26037         }
26038         // only activate leave if mouse cursor is outside... bounding box..
26039         
26040         
26041         
26042         
26043         if (this.currentTip) {
26044             this.currentTip.leave();
26045         }
26046         //Roo.log('clear currentEl');
26047         this.currentEl = false;
26048         
26049         
26050     },
26051     alignment : {
26052         'left' : ['r-l', [-2,0], 'right'],
26053         'right' : ['l-r', [2,0], 'left'],
26054         'bottom' : ['t-b', [0,2], 'top'],
26055         'top' : [ 'b-t', [0,-2], 'bottom']
26056     }
26057     
26058 });
26059
26060
26061 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26062     
26063     
26064     bindEl : false,
26065     
26066     delay : null, // can be { show : 300 , hide: 500}
26067     
26068     timeout : null,
26069     
26070     hoverState : null, //???
26071     
26072     placement : 'bottom', 
26073     
26074     alignment : false,
26075     
26076     getAutoCreate : function(){
26077     
26078         var cfg = {
26079            cls : 'tooltip',
26080            role : 'tooltip',
26081            cn : [
26082                 {
26083                     cls : 'tooltip-arrow'
26084                 },
26085                 {
26086                     cls : 'tooltip-inner'
26087                 }
26088            ]
26089         };
26090         
26091         return cfg;
26092     },
26093     bind : function(el)
26094     {
26095         this.bindEl = el;
26096     },
26097       
26098     
26099     enter : function () {
26100        
26101         if (this.timeout != null) {
26102             clearTimeout(this.timeout);
26103         }
26104         
26105         this.hoverState = 'in';
26106          //Roo.log("enter - show");
26107         if (!this.delay || !this.delay.show) {
26108             this.show();
26109             return;
26110         }
26111         var _t = this;
26112         this.timeout = setTimeout(function () {
26113             if (_t.hoverState == 'in') {
26114                 _t.show();
26115             }
26116         }, this.delay.show);
26117     },
26118     leave : function()
26119     {
26120         clearTimeout(this.timeout);
26121     
26122         this.hoverState = 'out';
26123          if (!this.delay || !this.delay.hide) {
26124             this.hide();
26125             return;
26126         }
26127        
26128         var _t = this;
26129         this.timeout = setTimeout(function () {
26130             //Roo.log("leave - timeout");
26131             
26132             if (_t.hoverState == 'out') {
26133                 _t.hide();
26134                 Roo.bootstrap.Tooltip.currentEl = false;
26135             }
26136         }, delay);
26137     },
26138     
26139     show : function (msg)
26140     {
26141         if (!this.el) {
26142             this.render(document.body);
26143         }
26144         // set content.
26145         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26146         
26147         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26148         
26149         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26150         
26151         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26152         
26153         var placement = typeof this.placement == 'function' ?
26154             this.placement.call(this, this.el, on_el) :
26155             this.placement;
26156             
26157         var autoToken = /\s?auto?\s?/i;
26158         var autoPlace = autoToken.test(placement);
26159         if (autoPlace) {
26160             placement = placement.replace(autoToken, '') || 'top';
26161         }
26162         
26163         //this.el.detach()
26164         //this.el.setXY([0,0]);
26165         this.el.show();
26166         //this.el.dom.style.display='block';
26167         
26168         //this.el.appendTo(on_el);
26169         
26170         var p = this.getPosition();
26171         var box = this.el.getBox();
26172         
26173         if (autoPlace) {
26174             // fixme..
26175         }
26176         
26177         var align = this.alignment[placement];
26178         
26179         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26180         
26181         if(placement == 'top' || placement == 'bottom'){
26182             if(xy[0] < 0){
26183                 placement = 'right';
26184             }
26185             
26186             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26187                 placement = 'left';
26188             }
26189             
26190             var scroll = Roo.select('body', true).first().getScroll();
26191             
26192             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26193                 placement = 'top';
26194             }
26195             
26196         }
26197         
26198         this.el.alignTo(this.bindEl, align[0],align[1]);
26199         //var arrow = this.el.select('.arrow',true).first();
26200         //arrow.set(align[2], 
26201         
26202         this.el.addClass(placement);
26203         
26204         this.el.addClass('in fade');
26205         
26206         this.hoverState = null;
26207         
26208         if (this.el.hasClass('fade')) {
26209             // fade it?
26210         }
26211         
26212     },
26213     hide : function()
26214     {
26215          
26216         if (!this.el) {
26217             return;
26218         }
26219         //this.el.setXY([0,0]);
26220         this.el.removeClass('in');
26221         //this.el.hide();
26222         
26223     }
26224     
26225 });
26226  
26227
26228  /*
26229  * - LGPL
26230  *
26231  * Location Picker
26232  * 
26233  */
26234
26235 /**
26236  * @class Roo.bootstrap.LocationPicker
26237  * @extends Roo.bootstrap.Component
26238  * Bootstrap LocationPicker class
26239  * @cfg {Number} latitude Position when init default 0
26240  * @cfg {Number} longitude Position when init default 0
26241  * @cfg {Number} zoom default 15
26242  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26243  * @cfg {Boolean} mapTypeControl default false
26244  * @cfg {Boolean} disableDoubleClickZoom default false
26245  * @cfg {Boolean} scrollwheel default true
26246  * @cfg {Boolean} streetViewControl default false
26247  * @cfg {Number} radius default 0
26248  * @cfg {String} locationName
26249  * @cfg {Boolean} draggable default true
26250  * @cfg {Boolean} enableAutocomplete default false
26251  * @cfg {Boolean} enableReverseGeocode default true
26252  * @cfg {String} markerTitle
26253  * 
26254  * @constructor
26255  * Create a new LocationPicker
26256  * @param {Object} config The config object
26257  */
26258
26259
26260 Roo.bootstrap.LocationPicker = function(config){
26261     
26262     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26263     
26264     this.addEvents({
26265         /**
26266          * @event initial
26267          * Fires when the picker initialized.
26268          * @param {Roo.bootstrap.LocationPicker} this
26269          * @param {Google Location} location
26270          */
26271         initial : true,
26272         /**
26273          * @event positionchanged
26274          * Fires when the picker position changed.
26275          * @param {Roo.bootstrap.LocationPicker} this
26276          * @param {Google Location} location
26277          */
26278         positionchanged : true,
26279         /**
26280          * @event resize
26281          * Fires when the map resize.
26282          * @param {Roo.bootstrap.LocationPicker} this
26283          */
26284         resize : true,
26285         /**
26286          * @event show
26287          * Fires when the map show.
26288          * @param {Roo.bootstrap.LocationPicker} this
26289          */
26290         show : true,
26291         /**
26292          * @event hide
26293          * Fires when the map hide.
26294          * @param {Roo.bootstrap.LocationPicker} this
26295          */
26296         hide : true,
26297         /**
26298          * @event mapClick
26299          * Fires when click the map.
26300          * @param {Roo.bootstrap.LocationPicker} this
26301          * @param {Map event} e
26302          */
26303         mapClick : true,
26304         /**
26305          * @event mapRightClick
26306          * Fires when right click the map.
26307          * @param {Roo.bootstrap.LocationPicker} this
26308          * @param {Map event} e
26309          */
26310         mapRightClick : true,
26311         /**
26312          * @event markerClick
26313          * Fires when click the marker.
26314          * @param {Roo.bootstrap.LocationPicker} this
26315          * @param {Map event} e
26316          */
26317         markerClick : true,
26318         /**
26319          * @event markerRightClick
26320          * Fires when right click the marker.
26321          * @param {Roo.bootstrap.LocationPicker} this
26322          * @param {Map event} e
26323          */
26324         markerRightClick : true,
26325         /**
26326          * @event OverlayViewDraw
26327          * Fires when OverlayView Draw
26328          * @param {Roo.bootstrap.LocationPicker} this
26329          */
26330         OverlayViewDraw : true,
26331         /**
26332          * @event OverlayViewOnAdd
26333          * Fires when OverlayView Draw
26334          * @param {Roo.bootstrap.LocationPicker} this
26335          */
26336         OverlayViewOnAdd : true,
26337         /**
26338          * @event OverlayViewOnRemove
26339          * Fires when OverlayView Draw
26340          * @param {Roo.bootstrap.LocationPicker} this
26341          */
26342         OverlayViewOnRemove : true,
26343         /**
26344          * @event OverlayViewShow
26345          * Fires when OverlayView Draw
26346          * @param {Roo.bootstrap.LocationPicker} this
26347          * @param {Pixel} cpx
26348          */
26349         OverlayViewShow : true,
26350         /**
26351          * @event OverlayViewHide
26352          * Fires when OverlayView Draw
26353          * @param {Roo.bootstrap.LocationPicker} this
26354          */
26355         OverlayViewHide : true,
26356         /**
26357          * @event loadexception
26358          * Fires when load google lib failed.
26359          * @param {Roo.bootstrap.LocationPicker} this
26360          */
26361         loadexception : true
26362     });
26363         
26364 };
26365
26366 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26367     
26368     gMapContext: false,
26369     
26370     latitude: 0,
26371     longitude: 0,
26372     zoom: 15,
26373     mapTypeId: false,
26374     mapTypeControl: false,
26375     disableDoubleClickZoom: false,
26376     scrollwheel: true,
26377     streetViewControl: false,
26378     radius: 0,
26379     locationName: '',
26380     draggable: true,
26381     enableAutocomplete: false,
26382     enableReverseGeocode: true,
26383     markerTitle: '',
26384     
26385     getAutoCreate: function()
26386     {
26387
26388         var cfg = {
26389             tag: 'div',
26390             cls: 'roo-location-picker'
26391         };
26392         
26393         return cfg
26394     },
26395     
26396     initEvents: function(ct, position)
26397     {       
26398         if(!this.el.getWidth() || this.isApplied()){
26399             return;
26400         }
26401         
26402         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26403         
26404         this.initial();
26405     },
26406     
26407     initial: function()
26408     {
26409         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26410             this.fireEvent('loadexception', this);
26411             return;
26412         }
26413         
26414         if(!this.mapTypeId){
26415             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26416         }
26417         
26418         this.gMapContext = this.GMapContext();
26419         
26420         this.initOverlayView();
26421         
26422         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26423         
26424         var _this = this;
26425                 
26426         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26427             _this.setPosition(_this.gMapContext.marker.position);
26428         });
26429         
26430         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26431             _this.fireEvent('mapClick', this, event);
26432             
26433         });
26434
26435         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26436             _this.fireEvent('mapRightClick', this, event);
26437             
26438         });
26439         
26440         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26441             _this.fireEvent('markerClick', this, event);
26442             
26443         });
26444
26445         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26446             _this.fireEvent('markerRightClick', this, event);
26447             
26448         });
26449         
26450         this.setPosition(this.gMapContext.location);
26451         
26452         this.fireEvent('initial', this, this.gMapContext.location);
26453     },
26454     
26455     initOverlayView: function()
26456     {
26457         var _this = this;
26458         
26459         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26460             
26461             draw: function()
26462             {
26463                 _this.fireEvent('OverlayViewDraw', _this);
26464             },
26465             
26466             onAdd: function()
26467             {
26468                 _this.fireEvent('OverlayViewOnAdd', _this);
26469             },
26470             
26471             onRemove: function()
26472             {
26473                 _this.fireEvent('OverlayViewOnRemove', _this);
26474             },
26475             
26476             show: function(cpx)
26477             {
26478                 _this.fireEvent('OverlayViewShow', _this, cpx);
26479             },
26480             
26481             hide: function()
26482             {
26483                 _this.fireEvent('OverlayViewHide', _this);
26484             }
26485             
26486         });
26487     },
26488     
26489     fromLatLngToContainerPixel: function(event)
26490     {
26491         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26492     },
26493     
26494     isApplied: function() 
26495     {
26496         return this.getGmapContext() == false ? false : true;
26497     },
26498     
26499     getGmapContext: function() 
26500     {
26501         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26502     },
26503     
26504     GMapContext: function() 
26505     {
26506         var position = new google.maps.LatLng(this.latitude, this.longitude);
26507         
26508         var _map = new google.maps.Map(this.el.dom, {
26509             center: position,
26510             zoom: this.zoom,
26511             mapTypeId: this.mapTypeId,
26512             mapTypeControl: this.mapTypeControl,
26513             disableDoubleClickZoom: this.disableDoubleClickZoom,
26514             scrollwheel: this.scrollwheel,
26515             streetViewControl: this.streetViewControl,
26516             locationName: this.locationName,
26517             draggable: this.draggable,
26518             enableAutocomplete: this.enableAutocomplete,
26519             enableReverseGeocode: this.enableReverseGeocode
26520         });
26521         
26522         var _marker = new google.maps.Marker({
26523             position: position,
26524             map: _map,
26525             title: this.markerTitle,
26526             draggable: this.draggable
26527         });
26528         
26529         return {
26530             map: _map,
26531             marker: _marker,
26532             circle: null,
26533             location: position,
26534             radius: this.radius,
26535             locationName: this.locationName,
26536             addressComponents: {
26537                 formatted_address: null,
26538                 addressLine1: null,
26539                 addressLine2: null,
26540                 streetName: null,
26541                 streetNumber: null,
26542                 city: null,
26543                 district: null,
26544                 state: null,
26545                 stateOrProvince: null
26546             },
26547             settings: this,
26548             domContainer: this.el.dom,
26549             geodecoder: new google.maps.Geocoder()
26550         };
26551     },
26552     
26553     drawCircle: function(center, radius, options) 
26554     {
26555         if (this.gMapContext.circle != null) {
26556             this.gMapContext.circle.setMap(null);
26557         }
26558         if (radius > 0) {
26559             radius *= 1;
26560             options = Roo.apply({}, options, {
26561                 strokeColor: "#0000FF",
26562                 strokeOpacity: .35,
26563                 strokeWeight: 2,
26564                 fillColor: "#0000FF",
26565                 fillOpacity: .2
26566             });
26567             
26568             options.map = this.gMapContext.map;
26569             options.radius = radius;
26570             options.center = center;
26571             this.gMapContext.circle = new google.maps.Circle(options);
26572             return this.gMapContext.circle;
26573         }
26574         
26575         return null;
26576     },
26577     
26578     setPosition: function(location) 
26579     {
26580         this.gMapContext.location = location;
26581         this.gMapContext.marker.setPosition(location);
26582         this.gMapContext.map.panTo(location);
26583         this.drawCircle(location, this.gMapContext.radius, {});
26584         
26585         var _this = this;
26586         
26587         if (this.gMapContext.settings.enableReverseGeocode) {
26588             this.gMapContext.geodecoder.geocode({
26589                 latLng: this.gMapContext.location
26590             }, function(results, status) {
26591                 
26592                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26593                     _this.gMapContext.locationName = results[0].formatted_address;
26594                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26595                     
26596                     _this.fireEvent('positionchanged', this, location);
26597                 }
26598             });
26599             
26600             return;
26601         }
26602         
26603         this.fireEvent('positionchanged', this, location);
26604     },
26605     
26606     resize: function()
26607     {
26608         google.maps.event.trigger(this.gMapContext.map, "resize");
26609         
26610         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26611         
26612         this.fireEvent('resize', this);
26613     },
26614     
26615     setPositionByLatLng: function(latitude, longitude)
26616     {
26617         this.setPosition(new google.maps.LatLng(latitude, longitude));
26618     },
26619     
26620     getCurrentPosition: function() 
26621     {
26622         return {
26623             latitude: this.gMapContext.location.lat(),
26624             longitude: this.gMapContext.location.lng()
26625         };
26626     },
26627     
26628     getAddressName: function() 
26629     {
26630         return this.gMapContext.locationName;
26631     },
26632     
26633     getAddressComponents: function() 
26634     {
26635         return this.gMapContext.addressComponents;
26636     },
26637     
26638     address_component_from_google_geocode: function(address_components) 
26639     {
26640         var result = {};
26641         
26642         for (var i = 0; i < address_components.length; i++) {
26643             var component = address_components[i];
26644             if (component.types.indexOf("postal_code") >= 0) {
26645                 result.postalCode = component.short_name;
26646             } else if (component.types.indexOf("street_number") >= 0) {
26647                 result.streetNumber = component.short_name;
26648             } else if (component.types.indexOf("route") >= 0) {
26649                 result.streetName = component.short_name;
26650             } else if (component.types.indexOf("neighborhood") >= 0) {
26651                 result.city = component.short_name;
26652             } else if (component.types.indexOf("locality") >= 0) {
26653                 result.city = component.short_name;
26654             } else if (component.types.indexOf("sublocality") >= 0) {
26655                 result.district = component.short_name;
26656             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26657                 result.stateOrProvince = component.short_name;
26658             } else if (component.types.indexOf("country") >= 0) {
26659                 result.country = component.short_name;
26660             }
26661         }
26662         
26663         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26664         result.addressLine2 = "";
26665         return result;
26666     },
26667     
26668     setZoomLevel: function(zoom)
26669     {
26670         this.gMapContext.map.setZoom(zoom);
26671     },
26672     
26673     show: function()
26674     {
26675         if(!this.el){
26676             return;
26677         }
26678         
26679         this.el.show();
26680         
26681         this.resize();
26682         
26683         this.fireEvent('show', this);
26684     },
26685     
26686     hide: function()
26687     {
26688         if(!this.el){
26689             return;
26690         }
26691         
26692         this.el.hide();
26693         
26694         this.fireEvent('hide', this);
26695     }
26696     
26697 });
26698
26699 Roo.apply(Roo.bootstrap.LocationPicker, {
26700     
26701     OverlayView : function(map, options)
26702     {
26703         options = options || {};
26704         
26705         this.setMap(map);
26706     }
26707     
26708     
26709 });/*
26710  * - LGPL
26711  *
26712  * Alert
26713  * 
26714  */
26715
26716 /**
26717  * @class Roo.bootstrap.Alert
26718  * @extends Roo.bootstrap.Component
26719  * Bootstrap Alert class
26720  * @cfg {String} title The title of alert
26721  * @cfg {String} html The content of alert
26722  * @cfg {String} weight (  success | info | warning | danger )
26723  * @cfg {String} faicon font-awesomeicon
26724  * 
26725  * @constructor
26726  * Create a new alert
26727  * @param {Object} config The config object
26728  */
26729
26730
26731 Roo.bootstrap.Alert = function(config){
26732     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26733     
26734 };
26735
26736 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26737     
26738     title: '',
26739     html: '',
26740     weight: false,
26741     faicon: false,
26742     
26743     getAutoCreate : function()
26744     {
26745         
26746         var cfg = {
26747             tag : 'div',
26748             cls : 'alert',
26749             cn : [
26750                 {
26751                     tag : 'i',
26752                     cls : 'roo-alert-icon'
26753                     
26754                 },
26755                 {
26756                     tag : 'b',
26757                     cls : 'roo-alert-title',
26758                     html : this.title
26759                 },
26760                 {
26761                     tag : 'span',
26762                     cls : 'roo-alert-text',
26763                     html : this.html
26764                 }
26765             ]
26766         };
26767         
26768         if(this.faicon){
26769             cfg.cn[0].cls += ' fa ' + this.faicon;
26770         }
26771         
26772         if(this.weight){
26773             cfg.cls += ' alert-' + this.weight;
26774         }
26775         
26776         return cfg;
26777     },
26778     
26779     initEvents: function() 
26780     {
26781         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26782     },
26783     
26784     setTitle : function(str)
26785     {
26786         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26787     },
26788     
26789     setText : function(str)
26790     {
26791         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26792     },
26793     
26794     setWeight : function(weight)
26795     {
26796         if(this.weight){
26797             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26798         }
26799         
26800         this.weight = weight;
26801         
26802         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26803     },
26804     
26805     setIcon : function(icon)
26806     {
26807         if(this.faicon){
26808             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26809         }
26810         
26811         this.faicon = icon;
26812         
26813         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26814     },
26815     
26816     hide: function() 
26817     {
26818         this.el.hide();   
26819     },
26820     
26821     show: function() 
26822     {  
26823         this.el.show();   
26824     }
26825     
26826 });
26827
26828  
26829 /*
26830 * Licence: LGPL
26831 */
26832
26833 /**
26834  * @class Roo.bootstrap.UploadCropbox
26835  * @extends Roo.bootstrap.Component
26836  * Bootstrap UploadCropbox class
26837  * @cfg {String} emptyText show when image has been loaded
26838  * @cfg {String} rotateNotify show when image too small to rotate
26839  * @cfg {Number} errorTimeout default 3000
26840  * @cfg {Number} minWidth default 300
26841  * @cfg {Number} minHeight default 300
26842  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26843  * @cfg {Boolean} isDocument (true|false) default false
26844  * @cfg {String} url action url
26845  * @cfg {String} paramName default 'imageUpload'
26846  * @cfg {String} method default POST
26847  * @cfg {Boolean} loadMask (true|false) default true
26848  * @cfg {Boolean} loadingText default 'Loading...'
26849  * 
26850  * @constructor
26851  * Create a new UploadCropbox
26852  * @param {Object} config The config object
26853  */
26854
26855 Roo.bootstrap.UploadCropbox = function(config){
26856     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26857     
26858     this.addEvents({
26859         /**
26860          * @event beforeselectfile
26861          * Fire before select file
26862          * @param {Roo.bootstrap.UploadCropbox} this
26863          */
26864         "beforeselectfile" : true,
26865         /**
26866          * @event initial
26867          * Fire after initEvent
26868          * @param {Roo.bootstrap.UploadCropbox} this
26869          */
26870         "initial" : true,
26871         /**
26872          * @event crop
26873          * Fire after initEvent
26874          * @param {Roo.bootstrap.UploadCropbox} this
26875          * @param {String} data
26876          */
26877         "crop" : true,
26878         /**
26879          * @event prepare
26880          * Fire when preparing the file data
26881          * @param {Roo.bootstrap.UploadCropbox} this
26882          * @param {Object} file
26883          */
26884         "prepare" : true,
26885         /**
26886          * @event exception
26887          * Fire when get exception
26888          * @param {Roo.bootstrap.UploadCropbox} this
26889          * @param {XMLHttpRequest} xhr
26890          */
26891         "exception" : true,
26892         /**
26893          * @event beforeloadcanvas
26894          * Fire before load the canvas
26895          * @param {Roo.bootstrap.UploadCropbox} this
26896          * @param {String} src
26897          */
26898         "beforeloadcanvas" : true,
26899         /**
26900          * @event trash
26901          * Fire when trash image
26902          * @param {Roo.bootstrap.UploadCropbox} this
26903          */
26904         "trash" : true,
26905         /**
26906          * @event download
26907          * Fire when download the image
26908          * @param {Roo.bootstrap.UploadCropbox} this
26909          */
26910         "download" : true,
26911         /**
26912          * @event footerbuttonclick
26913          * Fire when footerbuttonclick
26914          * @param {Roo.bootstrap.UploadCropbox} this
26915          * @param {String} type
26916          */
26917         "footerbuttonclick" : true,
26918         /**
26919          * @event resize
26920          * Fire when resize
26921          * @param {Roo.bootstrap.UploadCropbox} this
26922          */
26923         "resize" : true,
26924         /**
26925          * @event rotate
26926          * Fire when rotate the image
26927          * @param {Roo.bootstrap.UploadCropbox} this
26928          * @param {String} pos
26929          */
26930         "rotate" : true,
26931         /**
26932          * @event inspect
26933          * Fire when inspect the file
26934          * @param {Roo.bootstrap.UploadCropbox} this
26935          * @param {Object} file
26936          */
26937         "inspect" : true,
26938         /**
26939          * @event upload
26940          * Fire when xhr upload the file
26941          * @param {Roo.bootstrap.UploadCropbox} this
26942          * @param {Object} data
26943          */
26944         "upload" : true,
26945         /**
26946          * @event arrange
26947          * Fire when arrange the file data
26948          * @param {Roo.bootstrap.UploadCropbox} this
26949          * @param {Object} formData
26950          */
26951         "arrange" : true
26952     });
26953     
26954     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26955 };
26956
26957 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26958     
26959     emptyText : 'Click to upload image',
26960     rotateNotify : 'Image is too small to rotate',
26961     errorTimeout : 3000,
26962     scale : 0,
26963     baseScale : 1,
26964     rotate : 0,
26965     dragable : false,
26966     pinching : false,
26967     mouseX : 0,
26968     mouseY : 0,
26969     cropData : false,
26970     minWidth : 300,
26971     minHeight : 300,
26972     file : false,
26973     exif : {},
26974     baseRotate : 1,
26975     cropType : 'image/jpeg',
26976     buttons : false,
26977     canvasLoaded : false,
26978     isDocument : false,
26979     method : 'POST',
26980     paramName : 'imageUpload',
26981     loadMask : true,
26982     loadingText : 'Loading...',
26983     maskEl : false,
26984     
26985     getAutoCreate : function()
26986     {
26987         var cfg = {
26988             tag : 'div',
26989             cls : 'roo-upload-cropbox',
26990             cn : [
26991                 {
26992                     tag : 'input',
26993                     cls : 'roo-upload-cropbox-selector',
26994                     type : 'file'
26995                 },
26996                 {
26997                     tag : 'div',
26998                     cls : 'roo-upload-cropbox-body',
26999                     style : 'cursor:pointer',
27000                     cn : [
27001                         {
27002                             tag : 'div',
27003                             cls : 'roo-upload-cropbox-preview'
27004                         },
27005                         {
27006                             tag : 'div',
27007                             cls : 'roo-upload-cropbox-thumb'
27008                         },
27009                         {
27010                             tag : 'div',
27011                             cls : 'roo-upload-cropbox-empty-notify',
27012                             html : this.emptyText
27013                         },
27014                         {
27015                             tag : 'div',
27016                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27017                             html : this.rotateNotify
27018                         }
27019                     ]
27020                 },
27021                 {
27022                     tag : 'div',
27023                     cls : 'roo-upload-cropbox-footer',
27024                     cn : {
27025                         tag : 'div',
27026                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27027                         cn : []
27028                     }
27029                 }
27030             ]
27031         };
27032         
27033         return cfg;
27034     },
27035     
27036     onRender : function(ct, position)
27037     {
27038         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27039         
27040         if (this.buttons.length) {
27041             
27042             Roo.each(this.buttons, function(bb) {
27043                 
27044                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27045                 
27046                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27047                 
27048             }, this);
27049         }
27050         
27051         if(this.loadMask){
27052             this.maskEl = this.el;
27053         }
27054     },
27055     
27056     initEvents : function()
27057     {
27058         this.urlAPI = (window.createObjectURL && window) || 
27059                                 (window.URL && URL.revokeObjectURL && URL) || 
27060                                 (window.webkitURL && webkitURL);
27061                         
27062         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27063         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27064         
27065         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27066         this.selectorEl.hide();
27067         
27068         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27069         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27070         
27071         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27072         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27073         this.thumbEl.hide();
27074         
27075         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27076         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27077         
27078         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27079         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27080         this.errorEl.hide();
27081         
27082         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27083         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27084         this.footerEl.hide();
27085         
27086         this.setThumbBoxSize();
27087         
27088         this.bind();
27089         
27090         this.resize();
27091         
27092         this.fireEvent('initial', this);
27093     },
27094
27095     bind : function()
27096     {
27097         var _this = this;
27098         
27099         window.addEventListener("resize", function() { _this.resize(); } );
27100         
27101         this.bodyEl.on('click', this.beforeSelectFile, this);
27102         
27103         if(Roo.isTouch){
27104             this.bodyEl.on('touchstart', this.onTouchStart, this);
27105             this.bodyEl.on('touchmove', this.onTouchMove, this);
27106             this.bodyEl.on('touchend', this.onTouchEnd, this);
27107         }
27108         
27109         if(!Roo.isTouch){
27110             this.bodyEl.on('mousedown', this.onMouseDown, this);
27111             this.bodyEl.on('mousemove', this.onMouseMove, this);
27112             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27113             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27114             Roo.get(document).on('mouseup', this.onMouseUp, this);
27115         }
27116         
27117         this.selectorEl.on('change', this.onFileSelected, this);
27118     },
27119     
27120     reset : function()
27121     {    
27122         this.scale = 0;
27123         this.baseScale = 1;
27124         this.rotate = 0;
27125         this.baseRotate = 1;
27126         this.dragable = false;
27127         this.pinching = false;
27128         this.mouseX = 0;
27129         this.mouseY = 0;
27130         this.cropData = false;
27131         this.notifyEl.dom.innerHTML = this.emptyText;
27132         
27133         this.selectorEl.dom.value = '';
27134         
27135     },
27136     
27137     resize : function()
27138     {
27139         if(this.fireEvent('resize', this) != false){
27140             this.setThumbBoxPosition();
27141             this.setCanvasPosition();
27142         }
27143     },
27144     
27145     onFooterButtonClick : function(e, el, o, type)
27146     {
27147         switch (type) {
27148             case 'rotate-left' :
27149                 this.onRotateLeft(e);
27150                 break;
27151             case 'rotate-right' :
27152                 this.onRotateRight(e);
27153                 break;
27154             case 'picture' :
27155                 this.beforeSelectFile(e);
27156                 break;
27157             case 'trash' :
27158                 this.trash(e);
27159                 break;
27160             case 'crop' :
27161                 this.crop(e);
27162                 break;
27163             case 'download' :
27164                 this.download(e);
27165                 break;
27166             default :
27167                 break;
27168         }
27169         
27170         this.fireEvent('footerbuttonclick', this, type);
27171     },
27172     
27173     beforeSelectFile : function(e)
27174     {
27175         e.preventDefault();
27176         
27177         if(this.fireEvent('beforeselectfile', this) != false){
27178             this.selectorEl.dom.click();
27179         }
27180     },
27181     
27182     onFileSelected : function(e)
27183     {
27184         e.preventDefault();
27185         
27186         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27187             return;
27188         }
27189         
27190         var file = this.selectorEl.dom.files[0];
27191         
27192         if(this.fireEvent('inspect', this, file) != false){
27193             this.prepare(file);
27194         }
27195         
27196     },
27197     
27198     trash : function(e)
27199     {
27200         this.fireEvent('trash', this);
27201     },
27202     
27203     download : function(e)
27204     {
27205         this.fireEvent('download', this);
27206     },
27207     
27208     loadCanvas : function(src)
27209     {   
27210         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27211             
27212             this.reset();
27213             
27214             this.imageEl = document.createElement('img');
27215             
27216             var _this = this;
27217             
27218             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27219             
27220             this.imageEl.src = src;
27221         }
27222     },
27223     
27224     onLoadCanvas : function()
27225     {   
27226         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27227         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27228         
27229         this.bodyEl.un('click', this.beforeSelectFile, this);
27230         
27231         this.notifyEl.hide();
27232         this.thumbEl.show();
27233         this.footerEl.show();
27234         
27235         this.baseRotateLevel();
27236         
27237         if(this.isDocument){
27238             this.setThumbBoxSize();
27239         }
27240         
27241         this.setThumbBoxPosition();
27242         
27243         this.baseScaleLevel();
27244         
27245         this.draw();
27246         
27247         this.resize();
27248         
27249         this.canvasLoaded = true;
27250         
27251         if(this.loadMask){
27252             this.maskEl.unmask();
27253         }
27254         
27255     },
27256     
27257     setCanvasPosition : function()
27258     {   
27259         if(!this.canvasEl){
27260             return;
27261         }
27262         
27263         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27264         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27265         
27266         this.previewEl.setLeft(pw);
27267         this.previewEl.setTop(ph);
27268         
27269     },
27270     
27271     onMouseDown : function(e)
27272     {   
27273         e.stopEvent();
27274         
27275         this.dragable = true;
27276         this.pinching = false;
27277         
27278         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27279             this.dragable = false;
27280             return;
27281         }
27282         
27283         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27284         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27285         
27286     },
27287     
27288     onMouseMove : function(e)
27289     {   
27290         e.stopEvent();
27291         
27292         if(!this.canvasLoaded){
27293             return;
27294         }
27295         
27296         if (!this.dragable){
27297             return;
27298         }
27299         
27300         var minX = Math.ceil(this.thumbEl.getLeft(true));
27301         var minY = Math.ceil(this.thumbEl.getTop(true));
27302         
27303         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27304         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27305         
27306         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27307         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27308         
27309         x = x - this.mouseX;
27310         y = y - this.mouseY;
27311         
27312         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27313         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27314         
27315         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27316         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27317         
27318         this.previewEl.setLeft(bgX);
27319         this.previewEl.setTop(bgY);
27320         
27321         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27322         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27323     },
27324     
27325     onMouseUp : function(e)
27326     {   
27327         e.stopEvent();
27328         
27329         this.dragable = false;
27330     },
27331     
27332     onMouseWheel : function(e)
27333     {   
27334         e.stopEvent();
27335         
27336         this.startScale = this.scale;
27337         
27338         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27339         
27340         if(!this.zoomable()){
27341             this.scale = this.startScale;
27342             return;
27343         }
27344         
27345         this.draw();
27346         
27347         return;
27348     },
27349     
27350     zoomable : function()
27351     {
27352         var minScale = this.thumbEl.getWidth() / this.minWidth;
27353         
27354         if(this.minWidth < this.minHeight){
27355             minScale = this.thumbEl.getHeight() / this.minHeight;
27356         }
27357         
27358         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27359         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27360         
27361         if(
27362                 this.isDocument &&
27363                 (this.rotate == 0 || this.rotate == 180) && 
27364                 (
27365                     width > this.imageEl.OriginWidth || 
27366                     height > this.imageEl.OriginHeight ||
27367                     (width < this.minWidth && height < this.minHeight)
27368                 )
27369         ){
27370             return false;
27371         }
27372         
27373         if(
27374                 this.isDocument &&
27375                 (this.rotate == 90 || this.rotate == 270) && 
27376                 (
27377                     width > this.imageEl.OriginWidth || 
27378                     height > this.imageEl.OriginHeight ||
27379                     (width < this.minHeight && height < this.minWidth)
27380                 )
27381         ){
27382             return false;
27383         }
27384         
27385         if(
27386                 !this.isDocument &&
27387                 (this.rotate == 0 || this.rotate == 180) && 
27388                 (
27389                     width < this.minWidth || 
27390                     width > this.imageEl.OriginWidth || 
27391                     height < this.minHeight || 
27392                     height > this.imageEl.OriginHeight
27393                 )
27394         ){
27395             return false;
27396         }
27397         
27398         if(
27399                 !this.isDocument &&
27400                 (this.rotate == 90 || this.rotate == 270) && 
27401                 (
27402                     width < this.minHeight || 
27403                     width > this.imageEl.OriginWidth || 
27404                     height < this.minWidth || 
27405                     height > this.imageEl.OriginHeight
27406                 )
27407         ){
27408             return false;
27409         }
27410         
27411         return true;
27412         
27413     },
27414     
27415     onRotateLeft : function(e)
27416     {   
27417         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27418             
27419             var minScale = this.thumbEl.getWidth() / this.minWidth;
27420             
27421             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27422             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27423             
27424             this.startScale = this.scale;
27425             
27426             while (this.getScaleLevel() < minScale){
27427             
27428                 this.scale = this.scale + 1;
27429                 
27430                 if(!this.zoomable()){
27431                     break;
27432                 }
27433                 
27434                 if(
27435                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27436                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27437                 ){
27438                     continue;
27439                 }
27440                 
27441                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27442
27443                 this.draw();
27444                 
27445                 return;
27446             }
27447             
27448             this.scale = this.startScale;
27449             
27450             this.onRotateFail();
27451             
27452             return false;
27453         }
27454         
27455         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27456
27457         if(this.isDocument){
27458             this.setThumbBoxSize();
27459             this.setThumbBoxPosition();
27460             this.setCanvasPosition();
27461         }
27462         
27463         this.draw();
27464         
27465         this.fireEvent('rotate', this, 'left');
27466         
27467     },
27468     
27469     onRotateRight : function(e)
27470     {
27471         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27472             
27473             var minScale = this.thumbEl.getWidth() / this.minWidth;
27474         
27475             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27476             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27477             
27478             this.startScale = this.scale;
27479             
27480             while (this.getScaleLevel() < minScale){
27481             
27482                 this.scale = this.scale + 1;
27483                 
27484                 if(!this.zoomable()){
27485                     break;
27486                 }
27487                 
27488                 if(
27489                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27490                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27491                 ){
27492                     continue;
27493                 }
27494                 
27495                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27496
27497                 this.draw();
27498                 
27499                 return;
27500             }
27501             
27502             this.scale = this.startScale;
27503             
27504             this.onRotateFail();
27505             
27506             return false;
27507         }
27508         
27509         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27510
27511         if(this.isDocument){
27512             this.setThumbBoxSize();
27513             this.setThumbBoxPosition();
27514             this.setCanvasPosition();
27515         }
27516         
27517         this.draw();
27518         
27519         this.fireEvent('rotate', this, 'right');
27520     },
27521     
27522     onRotateFail : function()
27523     {
27524         this.errorEl.show(true);
27525         
27526         var _this = this;
27527         
27528         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27529     },
27530     
27531     draw : function()
27532     {
27533         this.previewEl.dom.innerHTML = '';
27534         
27535         var canvasEl = document.createElement("canvas");
27536         
27537         var contextEl = canvasEl.getContext("2d");
27538         
27539         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27540         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27541         var center = this.imageEl.OriginWidth / 2;
27542         
27543         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27544             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27545             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27546             center = this.imageEl.OriginHeight / 2;
27547         }
27548         
27549         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27550         
27551         contextEl.translate(center, center);
27552         contextEl.rotate(this.rotate * Math.PI / 180);
27553
27554         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27555         
27556         this.canvasEl = document.createElement("canvas");
27557         
27558         this.contextEl = this.canvasEl.getContext("2d");
27559         
27560         switch (this.rotate) {
27561             case 0 :
27562                 
27563                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27564                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27565                 
27566                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27567                 
27568                 break;
27569             case 90 : 
27570                 
27571                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27572                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27573                 
27574                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27575                     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);
27576                     break;
27577                 }
27578                 
27579                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27580                 
27581                 break;
27582             case 180 :
27583                 
27584                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27585                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27586                 
27587                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27588                     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);
27589                     break;
27590                 }
27591                 
27592                 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);
27593                 
27594                 break;
27595             case 270 :
27596                 
27597                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27598                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27599         
27600                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27601                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27602                     break;
27603                 }
27604                 
27605                 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);
27606                 
27607                 break;
27608             default : 
27609                 break;
27610         }
27611         
27612         this.previewEl.appendChild(this.canvasEl);
27613         
27614         this.setCanvasPosition();
27615     },
27616     
27617     crop : function()
27618     {
27619         if(!this.canvasLoaded){
27620             return;
27621         }
27622         
27623         var imageCanvas = document.createElement("canvas");
27624         
27625         var imageContext = imageCanvas.getContext("2d");
27626         
27627         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27628         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27629         
27630         var center = imageCanvas.width / 2;
27631         
27632         imageContext.translate(center, center);
27633         
27634         imageContext.rotate(this.rotate * Math.PI / 180);
27635         
27636         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27637         
27638         var canvas = document.createElement("canvas");
27639         
27640         var context = canvas.getContext("2d");
27641                 
27642         canvas.width = this.minWidth;
27643         canvas.height = this.minHeight;
27644
27645         switch (this.rotate) {
27646             case 0 :
27647                 
27648                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27649                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27650                 
27651                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27652                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27653                 
27654                 var targetWidth = this.minWidth - 2 * x;
27655                 var targetHeight = this.minHeight - 2 * y;
27656                 
27657                 var scale = 1;
27658                 
27659                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27660                     scale = targetWidth / width;
27661                 }
27662                 
27663                 if(x > 0 && y == 0){
27664                     scale = targetHeight / height;
27665                 }
27666                 
27667                 if(x > 0 && y > 0){
27668                     scale = targetWidth / width;
27669                     
27670                     if(width < height){
27671                         scale = targetHeight / height;
27672                     }
27673                 }
27674                 
27675                 context.scale(scale, scale);
27676                 
27677                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27678                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27679
27680                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27681                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27682
27683                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27684                 
27685                 break;
27686             case 90 : 
27687                 
27688                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27689                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27690                 
27691                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27692                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27693                 
27694                 var targetWidth = this.minWidth - 2 * x;
27695                 var targetHeight = this.minHeight - 2 * y;
27696                 
27697                 var scale = 1;
27698                 
27699                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27700                     scale = targetWidth / width;
27701                 }
27702                 
27703                 if(x > 0 && y == 0){
27704                     scale = targetHeight / height;
27705                 }
27706                 
27707                 if(x > 0 && y > 0){
27708                     scale = targetWidth / width;
27709                     
27710                     if(width < height){
27711                         scale = targetHeight / height;
27712                     }
27713                 }
27714                 
27715                 context.scale(scale, scale);
27716                 
27717                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27718                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27719
27720                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27721                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27722                 
27723                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27724                 
27725                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27726                 
27727                 break;
27728             case 180 :
27729                 
27730                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27731                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27732                 
27733                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27734                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27735                 
27736                 var targetWidth = this.minWidth - 2 * x;
27737                 var targetHeight = this.minHeight - 2 * y;
27738                 
27739                 var scale = 1;
27740                 
27741                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27742                     scale = targetWidth / width;
27743                 }
27744                 
27745                 if(x > 0 && y == 0){
27746                     scale = targetHeight / height;
27747                 }
27748                 
27749                 if(x > 0 && y > 0){
27750                     scale = targetWidth / width;
27751                     
27752                     if(width < height){
27753                         scale = targetHeight / height;
27754                     }
27755                 }
27756                 
27757                 context.scale(scale, scale);
27758                 
27759                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27760                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27761
27762                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27763                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27764
27765                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27766                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27767                 
27768                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27769                 
27770                 break;
27771             case 270 :
27772                 
27773                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27774                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27775                 
27776                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27777                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27778                 
27779                 var targetWidth = this.minWidth - 2 * x;
27780                 var targetHeight = this.minHeight - 2 * y;
27781                 
27782                 var scale = 1;
27783                 
27784                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27785                     scale = targetWidth / width;
27786                 }
27787                 
27788                 if(x > 0 && y == 0){
27789                     scale = targetHeight / height;
27790                 }
27791                 
27792                 if(x > 0 && y > 0){
27793                     scale = targetWidth / width;
27794                     
27795                     if(width < height){
27796                         scale = targetHeight / height;
27797                     }
27798                 }
27799                 
27800                 context.scale(scale, scale);
27801                 
27802                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27803                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27804
27805                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27806                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27807                 
27808                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27809                 
27810                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27811                 
27812                 break;
27813             default : 
27814                 break;
27815         }
27816         
27817         this.cropData = canvas.toDataURL(this.cropType);
27818         
27819         if(this.fireEvent('crop', this, this.cropData) !== false){
27820             this.process(this.file, this.cropData);
27821         }
27822         
27823         return;
27824         
27825     },
27826     
27827     setThumbBoxSize : function()
27828     {
27829         var width, height;
27830         
27831         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27832             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27833             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27834             
27835             this.minWidth = width;
27836             this.minHeight = height;
27837             
27838             if(this.rotate == 90 || this.rotate == 270){
27839                 this.minWidth = height;
27840                 this.minHeight = width;
27841             }
27842         }
27843         
27844         height = 300;
27845         width = Math.ceil(this.minWidth * height / this.minHeight);
27846         
27847         if(this.minWidth > this.minHeight){
27848             width = 300;
27849             height = Math.ceil(this.minHeight * width / this.minWidth);
27850         }
27851         
27852         this.thumbEl.setStyle({
27853             width : width + 'px',
27854             height : height + 'px'
27855         });
27856
27857         return;
27858             
27859     },
27860     
27861     setThumbBoxPosition : function()
27862     {
27863         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27864         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27865         
27866         this.thumbEl.setLeft(x);
27867         this.thumbEl.setTop(y);
27868         
27869     },
27870     
27871     baseRotateLevel : function()
27872     {
27873         this.baseRotate = 1;
27874         
27875         if(
27876                 typeof(this.exif) != 'undefined' &&
27877                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27878                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27879         ){
27880             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27881         }
27882         
27883         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27884         
27885     },
27886     
27887     baseScaleLevel : function()
27888     {
27889         var width, height;
27890         
27891         if(this.isDocument){
27892             
27893             if(this.baseRotate == 6 || this.baseRotate == 8){
27894             
27895                 height = this.thumbEl.getHeight();
27896                 this.baseScale = height / this.imageEl.OriginWidth;
27897
27898                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27899                     width = this.thumbEl.getWidth();
27900                     this.baseScale = width / this.imageEl.OriginHeight;
27901                 }
27902
27903                 return;
27904             }
27905
27906             height = this.thumbEl.getHeight();
27907             this.baseScale = height / this.imageEl.OriginHeight;
27908
27909             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27910                 width = this.thumbEl.getWidth();
27911                 this.baseScale = width / this.imageEl.OriginWidth;
27912             }
27913
27914             return;
27915         }
27916         
27917         if(this.baseRotate == 6 || this.baseRotate == 8){
27918             
27919             width = this.thumbEl.getHeight();
27920             this.baseScale = width / this.imageEl.OriginHeight;
27921             
27922             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27923                 height = this.thumbEl.getWidth();
27924                 this.baseScale = height / this.imageEl.OriginHeight;
27925             }
27926             
27927             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27928                 height = this.thumbEl.getWidth();
27929                 this.baseScale = height / this.imageEl.OriginHeight;
27930                 
27931                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27932                     width = this.thumbEl.getHeight();
27933                     this.baseScale = width / this.imageEl.OriginWidth;
27934                 }
27935             }
27936             
27937             return;
27938         }
27939         
27940         width = this.thumbEl.getWidth();
27941         this.baseScale = width / this.imageEl.OriginWidth;
27942         
27943         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27944             height = this.thumbEl.getHeight();
27945             this.baseScale = height / this.imageEl.OriginHeight;
27946         }
27947         
27948         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27949             
27950             height = this.thumbEl.getHeight();
27951             this.baseScale = height / this.imageEl.OriginHeight;
27952             
27953             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27954                 width = this.thumbEl.getWidth();
27955                 this.baseScale = width / this.imageEl.OriginWidth;
27956             }
27957             
27958         }
27959         
27960         return;
27961     },
27962     
27963     getScaleLevel : function()
27964     {
27965         return this.baseScale * Math.pow(1.1, this.scale);
27966     },
27967     
27968     onTouchStart : function(e)
27969     {
27970         if(!this.canvasLoaded){
27971             this.beforeSelectFile(e);
27972             return;
27973         }
27974         
27975         var touches = e.browserEvent.touches;
27976         
27977         if(!touches){
27978             return;
27979         }
27980         
27981         if(touches.length == 1){
27982             this.onMouseDown(e);
27983             return;
27984         }
27985         
27986         if(touches.length != 2){
27987             return;
27988         }
27989         
27990         var coords = [];
27991         
27992         for(var i = 0, finger; finger = touches[i]; i++){
27993             coords.push(finger.pageX, finger.pageY);
27994         }
27995         
27996         var x = Math.pow(coords[0] - coords[2], 2);
27997         var y = Math.pow(coords[1] - coords[3], 2);
27998         
27999         this.startDistance = Math.sqrt(x + y);
28000         
28001         this.startScale = this.scale;
28002         
28003         this.pinching = true;
28004         this.dragable = false;
28005         
28006     },
28007     
28008     onTouchMove : function(e)
28009     {
28010         if(!this.pinching && !this.dragable){
28011             return;
28012         }
28013         
28014         var touches = e.browserEvent.touches;
28015         
28016         if(!touches){
28017             return;
28018         }
28019         
28020         if(this.dragable){
28021             this.onMouseMove(e);
28022             return;
28023         }
28024         
28025         var coords = [];
28026         
28027         for(var i = 0, finger; finger = touches[i]; i++){
28028             coords.push(finger.pageX, finger.pageY);
28029         }
28030         
28031         var x = Math.pow(coords[0] - coords[2], 2);
28032         var y = Math.pow(coords[1] - coords[3], 2);
28033         
28034         this.endDistance = Math.sqrt(x + y);
28035         
28036         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28037         
28038         if(!this.zoomable()){
28039             this.scale = this.startScale;
28040             return;
28041         }
28042         
28043         this.draw();
28044         
28045     },
28046     
28047     onTouchEnd : function(e)
28048     {
28049         this.pinching = false;
28050         this.dragable = false;
28051         
28052     },
28053     
28054     process : function(file, crop)
28055     {
28056         if(this.loadMask){
28057             this.maskEl.mask(this.loadingText);
28058         }
28059         
28060         this.xhr = new XMLHttpRequest();
28061         
28062         file.xhr = this.xhr;
28063
28064         this.xhr.open(this.method, this.url, true);
28065         
28066         var headers = {
28067             "Accept": "application/json",
28068             "Cache-Control": "no-cache",
28069             "X-Requested-With": "XMLHttpRequest"
28070         };
28071         
28072         for (var headerName in headers) {
28073             var headerValue = headers[headerName];
28074             if (headerValue) {
28075                 this.xhr.setRequestHeader(headerName, headerValue);
28076             }
28077         }
28078         
28079         var _this = this;
28080         
28081         this.xhr.onload = function()
28082         {
28083             _this.xhrOnLoad(_this.xhr);
28084         }
28085         
28086         this.xhr.onerror = function()
28087         {
28088             _this.xhrOnError(_this.xhr);
28089         }
28090         
28091         var formData = new FormData();
28092
28093         formData.append('returnHTML', 'NO');
28094         
28095         if(crop){
28096             formData.append('crop', crop);
28097         }
28098         
28099         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28100             formData.append(this.paramName, file, file.name);
28101         }
28102         
28103         if(typeof(file.filename) != 'undefined'){
28104             formData.append('filename', file.filename);
28105         }
28106         
28107         if(typeof(file.mimetype) != 'undefined'){
28108             formData.append('mimetype', file.mimetype);
28109         }
28110         
28111         if(this.fireEvent('arrange', this, formData) != false){
28112             this.xhr.send(formData);
28113         };
28114     },
28115     
28116     xhrOnLoad : function(xhr)
28117     {
28118         if(this.loadMask){
28119             this.maskEl.unmask();
28120         }
28121         
28122         if (xhr.readyState !== 4) {
28123             this.fireEvent('exception', this, xhr);
28124             return;
28125         }
28126
28127         var response = Roo.decode(xhr.responseText);
28128         
28129         if(!response.success){
28130             this.fireEvent('exception', this, xhr);
28131             return;
28132         }
28133         
28134         var response = Roo.decode(xhr.responseText);
28135         
28136         this.fireEvent('upload', this, response);
28137         
28138     },
28139     
28140     xhrOnError : function()
28141     {
28142         if(this.loadMask){
28143             this.maskEl.unmask();
28144         }
28145         
28146         Roo.log('xhr on error');
28147         
28148         var response = Roo.decode(xhr.responseText);
28149           
28150         Roo.log(response);
28151         
28152     },
28153     
28154     prepare : function(file)
28155     {   
28156         if(this.loadMask){
28157             this.maskEl.mask(this.loadingText);
28158         }
28159         
28160         this.file = false;
28161         this.exif = {};
28162         
28163         if(typeof(file) === 'string'){
28164             this.loadCanvas(file);
28165             return;
28166         }
28167         
28168         if(!file || !this.urlAPI){
28169             return;
28170         }
28171         
28172         this.file = file;
28173         this.cropType = file.type;
28174         
28175         var _this = this;
28176         
28177         if(this.fireEvent('prepare', this, this.file) != false){
28178             
28179             var reader = new FileReader();
28180             
28181             reader.onload = function (e) {
28182                 if (e.target.error) {
28183                     Roo.log(e.target.error);
28184                     return;
28185                 }
28186                 
28187                 var buffer = e.target.result,
28188                     dataView = new DataView(buffer),
28189                     offset = 2,
28190                     maxOffset = dataView.byteLength - 4,
28191                     markerBytes,
28192                     markerLength;
28193                 
28194                 if (dataView.getUint16(0) === 0xffd8) {
28195                     while (offset < maxOffset) {
28196                         markerBytes = dataView.getUint16(offset);
28197                         
28198                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28199                             markerLength = dataView.getUint16(offset + 2) + 2;
28200                             if (offset + markerLength > dataView.byteLength) {
28201                                 Roo.log('Invalid meta data: Invalid segment size.');
28202                                 break;
28203                             }
28204                             
28205                             if(markerBytes == 0xffe1){
28206                                 _this.parseExifData(
28207                                     dataView,
28208                                     offset,
28209                                     markerLength
28210                                 );
28211                             }
28212                             
28213                             offset += markerLength;
28214                             
28215                             continue;
28216                         }
28217                         
28218                         break;
28219                     }
28220                     
28221                 }
28222                 
28223                 var url = _this.urlAPI.createObjectURL(_this.file);
28224                 
28225                 _this.loadCanvas(url);
28226                 
28227                 return;
28228             }
28229             
28230             reader.readAsArrayBuffer(this.file);
28231             
28232         }
28233         
28234     },
28235     
28236     parseExifData : function(dataView, offset, length)
28237     {
28238         var tiffOffset = offset + 10,
28239             littleEndian,
28240             dirOffset;
28241     
28242         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28243             // No Exif data, might be XMP data instead
28244             return;
28245         }
28246         
28247         // Check for the ASCII code for "Exif" (0x45786966):
28248         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28249             // No Exif data, might be XMP data instead
28250             return;
28251         }
28252         if (tiffOffset + 8 > dataView.byteLength) {
28253             Roo.log('Invalid Exif data: Invalid segment size.');
28254             return;
28255         }
28256         // Check for the two null bytes:
28257         if (dataView.getUint16(offset + 8) !== 0x0000) {
28258             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28259             return;
28260         }
28261         // Check the byte alignment:
28262         switch (dataView.getUint16(tiffOffset)) {
28263         case 0x4949:
28264             littleEndian = true;
28265             break;
28266         case 0x4D4D:
28267             littleEndian = false;
28268             break;
28269         default:
28270             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28271             return;
28272         }
28273         // Check for the TIFF tag marker (0x002A):
28274         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28275             Roo.log('Invalid Exif data: Missing TIFF marker.');
28276             return;
28277         }
28278         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28279         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28280         
28281         this.parseExifTags(
28282             dataView,
28283             tiffOffset,
28284             tiffOffset + dirOffset,
28285             littleEndian
28286         );
28287     },
28288     
28289     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28290     {
28291         var tagsNumber,
28292             dirEndOffset,
28293             i;
28294         if (dirOffset + 6 > dataView.byteLength) {
28295             Roo.log('Invalid Exif data: Invalid directory offset.');
28296             return;
28297         }
28298         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28299         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28300         if (dirEndOffset + 4 > dataView.byteLength) {
28301             Roo.log('Invalid Exif data: Invalid directory size.');
28302             return;
28303         }
28304         for (i = 0; i < tagsNumber; i += 1) {
28305             this.parseExifTag(
28306                 dataView,
28307                 tiffOffset,
28308                 dirOffset + 2 + 12 * i, // tag offset
28309                 littleEndian
28310             );
28311         }
28312         // Return the offset to the next directory:
28313         return dataView.getUint32(dirEndOffset, littleEndian);
28314     },
28315     
28316     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28317     {
28318         var tag = dataView.getUint16(offset, littleEndian);
28319         
28320         this.exif[tag] = this.getExifValue(
28321             dataView,
28322             tiffOffset,
28323             offset,
28324             dataView.getUint16(offset + 2, littleEndian), // tag type
28325             dataView.getUint32(offset + 4, littleEndian), // tag length
28326             littleEndian
28327         );
28328     },
28329     
28330     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28331     {
28332         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28333             tagSize,
28334             dataOffset,
28335             values,
28336             i,
28337             str,
28338             c;
28339     
28340         if (!tagType) {
28341             Roo.log('Invalid Exif data: Invalid tag type.');
28342             return;
28343         }
28344         
28345         tagSize = tagType.size * length;
28346         // Determine if the value is contained in the dataOffset bytes,
28347         // or if the value at the dataOffset is a pointer to the actual data:
28348         dataOffset = tagSize > 4 ?
28349                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28350         if (dataOffset + tagSize > dataView.byteLength) {
28351             Roo.log('Invalid Exif data: Invalid data offset.');
28352             return;
28353         }
28354         if (length === 1) {
28355             return tagType.getValue(dataView, dataOffset, littleEndian);
28356         }
28357         values = [];
28358         for (i = 0; i < length; i += 1) {
28359             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28360         }
28361         
28362         if (tagType.ascii) {
28363             str = '';
28364             // Concatenate the chars:
28365             for (i = 0; i < values.length; i += 1) {
28366                 c = values[i];
28367                 // Ignore the terminating NULL byte(s):
28368                 if (c === '\u0000') {
28369                     break;
28370                 }
28371                 str += c;
28372             }
28373             return str;
28374         }
28375         return values;
28376     }
28377     
28378 });
28379
28380 Roo.apply(Roo.bootstrap.UploadCropbox, {
28381     tags : {
28382         'Orientation': 0x0112
28383     },
28384     
28385     Orientation: {
28386             1: 0, //'top-left',
28387 //            2: 'top-right',
28388             3: 180, //'bottom-right',
28389 //            4: 'bottom-left',
28390 //            5: 'left-top',
28391             6: 90, //'right-top',
28392 //            7: 'right-bottom',
28393             8: 270 //'left-bottom'
28394     },
28395     
28396     exifTagTypes : {
28397         // byte, 8-bit unsigned int:
28398         1: {
28399             getValue: function (dataView, dataOffset) {
28400                 return dataView.getUint8(dataOffset);
28401             },
28402             size: 1
28403         },
28404         // ascii, 8-bit byte:
28405         2: {
28406             getValue: function (dataView, dataOffset) {
28407                 return String.fromCharCode(dataView.getUint8(dataOffset));
28408             },
28409             size: 1,
28410             ascii: true
28411         },
28412         // short, 16 bit int:
28413         3: {
28414             getValue: function (dataView, dataOffset, littleEndian) {
28415                 return dataView.getUint16(dataOffset, littleEndian);
28416             },
28417             size: 2
28418         },
28419         // long, 32 bit int:
28420         4: {
28421             getValue: function (dataView, dataOffset, littleEndian) {
28422                 return dataView.getUint32(dataOffset, littleEndian);
28423             },
28424             size: 4
28425         },
28426         // rational = two long values, first is numerator, second is denominator:
28427         5: {
28428             getValue: function (dataView, dataOffset, littleEndian) {
28429                 return dataView.getUint32(dataOffset, littleEndian) /
28430                     dataView.getUint32(dataOffset + 4, littleEndian);
28431             },
28432             size: 8
28433         },
28434         // slong, 32 bit signed int:
28435         9: {
28436             getValue: function (dataView, dataOffset, littleEndian) {
28437                 return dataView.getInt32(dataOffset, littleEndian);
28438             },
28439             size: 4
28440         },
28441         // srational, two slongs, first is numerator, second is denominator:
28442         10: {
28443             getValue: function (dataView, dataOffset, littleEndian) {
28444                 return dataView.getInt32(dataOffset, littleEndian) /
28445                     dataView.getInt32(dataOffset + 4, littleEndian);
28446             },
28447             size: 8
28448         }
28449     },
28450     
28451     footer : {
28452         STANDARD : [
28453             {
28454                 tag : 'div',
28455                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28456                 action : 'rotate-left',
28457                 cn : [
28458                     {
28459                         tag : 'button',
28460                         cls : 'btn btn-default',
28461                         html : '<i class="fa fa-undo"></i>'
28462                     }
28463                 ]
28464             },
28465             {
28466                 tag : 'div',
28467                 cls : 'btn-group roo-upload-cropbox-picture',
28468                 action : 'picture',
28469                 cn : [
28470                     {
28471                         tag : 'button',
28472                         cls : 'btn btn-default',
28473                         html : '<i class="fa fa-picture-o"></i>'
28474                     }
28475                 ]
28476             },
28477             {
28478                 tag : 'div',
28479                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28480                 action : 'rotate-right',
28481                 cn : [
28482                     {
28483                         tag : 'button',
28484                         cls : 'btn btn-default',
28485                         html : '<i class="fa fa-repeat"></i>'
28486                     }
28487                 ]
28488             }
28489         ],
28490         DOCUMENT : [
28491             {
28492                 tag : 'div',
28493                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28494                 action : 'rotate-left',
28495                 cn : [
28496                     {
28497                         tag : 'button',
28498                         cls : 'btn btn-default',
28499                         html : '<i class="fa fa-undo"></i>'
28500                     }
28501                 ]
28502             },
28503             {
28504                 tag : 'div',
28505                 cls : 'btn-group roo-upload-cropbox-download',
28506                 action : 'download',
28507                 cn : [
28508                     {
28509                         tag : 'button',
28510                         cls : 'btn btn-default',
28511                         html : '<i class="fa fa-download"></i>'
28512                     }
28513                 ]
28514             },
28515             {
28516                 tag : 'div',
28517                 cls : 'btn-group roo-upload-cropbox-crop',
28518                 action : 'crop',
28519                 cn : [
28520                     {
28521                         tag : 'button',
28522                         cls : 'btn btn-default',
28523                         html : '<i class="fa fa-crop"></i>'
28524                     }
28525                 ]
28526             },
28527             {
28528                 tag : 'div',
28529                 cls : 'btn-group roo-upload-cropbox-trash',
28530                 action : 'trash',
28531                 cn : [
28532                     {
28533                         tag : 'button',
28534                         cls : 'btn btn-default',
28535                         html : '<i class="fa fa-trash"></i>'
28536                     }
28537                 ]
28538             },
28539             {
28540                 tag : 'div',
28541                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28542                 action : 'rotate-right',
28543                 cn : [
28544                     {
28545                         tag : 'button',
28546                         cls : 'btn btn-default',
28547                         html : '<i class="fa fa-repeat"></i>'
28548                     }
28549                 ]
28550             }
28551         ],
28552         ROTATOR : [
28553             {
28554                 tag : 'div',
28555                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28556                 action : 'rotate-left',
28557                 cn : [
28558                     {
28559                         tag : 'button',
28560                         cls : 'btn btn-default',
28561                         html : '<i class="fa fa-undo"></i>'
28562                     }
28563                 ]
28564             },
28565             {
28566                 tag : 'div',
28567                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28568                 action : 'rotate-right',
28569                 cn : [
28570                     {
28571                         tag : 'button',
28572                         cls : 'btn btn-default',
28573                         html : '<i class="fa fa-repeat"></i>'
28574                     }
28575                 ]
28576             }
28577         ]
28578     }
28579 });
28580
28581 /*
28582 * Licence: LGPL
28583 */
28584
28585 /**
28586  * @class Roo.bootstrap.DocumentManager
28587  * @extends Roo.bootstrap.Component
28588  * Bootstrap DocumentManager class
28589  * @cfg {String} paramName default 'imageUpload'
28590  * @cfg {String} toolTipName default 'filename'
28591  * @cfg {String} method default POST
28592  * @cfg {String} url action url
28593  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28594  * @cfg {Boolean} multiple multiple upload default true
28595  * @cfg {Number} thumbSize default 300
28596  * @cfg {String} fieldLabel
28597  * @cfg {Number} labelWidth default 4
28598  * @cfg {String} labelAlign (left|top) default left
28599  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28600 * @cfg {Number} labellg set the width of label (1-12)
28601  * @cfg {Number} labelmd set the width of label (1-12)
28602  * @cfg {Number} labelsm set the width of label (1-12)
28603  * @cfg {Number} labelxs set the width of label (1-12)
28604  * 
28605  * @constructor
28606  * Create a new DocumentManager
28607  * @param {Object} config The config object
28608  */
28609
28610 Roo.bootstrap.DocumentManager = function(config){
28611     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28612     
28613     this.files = [];
28614     this.delegates = [];
28615     
28616     this.addEvents({
28617         /**
28618          * @event initial
28619          * Fire when initial the DocumentManager
28620          * @param {Roo.bootstrap.DocumentManager} this
28621          */
28622         "initial" : true,
28623         /**
28624          * @event inspect
28625          * inspect selected file
28626          * @param {Roo.bootstrap.DocumentManager} this
28627          * @param {File} file
28628          */
28629         "inspect" : true,
28630         /**
28631          * @event exception
28632          * Fire when xhr load exception
28633          * @param {Roo.bootstrap.DocumentManager} this
28634          * @param {XMLHttpRequest} xhr
28635          */
28636         "exception" : true,
28637         /**
28638          * @event afterupload
28639          * Fire when xhr load exception
28640          * @param {Roo.bootstrap.DocumentManager} this
28641          * @param {XMLHttpRequest} xhr
28642          */
28643         "afterupload" : true,
28644         /**
28645          * @event prepare
28646          * prepare the form data
28647          * @param {Roo.bootstrap.DocumentManager} this
28648          * @param {Object} formData
28649          */
28650         "prepare" : true,
28651         /**
28652          * @event remove
28653          * Fire when remove the file
28654          * @param {Roo.bootstrap.DocumentManager} this
28655          * @param {Object} file
28656          */
28657         "remove" : true,
28658         /**
28659          * @event refresh
28660          * Fire after refresh the file
28661          * @param {Roo.bootstrap.DocumentManager} this
28662          */
28663         "refresh" : true,
28664         /**
28665          * @event click
28666          * Fire after click the image
28667          * @param {Roo.bootstrap.DocumentManager} this
28668          * @param {Object} file
28669          */
28670         "click" : true,
28671         /**
28672          * @event edit
28673          * Fire when upload a image and editable set to true
28674          * @param {Roo.bootstrap.DocumentManager} this
28675          * @param {Object} file
28676          */
28677         "edit" : true,
28678         /**
28679          * @event beforeselectfile
28680          * Fire before select file
28681          * @param {Roo.bootstrap.DocumentManager} this
28682          */
28683         "beforeselectfile" : true,
28684         /**
28685          * @event process
28686          * Fire before process file
28687          * @param {Roo.bootstrap.DocumentManager} this
28688          * @param {Object} file
28689          */
28690         "process" : true,
28691         /**
28692          * @event previewrendered
28693          * Fire when preview rendered
28694          * @param {Roo.bootstrap.DocumentManager} this
28695          * @param {Object} file
28696          */
28697         "previewrendered" : true
28698         
28699     });
28700 };
28701
28702 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28703     
28704     boxes : 0,
28705     inputName : '',
28706     thumbSize : 300,
28707     multiple : true,
28708     files : false,
28709     method : 'POST',
28710     url : '',
28711     paramName : 'imageUpload',
28712     toolTipName : 'filename',
28713     fieldLabel : '',
28714     labelWidth : 4,
28715     labelAlign : 'left',
28716     editable : true,
28717     delegates : false,
28718     xhr : false, 
28719     
28720     labellg : 0,
28721     labelmd : 0,
28722     labelsm : 0,
28723     labelxs : 0,
28724     
28725     getAutoCreate : function()
28726     {   
28727         var managerWidget = {
28728             tag : 'div',
28729             cls : 'roo-document-manager',
28730             cn : [
28731                 {
28732                     tag : 'input',
28733                     cls : 'roo-document-manager-selector',
28734                     type : 'file'
28735                 },
28736                 {
28737                     tag : 'div',
28738                     cls : 'roo-document-manager-uploader',
28739                     cn : [
28740                         {
28741                             tag : 'div',
28742                             cls : 'roo-document-manager-upload-btn',
28743                             html : '<i class="fa fa-plus"></i>'
28744                         }
28745                     ]
28746                     
28747                 }
28748             ]
28749         };
28750         
28751         var content = [
28752             {
28753                 tag : 'div',
28754                 cls : 'column col-md-12',
28755                 cn : managerWidget
28756             }
28757         ];
28758         
28759         if(this.fieldLabel.length){
28760             
28761             content = [
28762                 {
28763                     tag : 'div',
28764                     cls : 'column col-md-12',
28765                     html : this.fieldLabel
28766                 },
28767                 {
28768                     tag : 'div',
28769                     cls : 'column col-md-12',
28770                     cn : managerWidget
28771                 }
28772             ];
28773
28774             if(this.labelAlign == 'left'){
28775                 content = [
28776                     {
28777                         tag : 'div',
28778                         cls : 'column',
28779                         html : this.fieldLabel
28780                     },
28781                     {
28782                         tag : 'div',
28783                         cls : 'column',
28784                         cn : managerWidget
28785                     }
28786                 ];
28787                 
28788                 if(this.labelWidth > 12){
28789                     content[0].style = "width: " + this.labelWidth + 'px';
28790                 }
28791
28792                 if(this.labelWidth < 13 && this.labelmd == 0){
28793                     this.labelmd = this.labelWidth;
28794                 }
28795
28796                 if(this.labellg > 0){
28797                     content[0].cls += ' col-lg-' + this.labellg;
28798                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28799                 }
28800
28801                 if(this.labelmd > 0){
28802                     content[0].cls += ' col-md-' + this.labelmd;
28803                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28804                 }
28805
28806                 if(this.labelsm > 0){
28807                     content[0].cls += ' col-sm-' + this.labelsm;
28808                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28809                 }
28810
28811                 if(this.labelxs > 0){
28812                     content[0].cls += ' col-xs-' + this.labelxs;
28813                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28814                 }
28815                 
28816             }
28817         }
28818         
28819         var cfg = {
28820             tag : 'div',
28821             cls : 'row clearfix',
28822             cn : content
28823         };
28824         
28825         return cfg;
28826         
28827     },
28828     
28829     initEvents : function()
28830     {
28831         this.managerEl = this.el.select('.roo-document-manager', true).first();
28832         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28833         
28834         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28835         this.selectorEl.hide();
28836         
28837         if(this.multiple){
28838             this.selectorEl.attr('multiple', 'multiple');
28839         }
28840         
28841         this.selectorEl.on('change', this.onFileSelected, this);
28842         
28843         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28844         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28845         
28846         this.uploader.on('click', this.onUploaderClick, this);
28847         
28848         this.renderProgressDialog();
28849         
28850         var _this = this;
28851         
28852         window.addEventListener("resize", function() { _this.refresh(); } );
28853         
28854         this.fireEvent('initial', this);
28855     },
28856     
28857     renderProgressDialog : function()
28858     {
28859         var _this = this;
28860         
28861         this.progressDialog = new Roo.bootstrap.Modal({
28862             cls : 'roo-document-manager-progress-dialog',
28863             allow_close : false,
28864             title : '',
28865             buttons : [
28866                 {
28867                     name  :'cancel',
28868                     weight : 'danger',
28869                     html : 'Cancel'
28870                 }
28871             ], 
28872             listeners : { 
28873                 btnclick : function() {
28874                     _this.uploadCancel();
28875                     this.hide();
28876                 }
28877             }
28878         });
28879          
28880         this.progressDialog.render(Roo.get(document.body));
28881          
28882         this.progress = new Roo.bootstrap.Progress({
28883             cls : 'roo-document-manager-progress',
28884             active : true,
28885             striped : true
28886         });
28887         
28888         this.progress.render(this.progressDialog.getChildContainer());
28889         
28890         this.progressBar = new Roo.bootstrap.ProgressBar({
28891             cls : 'roo-document-manager-progress-bar',
28892             aria_valuenow : 0,
28893             aria_valuemin : 0,
28894             aria_valuemax : 12,
28895             panel : 'success'
28896         });
28897         
28898         this.progressBar.render(this.progress.getChildContainer());
28899     },
28900     
28901     onUploaderClick : function(e)
28902     {
28903         e.preventDefault();
28904      
28905         if(this.fireEvent('beforeselectfile', this) != false){
28906             this.selectorEl.dom.click();
28907         }
28908         
28909     },
28910     
28911     onFileSelected : function(e)
28912     {
28913         e.preventDefault();
28914         
28915         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28916             return;
28917         }
28918         
28919         Roo.each(this.selectorEl.dom.files, function(file){
28920             if(this.fireEvent('inspect', this, file) != false){
28921                 this.files.push(file);
28922             }
28923         }, this);
28924         
28925         this.queue();
28926         
28927     },
28928     
28929     queue : function()
28930     {
28931         this.selectorEl.dom.value = '';
28932         
28933         if(!this.files || !this.files.length){
28934             return;
28935         }
28936         
28937         if(this.boxes > 0 && this.files.length > this.boxes){
28938             this.files = this.files.slice(0, this.boxes);
28939         }
28940         
28941         this.uploader.show();
28942         
28943         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28944             this.uploader.hide();
28945         }
28946         
28947         var _this = this;
28948         
28949         var files = [];
28950         
28951         var docs = [];
28952         
28953         Roo.each(this.files, function(file){
28954             
28955             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28956                 var f = this.renderPreview(file);
28957                 files.push(f);
28958                 return;
28959             }
28960             
28961             if(file.type.indexOf('image') != -1){
28962                 this.delegates.push(
28963                     (function(){
28964                         _this.process(file);
28965                     }).createDelegate(this)
28966                 );
28967         
28968                 return;
28969             }
28970             
28971             docs.push(
28972                 (function(){
28973                     _this.process(file);
28974                 }).createDelegate(this)
28975             );
28976             
28977         }, this);
28978         
28979         this.files = files;
28980         
28981         this.delegates = this.delegates.concat(docs);
28982         
28983         if(!this.delegates.length){
28984             this.refresh();
28985             return;
28986         }
28987         
28988         this.progressBar.aria_valuemax = this.delegates.length;
28989         
28990         this.arrange();
28991         
28992         return;
28993     },
28994     
28995     arrange : function()
28996     {
28997         if(!this.delegates.length){
28998             this.progressDialog.hide();
28999             this.refresh();
29000             return;
29001         }
29002         
29003         var delegate = this.delegates.shift();
29004         
29005         this.progressDialog.show();
29006         
29007         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29008         
29009         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29010         
29011         delegate();
29012     },
29013     
29014     refresh : function()
29015     {
29016         this.uploader.show();
29017         
29018         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29019             this.uploader.hide();
29020         }
29021         
29022         Roo.isTouch ? this.closable(false) : this.closable(true);
29023         
29024         this.fireEvent('refresh', this);
29025     },
29026     
29027     onRemove : function(e, el, o)
29028     {
29029         e.preventDefault();
29030         
29031         this.fireEvent('remove', this, o);
29032         
29033     },
29034     
29035     remove : function(o)
29036     {
29037         var files = [];
29038         
29039         Roo.each(this.files, function(file){
29040             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29041                 files.push(file);
29042                 return;
29043             }
29044
29045             o.target.remove();
29046
29047         }, this);
29048         
29049         this.files = files;
29050         
29051         this.refresh();
29052     },
29053     
29054     clear : function()
29055     {
29056         Roo.each(this.files, function(file){
29057             if(!file.target){
29058                 return;
29059             }
29060             
29061             file.target.remove();
29062
29063         }, this);
29064         
29065         this.files = [];
29066         
29067         this.refresh();
29068     },
29069     
29070     onClick : function(e, el, o)
29071     {
29072         e.preventDefault();
29073         
29074         this.fireEvent('click', this, o);
29075         
29076     },
29077     
29078     closable : function(closable)
29079     {
29080         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29081             
29082             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29083             
29084             if(closable){
29085                 el.show();
29086                 return;
29087             }
29088             
29089             el.hide();
29090             
29091         }, this);
29092     },
29093     
29094     xhrOnLoad : function(xhr)
29095     {
29096         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29097             el.remove();
29098         }, this);
29099         
29100         if (xhr.readyState !== 4) {
29101             this.arrange();
29102             this.fireEvent('exception', this, xhr);
29103             return;
29104         }
29105
29106         var response = Roo.decode(xhr.responseText);
29107         
29108         if(!response.success){
29109             this.arrange();
29110             this.fireEvent('exception', this, xhr);
29111             return;
29112         }
29113         
29114         var file = this.renderPreview(response.data);
29115         
29116         this.files.push(file);
29117         
29118         this.arrange();
29119         
29120         this.fireEvent('afterupload', this, xhr);
29121         
29122     },
29123     
29124     xhrOnError : function(xhr)
29125     {
29126         Roo.log('xhr on error');
29127         
29128         var response = Roo.decode(xhr.responseText);
29129           
29130         Roo.log(response);
29131         
29132         this.arrange();
29133     },
29134     
29135     process : function(file)
29136     {
29137         if(this.fireEvent('process', this, file) !== false){
29138             if(this.editable && file.type.indexOf('image') != -1){
29139                 this.fireEvent('edit', this, file);
29140                 return;
29141             }
29142
29143             this.uploadStart(file, false);
29144
29145             return;
29146         }
29147         
29148     },
29149     
29150     uploadStart : function(file, crop)
29151     {
29152         this.xhr = new XMLHttpRequest();
29153         
29154         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29155             this.arrange();
29156             return;
29157         }
29158         
29159         file.xhr = this.xhr;
29160             
29161         this.managerEl.createChild({
29162             tag : 'div',
29163             cls : 'roo-document-manager-loading',
29164             cn : [
29165                 {
29166                     tag : 'div',
29167                     tooltip : file.name,
29168                     cls : 'roo-document-manager-thumb',
29169                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29170                 }
29171             ]
29172
29173         });
29174
29175         this.xhr.open(this.method, this.url, true);
29176         
29177         var headers = {
29178             "Accept": "application/json",
29179             "Cache-Control": "no-cache",
29180             "X-Requested-With": "XMLHttpRequest"
29181         };
29182         
29183         for (var headerName in headers) {
29184             var headerValue = headers[headerName];
29185             if (headerValue) {
29186                 this.xhr.setRequestHeader(headerName, headerValue);
29187             }
29188         }
29189         
29190         var _this = this;
29191         
29192         this.xhr.onload = function()
29193         {
29194             _this.xhrOnLoad(_this.xhr);
29195         }
29196         
29197         this.xhr.onerror = function()
29198         {
29199             _this.xhrOnError(_this.xhr);
29200         }
29201         
29202         var formData = new FormData();
29203
29204         formData.append('returnHTML', 'NO');
29205         
29206         if(crop){
29207             formData.append('crop', crop);
29208         }
29209         
29210         formData.append(this.paramName, file, file.name);
29211         
29212         var options = {
29213             file : file, 
29214             manually : false
29215         };
29216         
29217         if(this.fireEvent('prepare', this, formData, options) != false){
29218             
29219             if(options.manually){
29220                 return;
29221             }
29222             
29223             this.xhr.send(formData);
29224             return;
29225         };
29226         
29227         this.uploadCancel();
29228     },
29229     
29230     uploadCancel : function()
29231     {
29232         if (this.xhr) {
29233             this.xhr.abort();
29234         }
29235         
29236         this.delegates = [];
29237         
29238         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29239             el.remove();
29240         }, this);
29241         
29242         this.arrange();
29243     },
29244     
29245     renderPreview : function(file)
29246     {
29247         if(typeof(file.target) != 'undefined' && file.target){
29248             return file;
29249         }
29250         
29251         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29252         
29253         var previewEl = this.managerEl.createChild({
29254             tag : 'div',
29255             cls : 'roo-document-manager-preview',
29256             cn : [
29257                 {
29258                     tag : 'div',
29259                     tooltip : file[this.toolTipName],
29260                     cls : 'roo-document-manager-thumb',
29261                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29262                 },
29263                 {
29264                     tag : 'button',
29265                     cls : 'close',
29266                     html : '<i class="fa fa-times-circle"></i>'
29267                 }
29268             ]
29269         });
29270
29271         var close = previewEl.select('button.close', true).first();
29272
29273         close.on('click', this.onRemove, this, file);
29274
29275         file.target = previewEl;
29276
29277         var image = previewEl.select('img', true).first();
29278         
29279         var _this = this;
29280         
29281         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29282         
29283         image.on('click', this.onClick, this, file);
29284         
29285         this.fireEvent('previewrendered', this, file);
29286         
29287         return file;
29288         
29289     },
29290     
29291     onPreviewLoad : function(file, image)
29292     {
29293         if(typeof(file.target) == 'undefined' || !file.target){
29294             return;
29295         }
29296         
29297         var width = image.dom.naturalWidth || image.dom.width;
29298         var height = image.dom.naturalHeight || image.dom.height;
29299         
29300         if(width > height){
29301             file.target.addClass('wide');
29302             return;
29303         }
29304         
29305         file.target.addClass('tall');
29306         return;
29307         
29308     },
29309     
29310     uploadFromSource : function(file, crop)
29311     {
29312         this.xhr = new XMLHttpRequest();
29313         
29314         this.managerEl.createChild({
29315             tag : 'div',
29316             cls : 'roo-document-manager-loading',
29317             cn : [
29318                 {
29319                     tag : 'div',
29320                     tooltip : file.name,
29321                     cls : 'roo-document-manager-thumb',
29322                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29323                 }
29324             ]
29325
29326         });
29327
29328         this.xhr.open(this.method, this.url, true);
29329         
29330         var headers = {
29331             "Accept": "application/json",
29332             "Cache-Control": "no-cache",
29333             "X-Requested-With": "XMLHttpRequest"
29334         };
29335         
29336         for (var headerName in headers) {
29337             var headerValue = headers[headerName];
29338             if (headerValue) {
29339                 this.xhr.setRequestHeader(headerName, headerValue);
29340             }
29341         }
29342         
29343         var _this = this;
29344         
29345         this.xhr.onload = function()
29346         {
29347             _this.xhrOnLoad(_this.xhr);
29348         }
29349         
29350         this.xhr.onerror = function()
29351         {
29352             _this.xhrOnError(_this.xhr);
29353         }
29354         
29355         var formData = new FormData();
29356
29357         formData.append('returnHTML', 'NO');
29358         
29359         formData.append('crop', crop);
29360         
29361         if(typeof(file.filename) != 'undefined'){
29362             formData.append('filename', file.filename);
29363         }
29364         
29365         if(typeof(file.mimetype) != 'undefined'){
29366             formData.append('mimetype', file.mimetype);
29367         }
29368         
29369         Roo.log(formData);
29370         
29371         if(this.fireEvent('prepare', this, formData) != false){
29372             this.xhr.send(formData);
29373         };
29374     }
29375 });
29376
29377 /*
29378 * Licence: LGPL
29379 */
29380
29381 /**
29382  * @class Roo.bootstrap.DocumentViewer
29383  * @extends Roo.bootstrap.Component
29384  * Bootstrap DocumentViewer class
29385  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29386  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29387  * 
29388  * @constructor
29389  * Create a new DocumentViewer
29390  * @param {Object} config The config object
29391  */
29392
29393 Roo.bootstrap.DocumentViewer = function(config){
29394     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29395     
29396     this.addEvents({
29397         /**
29398          * @event initial
29399          * Fire after initEvent
29400          * @param {Roo.bootstrap.DocumentViewer} this
29401          */
29402         "initial" : true,
29403         /**
29404          * @event click
29405          * Fire after click
29406          * @param {Roo.bootstrap.DocumentViewer} this
29407          */
29408         "click" : true,
29409         /**
29410          * @event download
29411          * Fire after download button
29412          * @param {Roo.bootstrap.DocumentViewer} this
29413          */
29414         "download" : true,
29415         /**
29416          * @event trash
29417          * Fire after trash button
29418          * @param {Roo.bootstrap.DocumentViewer} this
29419          */
29420         "trash" : true
29421         
29422     });
29423 };
29424
29425 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29426     
29427     showDownload : true,
29428     
29429     showTrash : true,
29430     
29431     getAutoCreate : function()
29432     {
29433         var cfg = {
29434             tag : 'div',
29435             cls : 'roo-document-viewer',
29436             cn : [
29437                 {
29438                     tag : 'div',
29439                     cls : 'roo-document-viewer-body',
29440                     cn : [
29441                         {
29442                             tag : 'div',
29443                             cls : 'roo-document-viewer-thumb',
29444                             cn : [
29445                                 {
29446                                     tag : 'img',
29447                                     cls : 'roo-document-viewer-image'
29448                                 }
29449                             ]
29450                         }
29451                     ]
29452                 },
29453                 {
29454                     tag : 'div',
29455                     cls : 'roo-document-viewer-footer',
29456                     cn : {
29457                         tag : 'div',
29458                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29459                         cn : [
29460                             {
29461                                 tag : 'div',
29462                                 cls : 'btn-group roo-document-viewer-download',
29463                                 cn : [
29464                                     {
29465                                         tag : 'button',
29466                                         cls : 'btn btn-default',
29467                                         html : '<i class="fa fa-download"></i>'
29468                                     }
29469                                 ]
29470                             },
29471                             {
29472                                 tag : 'div',
29473                                 cls : 'btn-group roo-document-viewer-trash',
29474                                 cn : [
29475                                     {
29476                                         tag : 'button',
29477                                         cls : 'btn btn-default',
29478                                         html : '<i class="fa fa-trash"></i>'
29479                                     }
29480                                 ]
29481                             }
29482                         ]
29483                     }
29484                 }
29485             ]
29486         };
29487         
29488         return cfg;
29489     },
29490     
29491     initEvents : function()
29492     {
29493         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29494         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29495         
29496         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29497         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29498         
29499         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29500         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29501         
29502         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29503         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29504         
29505         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29506         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29507         
29508         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29509         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29510         
29511         this.bodyEl.on('click', this.onClick, this);
29512         this.downloadBtn.on('click', this.onDownload, this);
29513         this.trashBtn.on('click', this.onTrash, this);
29514         
29515         this.downloadBtn.hide();
29516         this.trashBtn.hide();
29517         
29518         if(this.showDownload){
29519             this.downloadBtn.show();
29520         }
29521         
29522         if(this.showTrash){
29523             this.trashBtn.show();
29524         }
29525         
29526         if(!this.showDownload && !this.showTrash) {
29527             this.footerEl.hide();
29528         }
29529         
29530     },
29531     
29532     initial : function()
29533     {
29534         this.fireEvent('initial', this);
29535         
29536     },
29537     
29538     onClick : function(e)
29539     {
29540         e.preventDefault();
29541         
29542         this.fireEvent('click', this);
29543     },
29544     
29545     onDownload : function(e)
29546     {
29547         e.preventDefault();
29548         
29549         this.fireEvent('download', this);
29550     },
29551     
29552     onTrash : function(e)
29553     {
29554         e.preventDefault();
29555         
29556         this.fireEvent('trash', this);
29557     }
29558     
29559 });
29560 /*
29561  * - LGPL
29562  *
29563  * nav progress bar
29564  * 
29565  */
29566
29567 /**
29568  * @class Roo.bootstrap.NavProgressBar
29569  * @extends Roo.bootstrap.Component
29570  * Bootstrap NavProgressBar class
29571  * 
29572  * @constructor
29573  * Create a new nav progress bar
29574  * @param {Object} config The config object
29575  */
29576
29577 Roo.bootstrap.NavProgressBar = function(config){
29578     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29579
29580     this.bullets = this.bullets || [];
29581    
29582 //    Roo.bootstrap.NavProgressBar.register(this);
29583      this.addEvents({
29584         /**
29585              * @event changed
29586              * Fires when the active item changes
29587              * @param {Roo.bootstrap.NavProgressBar} this
29588              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29589              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29590          */
29591         'changed': true
29592      });
29593     
29594 };
29595
29596 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29597     
29598     bullets : [],
29599     barItems : [],
29600     
29601     getAutoCreate : function()
29602     {
29603         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29604         
29605         cfg = {
29606             tag : 'div',
29607             cls : 'roo-navigation-bar-group',
29608             cn : [
29609                 {
29610                     tag : 'div',
29611                     cls : 'roo-navigation-top-bar'
29612                 },
29613                 {
29614                     tag : 'div',
29615                     cls : 'roo-navigation-bullets-bar',
29616                     cn : [
29617                         {
29618                             tag : 'ul',
29619                             cls : 'roo-navigation-bar'
29620                         }
29621                     ]
29622                 },
29623                 
29624                 {
29625                     tag : 'div',
29626                     cls : 'roo-navigation-bottom-bar'
29627                 }
29628             ]
29629             
29630         };
29631         
29632         return cfg;
29633         
29634     },
29635     
29636     initEvents: function() 
29637     {
29638         
29639     },
29640     
29641     onRender : function(ct, position) 
29642     {
29643         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29644         
29645         if(this.bullets.length){
29646             Roo.each(this.bullets, function(b){
29647                this.addItem(b);
29648             }, this);
29649         }
29650         
29651         this.format();
29652         
29653     },
29654     
29655     addItem : function(cfg)
29656     {
29657         var item = new Roo.bootstrap.NavProgressItem(cfg);
29658         
29659         item.parentId = this.id;
29660         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29661         
29662         if(cfg.html){
29663             var top = new Roo.bootstrap.Element({
29664                 tag : 'div',
29665                 cls : 'roo-navigation-bar-text'
29666             });
29667             
29668             var bottom = new Roo.bootstrap.Element({
29669                 tag : 'div',
29670                 cls : 'roo-navigation-bar-text'
29671             });
29672             
29673             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29674             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29675             
29676             var topText = new Roo.bootstrap.Element({
29677                 tag : 'span',
29678                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29679             });
29680             
29681             var bottomText = new Roo.bootstrap.Element({
29682                 tag : 'span',
29683                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29684             });
29685             
29686             topText.onRender(top.el, null);
29687             bottomText.onRender(bottom.el, null);
29688             
29689             item.topEl = top;
29690             item.bottomEl = bottom;
29691         }
29692         
29693         this.barItems.push(item);
29694         
29695         return item;
29696     },
29697     
29698     getActive : function()
29699     {
29700         var active = false;
29701         
29702         Roo.each(this.barItems, function(v){
29703             
29704             if (!v.isActive()) {
29705                 return;
29706             }
29707             
29708             active = v;
29709             return false;
29710             
29711         });
29712         
29713         return active;
29714     },
29715     
29716     setActiveItem : function(item)
29717     {
29718         var prev = false;
29719         
29720         Roo.each(this.barItems, function(v){
29721             if (v.rid == item.rid) {
29722                 return ;
29723             }
29724             
29725             if (v.isActive()) {
29726                 v.setActive(false);
29727                 prev = v;
29728             }
29729         });
29730
29731         item.setActive(true);
29732         
29733         this.fireEvent('changed', this, item, prev);
29734     },
29735     
29736     getBarItem: function(rid)
29737     {
29738         var ret = false;
29739         
29740         Roo.each(this.barItems, function(e) {
29741             if (e.rid != rid) {
29742                 return;
29743             }
29744             
29745             ret =  e;
29746             return false;
29747         });
29748         
29749         return ret;
29750     },
29751     
29752     indexOfItem : function(item)
29753     {
29754         var index = false;
29755         
29756         Roo.each(this.barItems, function(v, i){
29757             
29758             if (v.rid != item.rid) {
29759                 return;
29760             }
29761             
29762             index = i;
29763             return false
29764         });
29765         
29766         return index;
29767     },
29768     
29769     setActiveNext : function()
29770     {
29771         var i = this.indexOfItem(this.getActive());
29772         
29773         if (i > this.barItems.length) {
29774             return;
29775         }
29776         
29777         this.setActiveItem(this.barItems[i+1]);
29778     },
29779     
29780     setActivePrev : function()
29781     {
29782         var i = this.indexOfItem(this.getActive());
29783         
29784         if (i  < 1) {
29785             return;
29786         }
29787         
29788         this.setActiveItem(this.barItems[i-1]);
29789     },
29790     
29791     format : function()
29792     {
29793         if(!this.barItems.length){
29794             return;
29795         }
29796      
29797         var width = 100 / this.barItems.length;
29798         
29799         Roo.each(this.barItems, function(i){
29800             i.el.setStyle('width', width + '%');
29801             i.topEl.el.setStyle('width', width + '%');
29802             i.bottomEl.el.setStyle('width', width + '%');
29803         }, this);
29804         
29805     }
29806     
29807 });
29808 /*
29809  * - LGPL
29810  *
29811  * Nav Progress Item
29812  * 
29813  */
29814
29815 /**
29816  * @class Roo.bootstrap.NavProgressItem
29817  * @extends Roo.bootstrap.Component
29818  * Bootstrap NavProgressItem class
29819  * @cfg {String} rid the reference id
29820  * @cfg {Boolean} active (true|false) Is item active default false
29821  * @cfg {Boolean} disabled (true|false) Is item active default false
29822  * @cfg {String} html
29823  * @cfg {String} position (top|bottom) text position default bottom
29824  * @cfg {String} icon show icon instead of number
29825  * 
29826  * @constructor
29827  * Create a new NavProgressItem
29828  * @param {Object} config The config object
29829  */
29830 Roo.bootstrap.NavProgressItem = function(config){
29831     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29832     this.addEvents({
29833         // raw events
29834         /**
29835          * @event click
29836          * The raw click event for the entire grid.
29837          * @param {Roo.bootstrap.NavProgressItem} this
29838          * @param {Roo.EventObject} e
29839          */
29840         "click" : true
29841     });
29842    
29843 };
29844
29845 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29846     
29847     rid : '',
29848     active : false,
29849     disabled : false,
29850     html : '',
29851     position : 'bottom',
29852     icon : false,
29853     
29854     getAutoCreate : function()
29855     {
29856         var iconCls = 'roo-navigation-bar-item-icon';
29857         
29858         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29859         
29860         var cfg = {
29861             tag: 'li',
29862             cls: 'roo-navigation-bar-item',
29863             cn : [
29864                 {
29865                     tag : 'i',
29866                     cls : iconCls
29867                 }
29868             ]
29869         };
29870         
29871         if(this.active){
29872             cfg.cls += ' active';
29873         }
29874         if(this.disabled){
29875             cfg.cls += ' disabled';
29876         }
29877         
29878         return cfg;
29879     },
29880     
29881     disable : function()
29882     {
29883         this.setDisabled(true);
29884     },
29885     
29886     enable : function()
29887     {
29888         this.setDisabled(false);
29889     },
29890     
29891     initEvents: function() 
29892     {
29893         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29894         
29895         this.iconEl.on('click', this.onClick, this);
29896     },
29897     
29898     onClick : function(e)
29899     {
29900         e.preventDefault();
29901         
29902         if(this.disabled){
29903             return;
29904         }
29905         
29906         if(this.fireEvent('click', this, e) === false){
29907             return;
29908         };
29909         
29910         this.parent().setActiveItem(this);
29911     },
29912     
29913     isActive: function () 
29914     {
29915         return this.active;
29916     },
29917     
29918     setActive : function(state)
29919     {
29920         if(this.active == state){
29921             return;
29922         }
29923         
29924         this.active = state;
29925         
29926         if (state) {
29927             this.el.addClass('active');
29928             return;
29929         }
29930         
29931         this.el.removeClass('active');
29932         
29933         return;
29934     },
29935     
29936     setDisabled : function(state)
29937     {
29938         if(this.disabled == state){
29939             return;
29940         }
29941         
29942         this.disabled = state;
29943         
29944         if (state) {
29945             this.el.addClass('disabled');
29946             return;
29947         }
29948         
29949         this.el.removeClass('disabled');
29950     },
29951     
29952     tooltipEl : function()
29953     {
29954         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29955     }
29956 });
29957  
29958
29959  /*
29960  * - LGPL
29961  *
29962  * FieldLabel
29963  * 
29964  */
29965
29966 /**
29967  * @class Roo.bootstrap.FieldLabel
29968  * @extends Roo.bootstrap.Component
29969  * Bootstrap FieldLabel class
29970  * @cfg {String} html contents of the element
29971  * @cfg {String} tag tag of the element default label
29972  * @cfg {String} cls class of the element
29973  * @cfg {String} target label target 
29974  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29975  * @cfg {String} invalidClass default "text-warning"
29976  * @cfg {String} validClass default "text-success"
29977  * @cfg {String} iconTooltip default "This field is required"
29978  * @cfg {String} indicatorpos (left|right) default left
29979  * 
29980  * @constructor
29981  * Create a new FieldLabel
29982  * @param {Object} config The config object
29983  */
29984
29985 Roo.bootstrap.FieldLabel = function(config){
29986     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29987     
29988     this.addEvents({
29989             /**
29990              * @event invalid
29991              * Fires after the field has been marked as invalid.
29992              * @param {Roo.form.FieldLabel} this
29993              * @param {String} msg The validation message
29994              */
29995             invalid : true,
29996             /**
29997              * @event valid
29998              * Fires after the field has been validated with no errors.
29999              * @param {Roo.form.FieldLabel} this
30000              */
30001             valid : true
30002         });
30003 };
30004
30005 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30006     
30007     tag: 'label',
30008     cls: '',
30009     html: '',
30010     target: '',
30011     allowBlank : true,
30012     invalidClass : 'has-warning',
30013     validClass : 'has-success',
30014     iconTooltip : 'This field is required',
30015     indicatorpos : 'left',
30016     
30017     getAutoCreate : function(){
30018         
30019         var cfg = {
30020             tag : this.tag,
30021             cls : 'roo-bootstrap-field-label ' + this.cls,
30022             for : this.target,
30023             cn : [
30024                 {
30025                     tag : 'i',
30026                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
30027                     tooltip : this.iconTooltip
30028                 },
30029                 {
30030                     tag : 'span',
30031                     html : this.html
30032                 }
30033             ] 
30034         };
30035         
30036         if(this.indicatorpos == 'right'){
30037             var cfg = {
30038                 tag : this.tag,
30039                 cls : 'roo-bootstrap-field-label ' + this.cls,
30040                 for : this.target,
30041                 cn : [
30042                     {
30043                         tag : 'span',
30044                         html : this.html
30045                     },
30046                     {
30047                         tag : 'i',
30048                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
30049                         tooltip : this.iconTooltip
30050                     }
30051                 ] 
30052             };
30053         }
30054         
30055         return cfg;
30056     },
30057     
30058     initEvents: function() 
30059     {
30060         Roo.bootstrap.Element.superclass.initEvents.call(this);
30061         
30062         this.indicator = this.indicatorEl();
30063         
30064         if(this.indicator){
30065             this.indicator.removeClass('visible');
30066             this.indicator.addClass('invisible');
30067         }
30068         
30069         Roo.bootstrap.FieldLabel.register(this);
30070     },
30071     
30072     indicatorEl : function()
30073     {
30074         var indicator = this.el.select('i.roo-required-indicator',true).first();
30075         
30076         if(!indicator){
30077             return false;
30078         }
30079         
30080         return indicator;
30081         
30082     },
30083     
30084     /**
30085      * Mark this field as valid
30086      */
30087     markValid : function()
30088     {
30089         if(this.indicator){
30090             this.indicator.removeClass('visible');
30091             this.indicator.addClass('invisible');
30092         }
30093         
30094         this.el.removeClass(this.invalidClass);
30095         
30096         this.el.addClass(this.validClass);
30097         
30098         this.fireEvent('valid', this);
30099     },
30100     
30101     /**
30102      * Mark this field as invalid
30103      * @param {String} msg The validation message
30104      */
30105     markInvalid : function(msg)
30106     {
30107         if(this.indicator){
30108             this.indicator.removeClass('invisible');
30109             this.indicator.addClass('visible');
30110         }
30111         
30112         this.el.removeClass(this.validClass);
30113         
30114         this.el.addClass(this.invalidClass);
30115         
30116         this.fireEvent('invalid', this, msg);
30117     }
30118     
30119    
30120 });
30121
30122 Roo.apply(Roo.bootstrap.FieldLabel, {
30123     
30124     groups: {},
30125     
30126      /**
30127     * register a FieldLabel Group
30128     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30129     */
30130     register : function(label)
30131     {
30132         if(this.groups.hasOwnProperty(label.target)){
30133             return;
30134         }
30135      
30136         this.groups[label.target] = label;
30137         
30138     },
30139     /**
30140     * fetch a FieldLabel Group based on the target
30141     * @param {string} target
30142     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30143     */
30144     get: function(target) {
30145         if (typeof(this.groups[target]) == 'undefined') {
30146             return false;
30147         }
30148         
30149         return this.groups[target] ;
30150     }
30151 });
30152
30153  
30154
30155  /*
30156  * - LGPL
30157  *
30158  * page DateSplitField.
30159  * 
30160  */
30161
30162
30163 /**
30164  * @class Roo.bootstrap.DateSplitField
30165  * @extends Roo.bootstrap.Component
30166  * Bootstrap DateSplitField class
30167  * @cfg {string} fieldLabel - the label associated
30168  * @cfg {Number} labelWidth set the width of label (0-12)
30169  * @cfg {String} labelAlign (top|left)
30170  * @cfg {Boolean} dayAllowBlank (true|false) default false
30171  * @cfg {Boolean} monthAllowBlank (true|false) default false
30172  * @cfg {Boolean} yearAllowBlank (true|false) default false
30173  * @cfg {string} dayPlaceholder 
30174  * @cfg {string} monthPlaceholder
30175  * @cfg {string} yearPlaceholder
30176  * @cfg {string} dayFormat default 'd'
30177  * @cfg {string} monthFormat default 'm'
30178  * @cfg {string} yearFormat default 'Y'
30179  * @cfg {Number} labellg set the width of label (1-12)
30180  * @cfg {Number} labelmd set the width of label (1-12)
30181  * @cfg {Number} labelsm set the width of label (1-12)
30182  * @cfg {Number} labelxs set the width of label (1-12)
30183
30184  *     
30185  * @constructor
30186  * Create a new DateSplitField
30187  * @param {Object} config The config object
30188  */
30189
30190 Roo.bootstrap.DateSplitField = function(config){
30191     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30192     
30193     this.addEvents({
30194         // raw events
30195          /**
30196          * @event years
30197          * getting the data of years
30198          * @param {Roo.bootstrap.DateSplitField} this
30199          * @param {Object} years
30200          */
30201         "years" : true,
30202         /**
30203          * @event days
30204          * getting the data of days
30205          * @param {Roo.bootstrap.DateSplitField} this
30206          * @param {Object} days
30207          */
30208         "days" : true,
30209         /**
30210          * @event invalid
30211          * Fires after the field has been marked as invalid.
30212          * @param {Roo.form.Field} this
30213          * @param {String} msg The validation message
30214          */
30215         invalid : true,
30216        /**
30217          * @event valid
30218          * Fires after the field has been validated with no errors.
30219          * @param {Roo.form.Field} this
30220          */
30221         valid : true
30222     });
30223 };
30224
30225 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30226     
30227     fieldLabel : '',
30228     labelAlign : 'top',
30229     labelWidth : 3,
30230     dayAllowBlank : false,
30231     monthAllowBlank : false,
30232     yearAllowBlank : false,
30233     dayPlaceholder : '',
30234     monthPlaceholder : '',
30235     yearPlaceholder : '',
30236     dayFormat : 'd',
30237     monthFormat : 'm',
30238     yearFormat : 'Y',
30239     isFormField : true,
30240     labellg : 0,
30241     labelmd : 0,
30242     labelsm : 0,
30243     labelxs : 0,
30244     
30245     getAutoCreate : function()
30246     {
30247         var cfg = {
30248             tag : 'div',
30249             cls : 'row roo-date-split-field-group',
30250             cn : [
30251                 {
30252                     tag : 'input',
30253                     type : 'hidden',
30254                     cls : 'form-hidden-field roo-date-split-field-group-value',
30255                     name : this.name
30256                 }
30257             ]
30258         };
30259         
30260         var labelCls = 'col-md-12';
30261         var contentCls = 'col-md-4';
30262         
30263         if(this.fieldLabel){
30264             
30265             var label = {
30266                 tag : 'div',
30267                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30268                 cn : [
30269                     {
30270                         tag : 'label',
30271                         html : this.fieldLabel
30272                     }
30273                 ]
30274             };
30275             
30276             if(this.labelAlign == 'left'){
30277             
30278                 if(this.labelWidth > 12){
30279                     label.style = "width: " + this.labelWidth + 'px';
30280                 }
30281
30282                 if(this.labelWidth < 13 && this.labelmd == 0){
30283                     this.labelmd = this.labelWidth;
30284                 }
30285
30286                 if(this.labellg > 0){
30287                     labelCls = ' col-lg-' + this.labellg;
30288                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30289                 }
30290
30291                 if(this.labelmd > 0){
30292                     labelCls = ' col-md-' + this.labelmd;
30293                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30294                 }
30295
30296                 if(this.labelsm > 0){
30297                     labelCls = ' col-sm-' + this.labelsm;
30298                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30299                 }
30300
30301                 if(this.labelxs > 0){
30302                     labelCls = ' col-xs-' + this.labelxs;
30303                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30304                 }
30305             }
30306             
30307             label.cls += ' ' + labelCls;
30308             
30309             cfg.cn.push(label);
30310         }
30311         
30312         Roo.each(['day', 'month', 'year'], function(t){
30313             cfg.cn.push({
30314                 tag : 'div',
30315                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30316             });
30317         }, this);
30318         
30319         return cfg;
30320     },
30321     
30322     inputEl: function ()
30323     {
30324         return this.el.select('.roo-date-split-field-group-value', true).first();
30325     },
30326     
30327     onRender : function(ct, position) 
30328     {
30329         var _this = this;
30330         
30331         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30332         
30333         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30334         
30335         this.dayField = new Roo.bootstrap.ComboBox({
30336             allowBlank : this.dayAllowBlank,
30337             alwaysQuery : true,
30338             displayField : 'value',
30339             editable : false,
30340             fieldLabel : '',
30341             forceSelection : true,
30342             mode : 'local',
30343             placeholder : this.dayPlaceholder,
30344             selectOnFocus : true,
30345             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30346             triggerAction : 'all',
30347             typeAhead : true,
30348             valueField : 'value',
30349             store : new Roo.data.SimpleStore({
30350                 data : (function() {    
30351                     var days = [];
30352                     _this.fireEvent('days', _this, days);
30353                     return days;
30354                 })(),
30355                 fields : [ 'value' ]
30356             }),
30357             listeners : {
30358                 select : function (_self, record, index)
30359                 {
30360                     _this.setValue(_this.getValue());
30361                 }
30362             }
30363         });
30364
30365         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30366         
30367         this.monthField = new Roo.bootstrap.MonthField({
30368             after : '<i class=\"fa fa-calendar\"></i>',
30369             allowBlank : this.monthAllowBlank,
30370             placeholder : this.monthPlaceholder,
30371             readOnly : true,
30372             listeners : {
30373                 render : function (_self)
30374                 {
30375                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30376                         e.preventDefault();
30377                         _self.focus();
30378                     });
30379                 },
30380                 select : function (_self, oldvalue, newvalue)
30381                 {
30382                     _this.setValue(_this.getValue());
30383                 }
30384             }
30385         });
30386         
30387         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30388         
30389         this.yearField = new Roo.bootstrap.ComboBox({
30390             allowBlank : this.yearAllowBlank,
30391             alwaysQuery : true,
30392             displayField : 'value',
30393             editable : false,
30394             fieldLabel : '',
30395             forceSelection : true,
30396             mode : 'local',
30397             placeholder : this.yearPlaceholder,
30398             selectOnFocus : true,
30399             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30400             triggerAction : 'all',
30401             typeAhead : true,
30402             valueField : 'value',
30403             store : new Roo.data.SimpleStore({
30404                 data : (function() {
30405                     var years = [];
30406                     _this.fireEvent('years', _this, years);
30407                     return years;
30408                 })(),
30409                 fields : [ 'value' ]
30410             }),
30411             listeners : {
30412                 select : function (_self, record, index)
30413                 {
30414                     _this.setValue(_this.getValue());
30415                 }
30416             }
30417         });
30418
30419         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30420     },
30421     
30422     setValue : function(v, format)
30423     {
30424         this.inputEl.dom.value = v;
30425         
30426         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30427         
30428         var d = Date.parseDate(v, f);
30429         
30430         if(!d){
30431             this.validate();
30432             return;
30433         }
30434         
30435         this.setDay(d.format(this.dayFormat));
30436         this.setMonth(d.format(this.monthFormat));
30437         this.setYear(d.format(this.yearFormat));
30438         
30439         this.validate();
30440         
30441         return;
30442     },
30443     
30444     setDay : function(v)
30445     {
30446         this.dayField.setValue(v);
30447         this.inputEl.dom.value = this.getValue();
30448         this.validate();
30449         return;
30450     },
30451     
30452     setMonth : function(v)
30453     {
30454         this.monthField.setValue(v, true);
30455         this.inputEl.dom.value = this.getValue();
30456         this.validate();
30457         return;
30458     },
30459     
30460     setYear : function(v)
30461     {
30462         this.yearField.setValue(v);
30463         this.inputEl.dom.value = this.getValue();
30464         this.validate();
30465         return;
30466     },
30467     
30468     getDay : function()
30469     {
30470         return this.dayField.getValue();
30471     },
30472     
30473     getMonth : function()
30474     {
30475         return this.monthField.getValue();
30476     },
30477     
30478     getYear : function()
30479     {
30480         return this.yearField.getValue();
30481     },
30482     
30483     getValue : function()
30484     {
30485         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30486         
30487         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30488         
30489         return date;
30490     },
30491     
30492     reset : function()
30493     {
30494         this.setDay('');
30495         this.setMonth('');
30496         this.setYear('');
30497         this.inputEl.dom.value = '';
30498         this.validate();
30499         return;
30500     },
30501     
30502     validate : function()
30503     {
30504         var d = this.dayField.validate();
30505         var m = this.monthField.validate();
30506         var y = this.yearField.validate();
30507         
30508         var valid = true;
30509         
30510         if(
30511                 (!this.dayAllowBlank && !d) ||
30512                 (!this.monthAllowBlank && !m) ||
30513                 (!this.yearAllowBlank && !y)
30514         ){
30515             valid = false;
30516         }
30517         
30518         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30519             return valid;
30520         }
30521         
30522         if(valid){
30523             this.markValid();
30524             return valid;
30525         }
30526         
30527         this.markInvalid();
30528         
30529         return valid;
30530     },
30531     
30532     markValid : function()
30533     {
30534         
30535         var label = this.el.select('label', true).first();
30536         var icon = this.el.select('i.fa-star', true).first();
30537
30538         if(label && icon){
30539             icon.remove();
30540         }
30541         
30542         this.fireEvent('valid', this);
30543     },
30544     
30545      /**
30546      * Mark this field as invalid
30547      * @param {String} msg The validation message
30548      */
30549     markInvalid : function(msg)
30550     {
30551         
30552         var label = this.el.select('label', true).first();
30553         var icon = this.el.select('i.fa-star', true).first();
30554
30555         if(label && !icon){
30556             this.el.select('.roo-date-split-field-label', true).createChild({
30557                 tag : 'i',
30558                 cls : 'text-danger fa fa-lg fa-star',
30559                 tooltip : 'This field is required',
30560                 style : 'margin-right:5px;'
30561             }, label, true);
30562         }
30563         
30564         this.fireEvent('invalid', this, msg);
30565     },
30566     
30567     clearInvalid : function()
30568     {
30569         var label = this.el.select('label', true).first();
30570         var icon = this.el.select('i.fa-star', true).first();
30571
30572         if(label && icon){
30573             icon.remove();
30574         }
30575         
30576         this.fireEvent('valid', this);
30577     },
30578     
30579     getName: function()
30580     {
30581         return this.name;
30582     }
30583     
30584 });
30585
30586  /**
30587  *
30588  * This is based on 
30589  * http://masonry.desandro.com
30590  *
30591  * The idea is to render all the bricks based on vertical width...
30592  *
30593  * The original code extends 'outlayer' - we might need to use that....
30594  * 
30595  */
30596
30597
30598 /**
30599  * @class Roo.bootstrap.LayoutMasonry
30600  * @extends Roo.bootstrap.Component
30601  * Bootstrap Layout Masonry class
30602  * 
30603  * @constructor
30604  * Create a new Element
30605  * @param {Object} config The config object
30606  */
30607
30608 Roo.bootstrap.LayoutMasonry = function(config){
30609     
30610     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30611     
30612     this.bricks = [];
30613     
30614     Roo.bootstrap.LayoutMasonry.register(this);
30615     
30616     this.addEvents({
30617         // raw events
30618         /**
30619          * @event layout
30620          * Fire after layout the items
30621          * @param {Roo.bootstrap.LayoutMasonry} this
30622          * @param {Roo.EventObject} e
30623          */
30624         "layout" : true
30625     });
30626     
30627 };
30628
30629 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30630     
30631     /**
30632      * @cfg {Boolean} isLayoutInstant = no animation?
30633      */   
30634     isLayoutInstant : false, // needed?
30635    
30636     /**
30637      * @cfg {Number} boxWidth  width of the columns
30638      */   
30639     boxWidth : 450,
30640     
30641       /**
30642      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30643      */   
30644     boxHeight : 0,
30645     
30646     /**
30647      * @cfg {Number} padWidth padding below box..
30648      */   
30649     padWidth : 10, 
30650     
30651     /**
30652      * @cfg {Number} gutter gutter width..
30653      */   
30654     gutter : 10,
30655     
30656      /**
30657      * @cfg {Number} maxCols maximum number of columns
30658      */   
30659     
30660     maxCols: 0,
30661     
30662     /**
30663      * @cfg {Boolean} isAutoInitial defalut true
30664      */   
30665     isAutoInitial : true, 
30666     
30667     containerWidth: 0,
30668     
30669     /**
30670      * @cfg {Boolean} isHorizontal defalut false
30671      */   
30672     isHorizontal : false, 
30673
30674     currentSize : null,
30675     
30676     tag: 'div',
30677     
30678     cls: '',
30679     
30680     bricks: null, //CompositeElement
30681     
30682     cols : 1,
30683     
30684     _isLayoutInited : false,
30685     
30686 //    isAlternative : false, // only use for vertical layout...
30687     
30688     /**
30689      * @cfg {Number} alternativePadWidth padding below box..
30690      */   
30691     alternativePadWidth : 50,
30692     
30693     selectedBrick : [],
30694     
30695     getAutoCreate : function(){
30696         
30697         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30698         
30699         var cfg = {
30700             tag: this.tag,
30701             cls: 'blog-masonary-wrapper ' + this.cls,
30702             cn : {
30703                 cls : 'mas-boxes masonary'
30704             }
30705         };
30706         
30707         return cfg;
30708     },
30709     
30710     getChildContainer: function( )
30711     {
30712         if (this.boxesEl) {
30713             return this.boxesEl;
30714         }
30715         
30716         this.boxesEl = this.el.select('.mas-boxes').first();
30717         
30718         return this.boxesEl;
30719     },
30720     
30721     
30722     initEvents : function()
30723     {
30724         var _this = this;
30725         
30726         if(this.isAutoInitial){
30727             Roo.log('hook children rendered');
30728             this.on('childrenrendered', function() {
30729                 Roo.log('children rendered');
30730                 _this.initial();
30731             } ,this);
30732         }
30733     },
30734     
30735     initial : function()
30736     {
30737         this.selectedBrick = [];
30738         
30739         this.currentSize = this.el.getBox(true);
30740         
30741         Roo.EventManager.onWindowResize(this.resize, this); 
30742
30743         if(!this.isAutoInitial){
30744             this.layout();
30745             return;
30746         }
30747         
30748         this.layout();
30749         
30750         return;
30751         //this.layout.defer(500,this);
30752         
30753     },
30754     
30755     resize : function()
30756     {
30757         var cs = this.el.getBox(true);
30758         
30759         if (
30760                 this.currentSize.width == cs.width && 
30761                 this.currentSize.x == cs.x && 
30762                 this.currentSize.height == cs.height && 
30763                 this.currentSize.y == cs.y 
30764         ) {
30765             Roo.log("no change in with or X or Y");
30766             return;
30767         }
30768         
30769         this.currentSize = cs;
30770         
30771         this.layout();
30772         
30773     },
30774     
30775     layout : function()
30776     {   
30777         this._resetLayout();
30778         
30779         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30780         
30781         this.layoutItems( isInstant );
30782       
30783         this._isLayoutInited = true;
30784         
30785         this.fireEvent('layout', this);
30786         
30787     },
30788     
30789     _resetLayout : function()
30790     {
30791         if(this.isHorizontal){
30792             this.horizontalMeasureColumns();
30793             return;
30794         }
30795         
30796         this.verticalMeasureColumns();
30797         
30798     },
30799     
30800     verticalMeasureColumns : function()
30801     {
30802         this.getContainerWidth();
30803         
30804 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30805 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30806 //            return;
30807 //        }
30808         
30809         var boxWidth = this.boxWidth + this.padWidth;
30810         
30811         if(this.containerWidth < this.boxWidth){
30812             boxWidth = this.containerWidth
30813         }
30814         
30815         var containerWidth = this.containerWidth;
30816         
30817         var cols = Math.floor(containerWidth / boxWidth);
30818         
30819         this.cols = Math.max( cols, 1 );
30820         
30821         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30822         
30823         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30824         
30825         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30826         
30827         this.colWidth = boxWidth + avail - this.padWidth;
30828         
30829         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30830         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30831     },
30832     
30833     horizontalMeasureColumns : function()
30834     {
30835         this.getContainerWidth();
30836         
30837         var boxWidth = this.boxWidth;
30838         
30839         if(this.containerWidth < boxWidth){
30840             boxWidth = this.containerWidth;
30841         }
30842         
30843         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30844         
30845         this.el.setHeight(boxWidth);
30846         
30847     },
30848     
30849     getContainerWidth : function()
30850     {
30851         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30852     },
30853     
30854     layoutItems : function( isInstant )
30855     {
30856         Roo.log(this.bricks);
30857         
30858         var items = Roo.apply([], this.bricks);
30859         
30860         if(this.isHorizontal){
30861             this._horizontalLayoutItems( items , isInstant );
30862             return;
30863         }
30864         
30865 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30866 //            this._verticalAlternativeLayoutItems( items , isInstant );
30867 //            return;
30868 //        }
30869         
30870         this._verticalLayoutItems( items , isInstant );
30871         
30872     },
30873     
30874     _verticalLayoutItems : function ( items , isInstant)
30875     {
30876         if ( !items || !items.length ) {
30877             return;
30878         }
30879         
30880         var standard = [
30881             ['xs', 'xs', 'xs', 'tall'],
30882             ['xs', 'xs', 'tall'],
30883             ['xs', 'xs', 'sm'],
30884             ['xs', 'xs', 'xs'],
30885             ['xs', 'tall'],
30886             ['xs', 'sm'],
30887             ['xs', 'xs'],
30888             ['xs'],
30889             
30890             ['sm', 'xs', 'xs'],
30891             ['sm', 'xs'],
30892             ['sm'],
30893             
30894             ['tall', 'xs', 'xs', 'xs'],
30895             ['tall', 'xs', 'xs'],
30896             ['tall', 'xs'],
30897             ['tall']
30898             
30899         ];
30900         
30901         var queue = [];
30902         
30903         var boxes = [];
30904         
30905         var box = [];
30906         
30907         Roo.each(items, function(item, k){
30908             
30909             switch (item.size) {
30910                 // these layouts take up a full box,
30911                 case 'md' :
30912                 case 'md-left' :
30913                 case 'md-right' :
30914                 case 'wide' :
30915                     
30916                     if(box.length){
30917                         boxes.push(box);
30918                         box = [];
30919                     }
30920                     
30921                     boxes.push([item]);
30922                     
30923                     break;
30924                     
30925                 case 'xs' :
30926                 case 'sm' :
30927                 case 'tall' :
30928                     
30929                     box.push(item);
30930                     
30931                     break;
30932                 default :
30933                     break;
30934                     
30935             }
30936             
30937         }, this);
30938         
30939         if(box.length){
30940             boxes.push(box);
30941             box = [];
30942         }
30943         
30944         var filterPattern = function(box, length)
30945         {
30946             if(!box.length){
30947                 return;
30948             }
30949             
30950             var match = false;
30951             
30952             var pattern = box.slice(0, length);
30953             
30954             var format = [];
30955             
30956             Roo.each(pattern, function(i){
30957                 format.push(i.size);
30958             }, this);
30959             
30960             Roo.each(standard, function(s){
30961                 
30962                 if(String(s) != String(format)){
30963                     return;
30964                 }
30965                 
30966                 match = true;
30967                 return false;
30968                 
30969             }, this);
30970             
30971             if(!match && length == 1){
30972                 return;
30973             }
30974             
30975             if(!match){
30976                 filterPattern(box, length - 1);
30977                 return;
30978             }
30979                 
30980             queue.push(pattern);
30981
30982             box = box.slice(length, box.length);
30983
30984             filterPattern(box, 4);
30985
30986             return;
30987             
30988         }
30989         
30990         Roo.each(boxes, function(box, k){
30991             
30992             if(!box.length){
30993                 return;
30994             }
30995             
30996             if(box.length == 1){
30997                 queue.push(box);
30998                 return;
30999             }
31000             
31001             filterPattern(box, 4);
31002             
31003         }, this);
31004         
31005         this._processVerticalLayoutQueue( queue, isInstant );
31006         
31007     },
31008     
31009 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31010 //    {
31011 //        if ( !items || !items.length ) {
31012 //            return;
31013 //        }
31014 //
31015 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31016 //        
31017 //    },
31018     
31019     _horizontalLayoutItems : function ( items , isInstant)
31020     {
31021         if ( !items || !items.length || items.length < 3) {
31022             return;
31023         }
31024         
31025         items.reverse();
31026         
31027         var eItems = items.slice(0, 3);
31028         
31029         items = items.slice(3, items.length);
31030         
31031         var standard = [
31032             ['xs', 'xs', 'xs', 'wide'],
31033             ['xs', 'xs', 'wide'],
31034             ['xs', 'xs', 'sm'],
31035             ['xs', 'xs', 'xs'],
31036             ['xs', 'wide'],
31037             ['xs', 'sm'],
31038             ['xs', 'xs'],
31039             ['xs'],
31040             
31041             ['sm', 'xs', 'xs'],
31042             ['sm', 'xs'],
31043             ['sm'],
31044             
31045             ['wide', 'xs', 'xs', 'xs'],
31046             ['wide', 'xs', 'xs'],
31047             ['wide', 'xs'],
31048             ['wide'],
31049             
31050             ['wide-thin']
31051         ];
31052         
31053         var queue = [];
31054         
31055         var boxes = [];
31056         
31057         var box = [];
31058         
31059         Roo.each(items, function(item, k){
31060             
31061             switch (item.size) {
31062                 case 'md' :
31063                 case 'md-left' :
31064                 case 'md-right' :
31065                 case 'tall' :
31066                     
31067                     if(box.length){
31068                         boxes.push(box);
31069                         box = [];
31070                     }
31071                     
31072                     boxes.push([item]);
31073                     
31074                     break;
31075                     
31076                 case 'xs' :
31077                 case 'sm' :
31078                 case 'wide' :
31079                 case 'wide-thin' :
31080                     
31081                     box.push(item);
31082                     
31083                     break;
31084                 default :
31085                     break;
31086                     
31087             }
31088             
31089         }, this);
31090         
31091         if(box.length){
31092             boxes.push(box);
31093             box = [];
31094         }
31095         
31096         var filterPattern = function(box, length)
31097         {
31098             if(!box.length){
31099                 return;
31100             }
31101             
31102             var match = false;
31103             
31104             var pattern = box.slice(0, length);
31105             
31106             var format = [];
31107             
31108             Roo.each(pattern, function(i){
31109                 format.push(i.size);
31110             }, this);
31111             
31112             Roo.each(standard, function(s){
31113                 
31114                 if(String(s) != String(format)){
31115                     return;
31116                 }
31117                 
31118                 match = true;
31119                 return false;
31120                 
31121             }, this);
31122             
31123             if(!match && length == 1){
31124                 return;
31125             }
31126             
31127             if(!match){
31128                 filterPattern(box, length - 1);
31129                 return;
31130             }
31131                 
31132             queue.push(pattern);
31133
31134             box = box.slice(length, box.length);
31135
31136             filterPattern(box, 4);
31137
31138             return;
31139             
31140         }
31141         
31142         Roo.each(boxes, function(box, k){
31143             
31144             if(!box.length){
31145                 return;
31146             }
31147             
31148             if(box.length == 1){
31149                 queue.push(box);
31150                 return;
31151             }
31152             
31153             filterPattern(box, 4);
31154             
31155         }, this);
31156         
31157         
31158         var prune = [];
31159         
31160         var pos = this.el.getBox(true);
31161         
31162         var minX = pos.x;
31163         
31164         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31165         
31166         var hit_end = false;
31167         
31168         Roo.each(queue, function(box){
31169             
31170             if(hit_end){
31171                 
31172                 Roo.each(box, function(b){
31173                 
31174                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31175                     b.el.hide();
31176
31177                 }, this);
31178
31179                 return;
31180             }
31181             
31182             var mx = 0;
31183             
31184             Roo.each(box, function(b){
31185                 
31186                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31187                 b.el.show();
31188
31189                 mx = Math.max(mx, b.x);
31190                 
31191             }, this);
31192             
31193             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31194             
31195             if(maxX < minX){
31196                 
31197                 Roo.each(box, function(b){
31198                 
31199                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31200                     b.el.hide();
31201                     
31202                 }, this);
31203                 
31204                 hit_end = true;
31205                 
31206                 return;
31207             }
31208             
31209             prune.push(box);
31210             
31211         }, this);
31212         
31213         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31214     },
31215     
31216     /** Sets position of item in DOM
31217     * @param {Element} item
31218     * @param {Number} x - horizontal position
31219     * @param {Number} y - vertical position
31220     * @param {Boolean} isInstant - disables transitions
31221     */
31222     _processVerticalLayoutQueue : function( queue, isInstant )
31223     {
31224         var pos = this.el.getBox(true);
31225         var x = pos.x;
31226         var y = pos.y;
31227         var maxY = [];
31228         
31229         for (var i = 0; i < this.cols; i++){
31230             maxY[i] = pos.y;
31231         }
31232         
31233         Roo.each(queue, function(box, k){
31234             
31235             var col = k % this.cols;
31236             
31237             Roo.each(box, function(b,kk){
31238                 
31239                 b.el.position('absolute');
31240                 
31241                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31242                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31243                 
31244                 if(b.size == 'md-left' || b.size == 'md-right'){
31245                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31246                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31247                 }
31248                 
31249                 b.el.setWidth(width);
31250                 b.el.setHeight(height);
31251                 // iframe?
31252                 b.el.select('iframe',true).setSize(width,height);
31253                 
31254             }, this);
31255             
31256             for (var i = 0; i < this.cols; i++){
31257                 
31258                 if(maxY[i] < maxY[col]){
31259                     col = i;
31260                     continue;
31261                 }
31262                 
31263                 col = Math.min(col, i);
31264                 
31265             }
31266             
31267             x = pos.x + col * (this.colWidth + this.padWidth);
31268             
31269             y = maxY[col];
31270             
31271             var positions = [];
31272             
31273             switch (box.length){
31274                 case 1 :
31275                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31276                     break;
31277                 case 2 :
31278                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31279                     break;
31280                 case 3 :
31281                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31282                     break;
31283                 case 4 :
31284                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31285                     break;
31286                 default :
31287                     break;
31288             }
31289             
31290             Roo.each(box, function(b,kk){
31291                 
31292                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31293                 
31294                 var sz = b.el.getSize();
31295                 
31296                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31297                 
31298             }, this);
31299             
31300         }, this);
31301         
31302         var mY = 0;
31303         
31304         for (var i = 0; i < this.cols; i++){
31305             mY = Math.max(mY, maxY[i]);
31306         }
31307         
31308         this.el.setHeight(mY - pos.y);
31309         
31310     },
31311     
31312 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31313 //    {
31314 //        var pos = this.el.getBox(true);
31315 //        var x = pos.x;
31316 //        var y = pos.y;
31317 //        var maxX = pos.right;
31318 //        
31319 //        var maxHeight = 0;
31320 //        
31321 //        Roo.each(items, function(item, k){
31322 //            
31323 //            var c = k % 2;
31324 //            
31325 //            item.el.position('absolute');
31326 //                
31327 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31328 //
31329 //            item.el.setWidth(width);
31330 //
31331 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31332 //
31333 //            item.el.setHeight(height);
31334 //            
31335 //            if(c == 0){
31336 //                item.el.setXY([x, y], isInstant ? false : true);
31337 //            } else {
31338 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31339 //            }
31340 //            
31341 //            y = y + height + this.alternativePadWidth;
31342 //            
31343 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31344 //            
31345 //        }, this);
31346 //        
31347 //        this.el.setHeight(maxHeight);
31348 //        
31349 //    },
31350     
31351     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31352     {
31353         var pos = this.el.getBox(true);
31354         
31355         var minX = pos.x;
31356         var minY = pos.y;
31357         
31358         var maxX = pos.right;
31359         
31360         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31361         
31362         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31363         
31364         Roo.each(queue, function(box, k){
31365             
31366             Roo.each(box, function(b, kk){
31367                 
31368                 b.el.position('absolute');
31369                 
31370                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31371                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31372                 
31373                 if(b.size == 'md-left' || b.size == 'md-right'){
31374                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31375                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31376                 }
31377                 
31378                 b.el.setWidth(width);
31379                 b.el.setHeight(height);
31380                 
31381             }, this);
31382             
31383             if(!box.length){
31384                 return;
31385             }
31386             
31387             var positions = [];
31388             
31389             switch (box.length){
31390                 case 1 :
31391                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31392                     break;
31393                 case 2 :
31394                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31395                     break;
31396                 case 3 :
31397                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31398                     break;
31399                 case 4 :
31400                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31401                     break;
31402                 default :
31403                     break;
31404             }
31405             
31406             Roo.each(box, function(b,kk){
31407                 
31408                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31409                 
31410                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31411                 
31412             }, this);
31413             
31414         }, this);
31415         
31416     },
31417     
31418     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31419     {
31420         Roo.each(eItems, function(b,k){
31421             
31422             b.size = (k == 0) ? 'sm' : 'xs';
31423             b.x = (k == 0) ? 2 : 1;
31424             b.y = (k == 0) ? 2 : 1;
31425             
31426             b.el.position('absolute');
31427             
31428             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31429                 
31430             b.el.setWidth(width);
31431             
31432             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31433             
31434             b.el.setHeight(height);
31435             
31436         }, this);
31437
31438         var positions = [];
31439         
31440         positions.push({
31441             x : maxX - this.unitWidth * 2 - this.gutter,
31442             y : minY
31443         });
31444         
31445         positions.push({
31446             x : maxX - this.unitWidth,
31447             y : minY + (this.unitWidth + this.gutter) * 2
31448         });
31449         
31450         positions.push({
31451             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31452             y : minY
31453         });
31454         
31455         Roo.each(eItems, function(b,k){
31456             
31457             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31458
31459         }, this);
31460         
31461     },
31462     
31463     getVerticalOneBoxColPositions : function(x, y, box)
31464     {
31465         var pos = [];
31466         
31467         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31468         
31469         if(box[0].size == 'md-left'){
31470             rand = 0;
31471         }
31472         
31473         if(box[0].size == 'md-right'){
31474             rand = 1;
31475         }
31476         
31477         pos.push({
31478             x : x + (this.unitWidth + this.gutter) * rand,
31479             y : y
31480         });
31481         
31482         return pos;
31483     },
31484     
31485     getVerticalTwoBoxColPositions : function(x, y, box)
31486     {
31487         var pos = [];
31488         
31489         if(box[0].size == 'xs'){
31490             
31491             pos.push({
31492                 x : x,
31493                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31494             });
31495
31496             pos.push({
31497                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31498                 y : y
31499             });
31500             
31501             return pos;
31502             
31503         }
31504         
31505         pos.push({
31506             x : x,
31507             y : y
31508         });
31509
31510         pos.push({
31511             x : x + (this.unitWidth + this.gutter) * 2,
31512             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31513         });
31514         
31515         return pos;
31516         
31517     },
31518     
31519     getVerticalThreeBoxColPositions : function(x, y, box)
31520     {
31521         var pos = [];
31522         
31523         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31524             
31525             pos.push({
31526                 x : x,
31527                 y : y
31528             });
31529
31530             pos.push({
31531                 x : x + (this.unitWidth + this.gutter) * 1,
31532                 y : y
31533             });
31534             
31535             pos.push({
31536                 x : x + (this.unitWidth + this.gutter) * 2,
31537                 y : y
31538             });
31539             
31540             return pos;
31541             
31542         }
31543         
31544         if(box[0].size == 'xs' && box[1].size == 'xs'){
31545             
31546             pos.push({
31547                 x : x,
31548                 y : y
31549             });
31550
31551             pos.push({
31552                 x : x,
31553                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31554             });
31555             
31556             pos.push({
31557                 x : x + (this.unitWidth + this.gutter) * 1,
31558                 y : y
31559             });
31560             
31561             return pos;
31562             
31563         }
31564         
31565         pos.push({
31566             x : x,
31567             y : y
31568         });
31569
31570         pos.push({
31571             x : x + (this.unitWidth + this.gutter) * 2,
31572             y : y
31573         });
31574
31575         pos.push({
31576             x : x + (this.unitWidth + this.gutter) * 2,
31577             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31578         });
31579             
31580         return pos;
31581         
31582     },
31583     
31584     getVerticalFourBoxColPositions : function(x, y, box)
31585     {
31586         var pos = [];
31587         
31588         if(box[0].size == 'xs'){
31589             
31590             pos.push({
31591                 x : x,
31592                 y : y
31593             });
31594
31595             pos.push({
31596                 x : x,
31597                 y : y + (this.unitHeight + this.gutter) * 1
31598             });
31599             
31600             pos.push({
31601                 x : x,
31602                 y : y + (this.unitHeight + this.gutter) * 2
31603             });
31604             
31605             pos.push({
31606                 x : x + (this.unitWidth + this.gutter) * 1,
31607                 y : y
31608             });
31609             
31610             return pos;
31611             
31612         }
31613         
31614         pos.push({
31615             x : x,
31616             y : y
31617         });
31618
31619         pos.push({
31620             x : x + (this.unitWidth + this.gutter) * 2,
31621             y : y
31622         });
31623
31624         pos.push({
31625             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31626             y : y + (this.unitHeight + this.gutter) * 1
31627         });
31628
31629         pos.push({
31630             x : x + (this.unitWidth + this.gutter) * 2,
31631             y : y + (this.unitWidth + this.gutter) * 2
31632         });
31633
31634         return pos;
31635         
31636     },
31637     
31638     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31639     {
31640         var pos = [];
31641         
31642         if(box[0].size == 'md-left'){
31643             pos.push({
31644                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31645                 y : minY
31646             });
31647             
31648             return pos;
31649         }
31650         
31651         if(box[0].size == 'md-right'){
31652             pos.push({
31653                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31654                 y : minY + (this.unitWidth + this.gutter) * 1
31655             });
31656             
31657             return pos;
31658         }
31659         
31660         var rand = Math.floor(Math.random() * (4 - box[0].y));
31661         
31662         pos.push({
31663             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31664             y : minY + (this.unitWidth + this.gutter) * rand
31665         });
31666         
31667         return pos;
31668         
31669     },
31670     
31671     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31672     {
31673         var pos = [];
31674         
31675         if(box[0].size == 'xs'){
31676             
31677             pos.push({
31678                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31679                 y : minY
31680             });
31681
31682             pos.push({
31683                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31684                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31685             });
31686             
31687             return pos;
31688             
31689         }
31690         
31691         pos.push({
31692             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31693             y : minY
31694         });
31695
31696         pos.push({
31697             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31698             y : minY + (this.unitWidth + this.gutter) * 2
31699         });
31700         
31701         return pos;
31702         
31703     },
31704     
31705     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31706     {
31707         var pos = [];
31708         
31709         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31710             
31711             pos.push({
31712                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31713                 y : minY
31714             });
31715
31716             pos.push({
31717                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31718                 y : minY + (this.unitWidth + this.gutter) * 1
31719             });
31720             
31721             pos.push({
31722                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31723                 y : minY + (this.unitWidth + this.gutter) * 2
31724             });
31725             
31726             return pos;
31727             
31728         }
31729         
31730         if(box[0].size == 'xs' && box[1].size == 'xs'){
31731             
31732             pos.push({
31733                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31734                 y : minY
31735             });
31736
31737             pos.push({
31738                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31739                 y : minY
31740             });
31741             
31742             pos.push({
31743                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31744                 y : minY + (this.unitWidth + this.gutter) * 1
31745             });
31746             
31747             return pos;
31748             
31749         }
31750         
31751         pos.push({
31752             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31753             y : minY
31754         });
31755
31756         pos.push({
31757             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31758             y : minY + (this.unitWidth + this.gutter) * 2
31759         });
31760
31761         pos.push({
31762             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31763             y : minY + (this.unitWidth + this.gutter) * 2
31764         });
31765             
31766         return pos;
31767         
31768     },
31769     
31770     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31771     {
31772         var pos = [];
31773         
31774         if(box[0].size == 'xs'){
31775             
31776             pos.push({
31777                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31778                 y : minY
31779             });
31780
31781             pos.push({
31782                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31783                 y : minY
31784             });
31785             
31786             pos.push({
31787                 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),
31788                 y : minY
31789             });
31790             
31791             pos.push({
31792                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31793                 y : minY + (this.unitWidth + this.gutter) * 1
31794             });
31795             
31796             return pos;
31797             
31798         }
31799         
31800         pos.push({
31801             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31802             y : minY
31803         });
31804         
31805         pos.push({
31806             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31807             y : minY + (this.unitWidth + this.gutter) * 2
31808         });
31809         
31810         pos.push({
31811             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31812             y : minY + (this.unitWidth + this.gutter) * 2
31813         });
31814         
31815         pos.push({
31816             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),
31817             y : minY + (this.unitWidth + this.gutter) * 2
31818         });
31819
31820         return pos;
31821         
31822     },
31823     
31824     /**
31825     * remove a Masonry Brick
31826     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31827     */
31828     removeBrick : function(brick_id)
31829     {
31830         if (!brick_id) {
31831             return;
31832         }
31833         
31834         for (var i = 0; i<this.bricks.length; i++) {
31835             if (this.bricks[i].id == brick_id) {
31836                 this.bricks.splice(i,1);
31837                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31838                 this.initial();
31839             }
31840         }
31841     },
31842     
31843     /**
31844     * adds a Masonry Brick
31845     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31846     */
31847     addBrick : function(cfg)
31848     {
31849         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31850         //this.register(cn);
31851         cn.parentId = this.id;
31852         cn.onRender(this.el, null);
31853         return cn;
31854     },
31855     
31856     /**
31857     * register a Masonry Brick
31858     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31859     */
31860     
31861     register : function(brick)
31862     {
31863         this.bricks.push(brick);
31864         brick.masonryId = this.id;
31865     },
31866     
31867     /**
31868     * clear all the Masonry Brick
31869     */
31870     clearAll : function()
31871     {
31872         this.bricks = [];
31873         //this.getChildContainer().dom.innerHTML = "";
31874         this.el.dom.innerHTML = '';
31875     },
31876     
31877     getSelected : function()
31878     {
31879         if (!this.selectedBrick) {
31880             return false;
31881         }
31882         
31883         return this.selectedBrick;
31884     }
31885 });
31886
31887 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31888     
31889     groups: {},
31890      /**
31891     * register a Masonry Layout
31892     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31893     */
31894     
31895     register : function(layout)
31896     {
31897         this.groups[layout.id] = layout;
31898     },
31899     /**
31900     * fetch a  Masonry Layout based on the masonry layout ID
31901     * @param {string} the masonry layout to add
31902     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31903     */
31904     
31905     get: function(layout_id) {
31906         if (typeof(this.groups[layout_id]) == 'undefined') {
31907             return false;
31908         }
31909         return this.groups[layout_id] ;
31910     }
31911     
31912     
31913     
31914 });
31915
31916  
31917
31918  /**
31919  *
31920  * This is based on 
31921  * http://masonry.desandro.com
31922  *
31923  * The idea is to render all the bricks based on vertical width...
31924  *
31925  * The original code extends 'outlayer' - we might need to use that....
31926  * 
31927  */
31928
31929
31930 /**
31931  * @class Roo.bootstrap.LayoutMasonryAuto
31932  * @extends Roo.bootstrap.Component
31933  * Bootstrap Layout Masonry class
31934  * 
31935  * @constructor
31936  * Create a new Element
31937  * @param {Object} config The config object
31938  */
31939
31940 Roo.bootstrap.LayoutMasonryAuto = function(config){
31941     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31942 };
31943
31944 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31945     
31946       /**
31947      * @cfg {Boolean} isFitWidth  - resize the width..
31948      */   
31949     isFitWidth : false,  // options..
31950     /**
31951      * @cfg {Boolean} isOriginLeft = left align?
31952      */   
31953     isOriginLeft : true,
31954     /**
31955      * @cfg {Boolean} isOriginTop = top align?
31956      */   
31957     isOriginTop : false,
31958     /**
31959      * @cfg {Boolean} isLayoutInstant = no animation?
31960      */   
31961     isLayoutInstant : false, // needed?
31962     /**
31963      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31964      */   
31965     isResizingContainer : true,
31966     /**
31967      * @cfg {Number} columnWidth  width of the columns 
31968      */   
31969     
31970     columnWidth : 0,
31971     
31972     /**
31973      * @cfg {Number} maxCols maximum number of columns
31974      */   
31975     
31976     maxCols: 0,
31977     /**
31978      * @cfg {Number} padHeight padding below box..
31979      */   
31980     
31981     padHeight : 10, 
31982     
31983     /**
31984      * @cfg {Boolean} isAutoInitial defalut true
31985      */   
31986     
31987     isAutoInitial : true, 
31988     
31989     // private?
31990     gutter : 0,
31991     
31992     containerWidth: 0,
31993     initialColumnWidth : 0,
31994     currentSize : null,
31995     
31996     colYs : null, // array.
31997     maxY : 0,
31998     padWidth: 10,
31999     
32000     
32001     tag: 'div',
32002     cls: '',
32003     bricks: null, //CompositeElement
32004     cols : 0, // array?
32005     // element : null, // wrapped now this.el
32006     _isLayoutInited : null, 
32007     
32008     
32009     getAutoCreate : function(){
32010         
32011         var cfg = {
32012             tag: this.tag,
32013             cls: 'blog-masonary-wrapper ' + this.cls,
32014             cn : {
32015                 cls : 'mas-boxes masonary'
32016             }
32017         };
32018         
32019         return cfg;
32020     },
32021     
32022     getChildContainer: function( )
32023     {
32024         if (this.boxesEl) {
32025             return this.boxesEl;
32026         }
32027         
32028         this.boxesEl = this.el.select('.mas-boxes').first();
32029         
32030         return this.boxesEl;
32031     },
32032     
32033     
32034     initEvents : function()
32035     {
32036         var _this = this;
32037         
32038         if(this.isAutoInitial){
32039             Roo.log('hook children rendered');
32040             this.on('childrenrendered', function() {
32041                 Roo.log('children rendered');
32042                 _this.initial();
32043             } ,this);
32044         }
32045         
32046     },
32047     
32048     initial : function()
32049     {
32050         this.reloadItems();
32051
32052         this.currentSize = this.el.getBox(true);
32053
32054         /// was window resize... - let's see if this works..
32055         Roo.EventManager.onWindowResize(this.resize, this); 
32056
32057         if(!this.isAutoInitial){
32058             this.layout();
32059             return;
32060         }
32061         
32062         this.layout.defer(500,this);
32063     },
32064     
32065     reloadItems: function()
32066     {
32067         this.bricks = this.el.select('.masonry-brick', true);
32068         
32069         this.bricks.each(function(b) {
32070             //Roo.log(b.getSize());
32071             if (!b.attr('originalwidth')) {
32072                 b.attr('originalwidth',  b.getSize().width);
32073             }
32074             
32075         });
32076         
32077         Roo.log(this.bricks.elements.length);
32078     },
32079     
32080     resize : function()
32081     {
32082         Roo.log('resize');
32083         var cs = this.el.getBox(true);
32084         
32085         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32086             Roo.log("no change in with or X");
32087             return;
32088         }
32089         this.currentSize = cs;
32090         this.layout();
32091     },
32092     
32093     layout : function()
32094     {
32095          Roo.log('layout');
32096         this._resetLayout();
32097         //this._manageStamps();
32098       
32099         // don't animate first layout
32100         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32101         this.layoutItems( isInstant );
32102       
32103         // flag for initalized
32104         this._isLayoutInited = true;
32105     },
32106     
32107     layoutItems : function( isInstant )
32108     {
32109         //var items = this._getItemsForLayout( this.items );
32110         // original code supports filtering layout items.. we just ignore it..
32111         
32112         this._layoutItems( this.bricks , isInstant );
32113       
32114         this._postLayout();
32115     },
32116     _layoutItems : function ( items , isInstant)
32117     {
32118        //this.fireEvent( 'layout', this, items );
32119     
32120
32121         if ( !items || !items.elements.length ) {
32122           // no items, emit event with empty array
32123             return;
32124         }
32125
32126         var queue = [];
32127         items.each(function(item) {
32128             Roo.log("layout item");
32129             Roo.log(item);
32130             // get x/y object from method
32131             var position = this._getItemLayoutPosition( item );
32132             // enqueue
32133             position.item = item;
32134             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32135             queue.push( position );
32136         }, this);
32137       
32138         this._processLayoutQueue( queue );
32139     },
32140     /** Sets position of item in DOM
32141     * @param {Element} item
32142     * @param {Number} x - horizontal position
32143     * @param {Number} y - vertical position
32144     * @param {Boolean} isInstant - disables transitions
32145     */
32146     _processLayoutQueue : function( queue )
32147     {
32148         for ( var i=0, len = queue.length; i < len; i++ ) {
32149             var obj = queue[i];
32150             obj.item.position('absolute');
32151             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32152         }
32153     },
32154       
32155     
32156     /**
32157     * Any logic you want to do after each layout,
32158     * i.e. size the container
32159     */
32160     _postLayout : function()
32161     {
32162         this.resizeContainer();
32163     },
32164     
32165     resizeContainer : function()
32166     {
32167         if ( !this.isResizingContainer ) {
32168             return;
32169         }
32170         var size = this._getContainerSize();
32171         if ( size ) {
32172             this.el.setSize(size.width,size.height);
32173             this.boxesEl.setSize(size.width,size.height);
32174         }
32175     },
32176     
32177     
32178     
32179     _resetLayout : function()
32180     {
32181         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32182         this.colWidth = this.el.getWidth();
32183         //this.gutter = this.el.getWidth(); 
32184         
32185         this.measureColumns();
32186
32187         // reset column Y
32188         var i = this.cols;
32189         this.colYs = [];
32190         while (i--) {
32191             this.colYs.push( 0 );
32192         }
32193     
32194         this.maxY = 0;
32195     },
32196
32197     measureColumns : function()
32198     {
32199         this.getContainerWidth();
32200       // if columnWidth is 0, default to outerWidth of first item
32201         if ( !this.columnWidth ) {
32202             var firstItem = this.bricks.first();
32203             Roo.log(firstItem);
32204             this.columnWidth  = this.containerWidth;
32205             if (firstItem && firstItem.attr('originalwidth') ) {
32206                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32207             }
32208             // columnWidth fall back to item of first element
32209             Roo.log("set column width?");
32210                         this.initialColumnWidth = this.columnWidth  ;
32211
32212             // if first elem has no width, default to size of container
32213             
32214         }
32215         
32216         
32217         if (this.initialColumnWidth) {
32218             this.columnWidth = this.initialColumnWidth;
32219         }
32220         
32221         
32222             
32223         // column width is fixed at the top - however if container width get's smaller we should
32224         // reduce it...
32225         
32226         // this bit calcs how man columns..
32227             
32228         var columnWidth = this.columnWidth += this.gutter;
32229       
32230         // calculate columns
32231         var containerWidth = this.containerWidth + this.gutter;
32232         
32233         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32234         // fix rounding errors, typically with gutters
32235         var excess = columnWidth - containerWidth % columnWidth;
32236         
32237         
32238         // if overshoot is less than a pixel, round up, otherwise floor it
32239         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32240         cols = Math[ mathMethod ]( cols );
32241         this.cols = Math.max( cols, 1 );
32242         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32243         
32244          // padding positioning..
32245         var totalColWidth = this.cols * this.columnWidth;
32246         var padavail = this.containerWidth - totalColWidth;
32247         // so for 2 columns - we need 3 'pads'
32248         
32249         var padNeeded = (1+this.cols) * this.padWidth;
32250         
32251         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32252         
32253         this.columnWidth += padExtra
32254         //this.padWidth = Math.floor(padavail /  ( this.cols));
32255         
32256         // adjust colum width so that padding is fixed??
32257         
32258         // we have 3 columns ... total = width * 3
32259         // we have X left over... that should be used by 
32260         
32261         //if (this.expandC) {
32262             
32263         //}
32264         
32265         
32266         
32267     },
32268     
32269     getContainerWidth : function()
32270     {
32271        /* // container is parent if fit width
32272         var container = this.isFitWidth ? this.element.parentNode : this.element;
32273         // check that this.size and size are there
32274         // IE8 triggers resize on body size change, so they might not be
32275         
32276         var size = getSize( container );  //FIXME
32277         this.containerWidth = size && size.innerWidth; //FIXME
32278         */
32279          
32280         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32281         
32282     },
32283     
32284     _getItemLayoutPosition : function( item )  // what is item?
32285     {
32286         // we resize the item to our columnWidth..
32287       
32288         item.setWidth(this.columnWidth);
32289         item.autoBoxAdjust  = false;
32290         
32291         var sz = item.getSize();
32292  
32293         // how many columns does this brick span
32294         var remainder = this.containerWidth % this.columnWidth;
32295         
32296         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32297         // round if off by 1 pixel, otherwise use ceil
32298         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32299         colSpan = Math.min( colSpan, this.cols );
32300         
32301         // normally this should be '1' as we dont' currently allow multi width columns..
32302         
32303         var colGroup = this._getColGroup( colSpan );
32304         // get the minimum Y value from the columns
32305         var minimumY = Math.min.apply( Math, colGroup );
32306         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32307         
32308         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32309          
32310         // position the brick
32311         var position = {
32312             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32313             y: this.currentSize.y + minimumY + this.padHeight
32314         };
32315         
32316         Roo.log(position);
32317         // apply setHeight to necessary columns
32318         var setHeight = minimumY + sz.height + this.padHeight;
32319         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32320         
32321         var setSpan = this.cols + 1 - colGroup.length;
32322         for ( var i = 0; i < setSpan; i++ ) {
32323           this.colYs[ shortColIndex + i ] = setHeight ;
32324         }
32325       
32326         return position;
32327     },
32328     
32329     /**
32330      * @param {Number} colSpan - number of columns the element spans
32331      * @returns {Array} colGroup
32332      */
32333     _getColGroup : function( colSpan )
32334     {
32335         if ( colSpan < 2 ) {
32336           // if brick spans only one column, use all the column Ys
32337           return this.colYs;
32338         }
32339       
32340         var colGroup = [];
32341         // how many different places could this brick fit horizontally
32342         var groupCount = this.cols + 1 - colSpan;
32343         // for each group potential horizontal position
32344         for ( var i = 0; i < groupCount; i++ ) {
32345           // make an array of colY values for that one group
32346           var groupColYs = this.colYs.slice( i, i + colSpan );
32347           // and get the max value of the array
32348           colGroup[i] = Math.max.apply( Math, groupColYs );
32349         }
32350         return colGroup;
32351     },
32352     /*
32353     _manageStamp : function( stamp )
32354     {
32355         var stampSize =  stamp.getSize();
32356         var offset = stamp.getBox();
32357         // get the columns that this stamp affects
32358         var firstX = this.isOriginLeft ? offset.x : offset.right;
32359         var lastX = firstX + stampSize.width;
32360         var firstCol = Math.floor( firstX / this.columnWidth );
32361         firstCol = Math.max( 0, firstCol );
32362         
32363         var lastCol = Math.floor( lastX / this.columnWidth );
32364         // lastCol should not go over if multiple of columnWidth #425
32365         lastCol -= lastX % this.columnWidth ? 0 : 1;
32366         lastCol = Math.min( this.cols - 1, lastCol );
32367         
32368         // set colYs to bottom of the stamp
32369         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32370             stampSize.height;
32371             
32372         for ( var i = firstCol; i <= lastCol; i++ ) {
32373           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32374         }
32375     },
32376     */
32377     
32378     _getContainerSize : function()
32379     {
32380         this.maxY = Math.max.apply( Math, this.colYs );
32381         var size = {
32382             height: this.maxY
32383         };
32384       
32385         if ( this.isFitWidth ) {
32386             size.width = this._getContainerFitWidth();
32387         }
32388       
32389         return size;
32390     },
32391     
32392     _getContainerFitWidth : function()
32393     {
32394         var unusedCols = 0;
32395         // count unused columns
32396         var i = this.cols;
32397         while ( --i ) {
32398           if ( this.colYs[i] !== 0 ) {
32399             break;
32400           }
32401           unusedCols++;
32402         }
32403         // fit container to columns that have been used
32404         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32405     },
32406     
32407     needsResizeLayout : function()
32408     {
32409         var previousWidth = this.containerWidth;
32410         this.getContainerWidth();
32411         return previousWidth !== this.containerWidth;
32412     }
32413  
32414 });
32415
32416  
32417
32418  /*
32419  * - LGPL
32420  *
32421  * element
32422  * 
32423  */
32424
32425 /**
32426  * @class Roo.bootstrap.MasonryBrick
32427  * @extends Roo.bootstrap.Component
32428  * Bootstrap MasonryBrick class
32429  * 
32430  * @constructor
32431  * Create a new MasonryBrick
32432  * @param {Object} config The config object
32433  */
32434
32435 Roo.bootstrap.MasonryBrick = function(config){
32436     
32437     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32438     
32439     Roo.bootstrap.MasonryBrick.register(this);
32440     
32441     this.addEvents({
32442         // raw events
32443         /**
32444          * @event click
32445          * When a MasonryBrick is clcik
32446          * @param {Roo.bootstrap.MasonryBrick} this
32447          * @param {Roo.EventObject} e
32448          */
32449         "click" : true
32450     });
32451 };
32452
32453 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32454     
32455     /**
32456      * @cfg {String} title
32457      */   
32458     title : '',
32459     /**
32460      * @cfg {String} html
32461      */   
32462     html : '',
32463     /**
32464      * @cfg {String} bgimage
32465      */   
32466     bgimage : '',
32467     /**
32468      * @cfg {String} videourl
32469      */   
32470     videourl : '',
32471     /**
32472      * @cfg {String} cls
32473      */   
32474     cls : '',
32475     /**
32476      * @cfg {String} href
32477      */   
32478     href : '',
32479     /**
32480      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32481      */   
32482     size : 'xs',
32483     
32484     /**
32485      * @cfg {String} placetitle (center|bottom)
32486      */   
32487     placetitle : '',
32488     
32489     /**
32490      * @cfg {Boolean} isFitContainer defalut true
32491      */   
32492     isFitContainer : true, 
32493     
32494     /**
32495      * @cfg {Boolean} preventDefault defalut false
32496      */   
32497     preventDefault : false, 
32498     
32499     /**
32500      * @cfg {Boolean} inverse defalut false
32501      */   
32502     maskInverse : false, 
32503     
32504     getAutoCreate : function()
32505     {
32506         if(!this.isFitContainer){
32507             return this.getSplitAutoCreate();
32508         }
32509         
32510         var cls = 'masonry-brick masonry-brick-full';
32511         
32512         if(this.href.length){
32513             cls += ' masonry-brick-link';
32514         }
32515         
32516         if(this.bgimage.length){
32517             cls += ' masonry-brick-image';
32518         }
32519         
32520         if(this.maskInverse){
32521             cls += ' mask-inverse';
32522         }
32523         
32524         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32525             cls += ' enable-mask';
32526         }
32527         
32528         if(this.size){
32529             cls += ' masonry-' + this.size + '-brick';
32530         }
32531         
32532         if(this.placetitle.length){
32533             
32534             switch (this.placetitle) {
32535                 case 'center' :
32536                     cls += ' masonry-center-title';
32537                     break;
32538                 case 'bottom' :
32539                     cls += ' masonry-bottom-title';
32540                     break;
32541                 default:
32542                     break;
32543             }
32544             
32545         } else {
32546             if(!this.html.length && !this.bgimage.length){
32547                 cls += ' masonry-center-title';
32548             }
32549
32550             if(!this.html.length && this.bgimage.length){
32551                 cls += ' masonry-bottom-title';
32552             }
32553         }
32554         
32555         if(this.cls){
32556             cls += ' ' + this.cls;
32557         }
32558         
32559         var cfg = {
32560             tag: (this.href.length) ? 'a' : 'div',
32561             cls: cls,
32562             cn: [
32563                 {
32564                     tag: 'div',
32565                     cls: 'masonry-brick-mask'
32566                 },
32567                 {
32568                     tag: 'div',
32569                     cls: 'masonry-brick-paragraph',
32570                     cn: []
32571                 }
32572             ]
32573         };
32574         
32575         if(this.href.length){
32576             cfg.href = this.href;
32577         }
32578         
32579         var cn = cfg.cn[1].cn;
32580         
32581         if(this.title.length){
32582             cn.push({
32583                 tag: 'h4',
32584                 cls: 'masonry-brick-title',
32585                 html: this.title
32586             });
32587         }
32588         
32589         if(this.html.length){
32590             cn.push({
32591                 tag: 'p',
32592                 cls: 'masonry-brick-text',
32593                 html: this.html
32594             });
32595         }
32596         
32597         if (!this.title.length && !this.html.length) {
32598             cfg.cn[1].cls += ' hide';
32599         }
32600         
32601         if(this.bgimage.length){
32602             cfg.cn.push({
32603                 tag: 'img',
32604                 cls: 'masonry-brick-image-view',
32605                 src: this.bgimage
32606             });
32607         }
32608         
32609         if(this.videourl.length){
32610             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32611             // youtube support only?
32612             cfg.cn.push({
32613                 tag: 'iframe',
32614                 cls: 'masonry-brick-image-view',
32615                 src: vurl,
32616                 frameborder : 0,
32617                 allowfullscreen : true
32618             });
32619         }
32620         
32621         return cfg;
32622         
32623     },
32624     
32625     getSplitAutoCreate : function()
32626     {
32627         var cls = 'masonry-brick masonry-brick-split';
32628         
32629         if(this.href.length){
32630             cls += ' masonry-brick-link';
32631         }
32632         
32633         if(this.bgimage.length){
32634             cls += ' masonry-brick-image';
32635         }
32636         
32637         if(this.size){
32638             cls += ' masonry-' + this.size + '-brick';
32639         }
32640         
32641         switch (this.placetitle) {
32642             case 'center' :
32643                 cls += ' masonry-center-title';
32644                 break;
32645             case 'bottom' :
32646                 cls += ' masonry-bottom-title';
32647                 break;
32648             default:
32649                 if(!this.bgimage.length){
32650                     cls += ' masonry-center-title';
32651                 }
32652
32653                 if(this.bgimage.length){
32654                     cls += ' masonry-bottom-title';
32655                 }
32656                 break;
32657         }
32658         
32659         if(this.cls){
32660             cls += ' ' + this.cls;
32661         }
32662         
32663         var cfg = {
32664             tag: (this.href.length) ? 'a' : 'div',
32665             cls: cls,
32666             cn: [
32667                 {
32668                     tag: 'div',
32669                     cls: 'masonry-brick-split-head',
32670                     cn: [
32671                         {
32672                             tag: 'div',
32673                             cls: 'masonry-brick-paragraph',
32674                             cn: []
32675                         }
32676                     ]
32677                 },
32678                 {
32679                     tag: 'div',
32680                     cls: 'masonry-brick-split-body',
32681                     cn: []
32682                 }
32683             ]
32684         };
32685         
32686         if(this.href.length){
32687             cfg.href = this.href;
32688         }
32689         
32690         if(this.title.length){
32691             cfg.cn[0].cn[0].cn.push({
32692                 tag: 'h4',
32693                 cls: 'masonry-brick-title',
32694                 html: this.title
32695             });
32696         }
32697         
32698         if(this.html.length){
32699             cfg.cn[1].cn.push({
32700                 tag: 'p',
32701                 cls: 'masonry-brick-text',
32702                 html: this.html
32703             });
32704         }
32705
32706         if(this.bgimage.length){
32707             cfg.cn[0].cn.push({
32708                 tag: 'img',
32709                 cls: 'masonry-brick-image-view',
32710                 src: this.bgimage
32711             });
32712         }
32713         
32714         if(this.videourl.length){
32715             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32716             // youtube support only?
32717             cfg.cn[0].cn.cn.push({
32718                 tag: 'iframe',
32719                 cls: 'masonry-brick-image-view',
32720                 src: vurl,
32721                 frameborder : 0,
32722                 allowfullscreen : true
32723             });
32724         }
32725         
32726         return cfg;
32727     },
32728     
32729     initEvents: function() 
32730     {
32731         switch (this.size) {
32732             case 'xs' :
32733                 this.x = 1;
32734                 this.y = 1;
32735                 break;
32736             case 'sm' :
32737                 this.x = 2;
32738                 this.y = 2;
32739                 break;
32740             case 'md' :
32741             case 'md-left' :
32742             case 'md-right' :
32743                 this.x = 3;
32744                 this.y = 3;
32745                 break;
32746             case 'tall' :
32747                 this.x = 2;
32748                 this.y = 3;
32749                 break;
32750             case 'wide' :
32751                 this.x = 3;
32752                 this.y = 2;
32753                 break;
32754             case 'wide-thin' :
32755                 this.x = 3;
32756                 this.y = 1;
32757                 break;
32758                         
32759             default :
32760                 break;
32761         }
32762         
32763         if(Roo.isTouch){
32764             this.el.on('touchstart', this.onTouchStart, this);
32765             this.el.on('touchmove', this.onTouchMove, this);
32766             this.el.on('touchend', this.onTouchEnd, this);
32767             this.el.on('contextmenu', this.onContextMenu, this);
32768         } else {
32769             this.el.on('mouseenter'  ,this.enter, this);
32770             this.el.on('mouseleave', this.leave, this);
32771             this.el.on('click', this.onClick, this);
32772         }
32773         
32774         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32775             this.parent().bricks.push(this);   
32776         }
32777         
32778     },
32779     
32780     onClick: function(e, el)
32781     {
32782         var time = this.endTimer - this.startTimer;
32783         // Roo.log(e.preventDefault());
32784         if(Roo.isTouch){
32785             if(time > 1000){
32786                 e.preventDefault();
32787                 return;
32788             }
32789         }
32790         
32791         if(!this.preventDefault){
32792             return;
32793         }
32794         
32795         e.preventDefault();
32796         
32797         if (this.activeClass != '') {
32798             this.selectBrick();
32799         }
32800         
32801         this.fireEvent('click', this, e);
32802     },
32803     
32804     enter: function(e, el)
32805     {
32806         e.preventDefault();
32807         
32808         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32809             return;
32810         }
32811         
32812         if(this.bgimage.length && this.html.length){
32813             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32814         }
32815     },
32816     
32817     leave: function(e, el)
32818     {
32819         e.preventDefault();
32820         
32821         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32822             return;
32823         }
32824         
32825         if(this.bgimage.length && this.html.length){
32826             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32827         }
32828     },
32829     
32830     onTouchStart: function(e, el)
32831     {
32832 //        e.preventDefault();
32833         
32834         this.touchmoved = false;
32835         
32836         if(!this.isFitContainer){
32837             return;
32838         }
32839         
32840         if(!this.bgimage.length || !this.html.length){
32841             return;
32842         }
32843         
32844         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32845         
32846         this.timer = new Date().getTime();
32847         
32848     },
32849     
32850     onTouchMove: function(e, el)
32851     {
32852         this.touchmoved = true;
32853     },
32854     
32855     onContextMenu : function(e,el)
32856     {
32857         e.preventDefault();
32858         e.stopPropagation();
32859         return false;
32860     },
32861     
32862     onTouchEnd: function(e, el)
32863     {
32864 //        e.preventDefault();
32865         
32866         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32867         
32868             this.leave(e,el);
32869             
32870             return;
32871         }
32872         
32873         if(!this.bgimage.length || !this.html.length){
32874             
32875             if(this.href.length){
32876                 window.location.href = this.href;
32877             }
32878             
32879             return;
32880         }
32881         
32882         if(!this.isFitContainer){
32883             return;
32884         }
32885         
32886         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32887         
32888         window.location.href = this.href;
32889     },
32890     
32891     //selection on single brick only
32892     selectBrick : function() {
32893         
32894         if (!this.parentId) {
32895             return;
32896         }
32897         
32898         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32899         var index = m.selectedBrick.indexOf(this.id);
32900         
32901         if ( index > -1) {
32902             m.selectedBrick.splice(index,1);
32903             this.el.removeClass(this.activeClass);
32904             return;
32905         }
32906         
32907         for(var i = 0; i < m.selectedBrick.length; i++) {
32908             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32909             b.el.removeClass(b.activeClass);
32910         }
32911         
32912         m.selectedBrick = [];
32913         
32914         m.selectedBrick.push(this.id);
32915         this.el.addClass(this.activeClass);
32916         return;
32917     },
32918     
32919     isSelected : function(){
32920         return this.el.hasClass(this.activeClass);
32921         
32922     }
32923 });
32924
32925 Roo.apply(Roo.bootstrap.MasonryBrick, {
32926     
32927     //groups: {},
32928     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32929      /**
32930     * register a Masonry Brick
32931     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32932     */
32933     
32934     register : function(brick)
32935     {
32936         //this.groups[brick.id] = brick;
32937         this.groups.add(brick.id, brick);
32938     },
32939     /**
32940     * fetch a  masonry brick based on the masonry brick ID
32941     * @param {string} the masonry brick to add
32942     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32943     */
32944     
32945     get: function(brick_id) 
32946     {
32947         // if (typeof(this.groups[brick_id]) == 'undefined') {
32948         //     return false;
32949         // }
32950         // return this.groups[brick_id] ;
32951         
32952         if(this.groups.key(brick_id)) {
32953             return this.groups.key(brick_id);
32954         }
32955         
32956         return false;
32957     }
32958     
32959     
32960     
32961 });
32962
32963  /*
32964  * - LGPL
32965  *
32966  * element
32967  * 
32968  */
32969
32970 /**
32971  * @class Roo.bootstrap.Brick
32972  * @extends Roo.bootstrap.Component
32973  * Bootstrap Brick class
32974  * 
32975  * @constructor
32976  * Create a new Brick
32977  * @param {Object} config The config object
32978  */
32979
32980 Roo.bootstrap.Brick = function(config){
32981     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32982     
32983     this.addEvents({
32984         // raw events
32985         /**
32986          * @event click
32987          * When a Brick is click
32988          * @param {Roo.bootstrap.Brick} this
32989          * @param {Roo.EventObject} e
32990          */
32991         "click" : true
32992     });
32993 };
32994
32995 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32996     
32997     /**
32998      * @cfg {String} title
32999      */   
33000     title : '',
33001     /**
33002      * @cfg {String} html
33003      */   
33004     html : '',
33005     /**
33006      * @cfg {String} bgimage
33007      */   
33008     bgimage : '',
33009     /**
33010      * @cfg {String} cls
33011      */   
33012     cls : '',
33013     /**
33014      * @cfg {String} href
33015      */   
33016     href : '',
33017     /**
33018      * @cfg {String} video
33019      */   
33020     video : '',
33021     /**
33022      * @cfg {Boolean} square
33023      */   
33024     square : true,
33025     
33026     getAutoCreate : function()
33027     {
33028         var cls = 'roo-brick';
33029         
33030         if(this.href.length){
33031             cls += ' roo-brick-link';
33032         }
33033         
33034         if(this.bgimage.length){
33035             cls += ' roo-brick-image';
33036         }
33037         
33038         if(!this.html.length && !this.bgimage.length){
33039             cls += ' roo-brick-center-title';
33040         }
33041         
33042         if(!this.html.length && this.bgimage.length){
33043             cls += ' roo-brick-bottom-title';
33044         }
33045         
33046         if(this.cls){
33047             cls += ' ' + this.cls;
33048         }
33049         
33050         var cfg = {
33051             tag: (this.href.length) ? 'a' : 'div',
33052             cls: cls,
33053             cn: [
33054                 {
33055                     tag: 'div',
33056                     cls: 'roo-brick-paragraph',
33057                     cn: []
33058                 }
33059             ]
33060         };
33061         
33062         if(this.href.length){
33063             cfg.href = this.href;
33064         }
33065         
33066         var cn = cfg.cn[0].cn;
33067         
33068         if(this.title.length){
33069             cn.push({
33070                 tag: 'h4',
33071                 cls: 'roo-brick-title',
33072                 html: this.title
33073             });
33074         }
33075         
33076         if(this.html.length){
33077             cn.push({
33078                 tag: 'p',
33079                 cls: 'roo-brick-text',
33080                 html: this.html
33081             });
33082         } else {
33083             cn.cls += ' hide';
33084         }
33085         
33086         if(this.bgimage.length){
33087             cfg.cn.push({
33088                 tag: 'img',
33089                 cls: 'roo-brick-image-view',
33090                 src: this.bgimage
33091             });
33092         }
33093         
33094         return cfg;
33095     },
33096     
33097     initEvents: function() 
33098     {
33099         if(this.title.length || this.html.length){
33100             this.el.on('mouseenter'  ,this.enter, this);
33101             this.el.on('mouseleave', this.leave, this);
33102         }
33103         
33104         Roo.EventManager.onWindowResize(this.resize, this); 
33105         
33106         if(this.bgimage.length){
33107             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33108             this.imageEl.on('load', this.onImageLoad, this);
33109             return;
33110         }
33111         
33112         this.resize();
33113     },
33114     
33115     onImageLoad : function()
33116     {
33117         this.resize();
33118     },
33119     
33120     resize : function()
33121     {
33122         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33123         
33124         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33125         
33126         if(this.bgimage.length){
33127             var image = this.el.select('.roo-brick-image-view', true).first();
33128             
33129             image.setWidth(paragraph.getWidth());
33130             
33131             if(this.square){
33132                 image.setHeight(paragraph.getWidth());
33133             }
33134             
33135             this.el.setHeight(image.getHeight());
33136             paragraph.setHeight(image.getHeight());
33137             
33138         }
33139         
33140     },
33141     
33142     enter: function(e, el)
33143     {
33144         e.preventDefault();
33145         
33146         if(this.bgimage.length){
33147             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33148             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33149         }
33150     },
33151     
33152     leave: function(e, el)
33153     {
33154         e.preventDefault();
33155         
33156         if(this.bgimage.length){
33157             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33158             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33159         }
33160     }
33161     
33162 });
33163
33164  
33165
33166  /*
33167  * - LGPL
33168  *
33169  * Number field 
33170  */
33171
33172 /**
33173  * @class Roo.bootstrap.NumberField
33174  * @extends Roo.bootstrap.Input
33175  * Bootstrap NumberField class
33176  * 
33177  * 
33178  * 
33179  * 
33180  * @constructor
33181  * Create a new NumberField
33182  * @param {Object} config The config object
33183  */
33184
33185 Roo.bootstrap.NumberField = function(config){
33186     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33187 };
33188
33189 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33190     
33191     /**
33192      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33193      */
33194     allowDecimals : true,
33195     /**
33196      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33197      */
33198     decimalSeparator : ".",
33199     /**
33200      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33201      */
33202     decimalPrecision : 2,
33203     /**
33204      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33205      */
33206     allowNegative : true,
33207     
33208     /**
33209      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33210      */
33211     allowZero: true,
33212     /**
33213      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33214      */
33215     minValue : Number.NEGATIVE_INFINITY,
33216     /**
33217      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33218      */
33219     maxValue : Number.MAX_VALUE,
33220     /**
33221      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33222      */
33223     minText : "The minimum value for this field is {0}",
33224     /**
33225      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33226      */
33227     maxText : "The maximum value for this field is {0}",
33228     /**
33229      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33230      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33231      */
33232     nanText : "{0} is not a valid number",
33233     /**
33234      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33235      */
33236     castInt : true,
33237     /**
33238      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33239      */
33240     thousandsDelimiter : false,
33241     /**
33242      * @cfg {String} valueAlign alignment of value
33243      */
33244     valueAlign : "left",
33245
33246     getAutoCreate : function()
33247     {
33248         var hiddenInput = {
33249             tag: 'input',
33250             type: 'hidden',
33251             id: Roo.id(),
33252             cls: 'hidden-number-input'
33253         };
33254         
33255         if (this.name) {
33256             hiddenInput.name = this.name;
33257         }
33258         
33259         this.name = '';
33260         
33261         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33262         
33263         this.name = hiddenInput.name;
33264         
33265         if(cfg.cn.length > 0) {
33266             cfg.cn.push(hiddenInput);
33267         }
33268         
33269         return cfg;
33270     },
33271
33272     // private
33273     initEvents : function()
33274     {   
33275         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33276         
33277         var allowed = "0123456789";
33278         
33279         if(this.allowDecimals){
33280             allowed += this.decimalSeparator;
33281         }
33282         
33283         if(this.allowNegative){
33284             allowed += "-";
33285         }
33286         
33287         if(this.thousandsDelimiter) {
33288             allowed += ",";
33289         }
33290         
33291         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33292         
33293         var keyPress = function(e){
33294             
33295             var k = e.getKey();
33296             
33297             var c = e.getCharCode();
33298             
33299             if(
33300                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33301                     allowed.indexOf(String.fromCharCode(c)) === -1
33302             ){
33303                 e.stopEvent();
33304                 return;
33305             }
33306             
33307             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33308                 return;
33309             }
33310             
33311             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33312                 e.stopEvent();
33313             }
33314         };
33315         
33316         this.el.on("keypress", keyPress, this);
33317     },
33318     
33319     validateValue : function(value)
33320     {
33321         
33322         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33323             return false;
33324         }
33325         
33326         var num = this.parseValue(value);
33327         
33328         if(isNaN(num)){
33329             this.markInvalid(String.format(this.nanText, value));
33330             return false;
33331         }
33332         
33333         if(num < this.minValue){
33334             this.markInvalid(String.format(this.minText, this.minValue));
33335             return false;
33336         }
33337         
33338         if(num > this.maxValue){
33339             this.markInvalid(String.format(this.maxText, this.maxValue));
33340             return false;
33341         }
33342         
33343         return true;
33344     },
33345
33346     getValue : function()
33347     {
33348         var v = this.hiddenEl().getValue();
33349         
33350         return this.fixPrecision(this.parseValue(v));
33351     },
33352
33353     parseValue : function(value)
33354     {
33355         if(this.thousandsDelimiter) {
33356             value += "";
33357             r = new RegExp(",", "g");
33358             value = value.replace(r, "");
33359         }
33360         
33361         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33362         return isNaN(value) ? '' : value;
33363     },
33364
33365     fixPrecision : function(value)
33366     {
33367         if(this.thousandsDelimiter) {
33368             value += "";
33369             r = new RegExp(",", "g");
33370             value = value.replace(r, "");
33371         }
33372         
33373         var nan = isNaN(value);
33374         
33375         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33376             return nan ? '' : value;
33377         }
33378         return parseFloat(value).toFixed(this.decimalPrecision);
33379     },
33380
33381     setValue : function(v)
33382     {
33383         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33384         
33385         this.value = v;
33386         
33387         if(this.rendered){
33388             
33389             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33390             
33391             this.inputEl().dom.value = (v == '') ? '' :
33392                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33393             
33394             if(!this.allowZero && v === '0') {
33395                 this.hiddenEl().dom.value = '';
33396                 this.inputEl().dom.value = '';
33397             }
33398             
33399             this.validate();
33400         }
33401     },
33402
33403     decimalPrecisionFcn : function(v)
33404     {
33405         return Math.floor(v);
33406     },
33407
33408     beforeBlur : function()
33409     {
33410         if(!this.castInt){
33411             return;
33412         }
33413         
33414         var v = this.parseValue(this.getRawValue());
33415         
33416         if(v || v === 0){
33417             this.setValue(v);
33418         }
33419     },
33420     
33421     hiddenEl : function()
33422     {
33423         return this.el.select('input.hidden-number-input',true).first();
33424     }
33425     
33426 });
33427
33428  
33429
33430 /*
33431 * Licence: LGPL
33432 */
33433
33434 /**
33435  * @class Roo.bootstrap.DocumentSlider
33436  * @extends Roo.bootstrap.Component
33437  * Bootstrap DocumentSlider class
33438  * 
33439  * @constructor
33440  * Create a new DocumentViewer
33441  * @param {Object} config The config object
33442  */
33443
33444 Roo.bootstrap.DocumentSlider = function(config){
33445     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33446     
33447     this.files = [];
33448     
33449     this.addEvents({
33450         /**
33451          * @event initial
33452          * Fire after initEvent
33453          * @param {Roo.bootstrap.DocumentSlider} this
33454          */
33455         "initial" : true,
33456         /**
33457          * @event update
33458          * Fire after update
33459          * @param {Roo.bootstrap.DocumentSlider} this
33460          */
33461         "update" : true,
33462         /**
33463          * @event click
33464          * Fire after click
33465          * @param {Roo.bootstrap.DocumentSlider} this
33466          */
33467         "click" : true
33468     });
33469 };
33470
33471 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33472     
33473     files : false,
33474     
33475     indicator : 0,
33476     
33477     getAutoCreate : function()
33478     {
33479         var cfg = {
33480             tag : 'div',
33481             cls : 'roo-document-slider',
33482             cn : [
33483                 {
33484                     tag : 'div',
33485                     cls : 'roo-document-slider-header',
33486                     cn : [
33487                         {
33488                             tag : 'div',
33489                             cls : 'roo-document-slider-header-title'
33490                         }
33491                     ]
33492                 },
33493                 {
33494                     tag : 'div',
33495                     cls : 'roo-document-slider-body',
33496                     cn : [
33497                         {
33498                             tag : 'div',
33499                             cls : 'roo-document-slider-prev',
33500                             cn : [
33501                                 {
33502                                     tag : 'i',
33503                                     cls : 'fa fa-chevron-left'
33504                                 }
33505                             ]
33506                         },
33507                         {
33508                             tag : 'div',
33509                             cls : 'roo-document-slider-thumb',
33510                             cn : [
33511                                 {
33512                                     tag : 'img',
33513                                     cls : 'roo-document-slider-image'
33514                                 }
33515                             ]
33516                         },
33517                         {
33518                             tag : 'div',
33519                             cls : 'roo-document-slider-next',
33520                             cn : [
33521                                 {
33522                                     tag : 'i',
33523                                     cls : 'fa fa-chevron-right'
33524                                 }
33525                             ]
33526                         }
33527                     ]
33528                 }
33529             ]
33530         };
33531         
33532         return cfg;
33533     },
33534     
33535     initEvents : function()
33536     {
33537         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33538         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33539         
33540         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33541         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33542         
33543         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33544         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33545         
33546         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33547         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33548         
33549         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33550         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33551         
33552         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33553         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33554         
33555         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33556         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33557         
33558         this.thumbEl.on('click', this.onClick, this);
33559         
33560         this.prevIndicator.on('click', this.prev, this);
33561         
33562         this.nextIndicator.on('click', this.next, this);
33563         
33564     },
33565     
33566     initial : function()
33567     {
33568         if(this.files.length){
33569             this.indicator = 1;
33570             this.update()
33571         }
33572         
33573         this.fireEvent('initial', this);
33574     },
33575     
33576     update : function()
33577     {
33578         this.imageEl.attr('src', this.files[this.indicator - 1]);
33579         
33580         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33581         
33582         this.prevIndicator.show();
33583         
33584         if(this.indicator == 1){
33585             this.prevIndicator.hide();
33586         }
33587         
33588         this.nextIndicator.show();
33589         
33590         if(this.indicator == this.files.length){
33591             this.nextIndicator.hide();
33592         }
33593         
33594         this.thumbEl.scrollTo('top');
33595         
33596         this.fireEvent('update', this);
33597     },
33598     
33599     onClick : function(e)
33600     {
33601         e.preventDefault();
33602         
33603         this.fireEvent('click', this);
33604     },
33605     
33606     prev : function(e)
33607     {
33608         e.preventDefault();
33609         
33610         this.indicator = Math.max(1, this.indicator - 1);
33611         
33612         this.update();
33613     },
33614     
33615     next : function(e)
33616     {
33617         e.preventDefault();
33618         
33619         this.indicator = Math.min(this.files.length, this.indicator + 1);
33620         
33621         this.update();
33622     }
33623 });
33624 /*
33625  * - LGPL
33626  *
33627  * RadioSet
33628  *
33629  *
33630  */
33631
33632 /**
33633  * @class Roo.bootstrap.RadioSet
33634  * @extends Roo.bootstrap.Input
33635  * Bootstrap RadioSet class
33636  * @cfg {String} indicatorpos (left|right) default left
33637  * @cfg {Boolean} inline (true|false) inline the element (default true)
33638  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33639  * @constructor
33640  * Create a new RadioSet
33641  * @param {Object} config The config object
33642  */
33643
33644 Roo.bootstrap.RadioSet = function(config){
33645     
33646     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33647     
33648     this.radioes = [];
33649     
33650     Roo.bootstrap.RadioSet.register(this);
33651     
33652     this.addEvents({
33653         /**
33654         * @event check
33655         * Fires when the element is checked or unchecked.
33656         * @param {Roo.bootstrap.RadioSet} this This radio
33657         * @param {Roo.bootstrap.Radio} item The checked item
33658         */
33659        check : true,
33660        /**
33661         * @event click
33662         * Fires when the element is click.
33663         * @param {Roo.bootstrap.RadioSet} this This radio set
33664         * @param {Roo.bootstrap.Radio} item The checked item
33665         * @param {Roo.EventObject} e The event object
33666         */
33667        click : true
33668     });
33669     
33670 };
33671
33672 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33673
33674     radioes : false,
33675     
33676     inline : true,
33677     
33678     weight : '',
33679     
33680     indicatorpos : 'left',
33681     
33682     getAutoCreate : function()
33683     {
33684         var label = {
33685             tag : 'label',
33686             cls : 'roo-radio-set-label',
33687             cn : [
33688                 {
33689                     tag : 'span',
33690                     html : this.fieldLabel
33691                 }
33692             ]
33693         };
33694         
33695         if(this.indicatorpos == 'left'){
33696             label.cn.unshift({
33697                 tag : 'i',
33698                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33699                 tooltip : 'This field is required'
33700             });
33701         } else {
33702             label.cn.push({
33703                 tag : 'i',
33704                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33705                 tooltip : 'This field is required'
33706             });
33707         }
33708         
33709         var items = {
33710             tag : 'div',
33711             cls : 'roo-radio-set-items'
33712         };
33713         
33714         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33715         
33716         if (align === 'left' && this.fieldLabel.length) {
33717             
33718             items = {
33719                 cls : "roo-radio-set-right", 
33720                 cn: [
33721                     items
33722                 ]
33723             };
33724             
33725             if(this.labelWidth > 12){
33726                 label.style = "width: " + this.labelWidth + 'px';
33727             }
33728             
33729             if(this.labelWidth < 13 && this.labelmd == 0){
33730                 this.labelmd = this.labelWidth;
33731             }
33732             
33733             if(this.labellg > 0){
33734                 label.cls += ' col-lg-' + this.labellg;
33735                 items.cls += ' col-lg-' + (12 - this.labellg);
33736             }
33737             
33738             if(this.labelmd > 0){
33739                 label.cls += ' col-md-' + this.labelmd;
33740                 items.cls += ' col-md-' + (12 - this.labelmd);
33741             }
33742             
33743             if(this.labelsm > 0){
33744                 label.cls += ' col-sm-' + this.labelsm;
33745                 items.cls += ' col-sm-' + (12 - this.labelsm);
33746             }
33747             
33748             if(this.labelxs > 0){
33749                 label.cls += ' col-xs-' + this.labelxs;
33750                 items.cls += ' col-xs-' + (12 - this.labelxs);
33751             }
33752         }
33753         
33754         var cfg = {
33755             tag : 'div',
33756             cls : 'roo-radio-set',
33757             cn : [
33758                 {
33759                     tag : 'input',
33760                     cls : 'roo-radio-set-input',
33761                     type : 'hidden',
33762                     name : this.name,
33763                     value : this.value ? this.value :  ''
33764                 },
33765                 label,
33766                 items
33767             ]
33768         };
33769         
33770         if(this.weight.length){
33771             cfg.cls += ' roo-radio-' + this.weight;
33772         }
33773         
33774         if(this.inline) {
33775             cfg.cls += ' roo-radio-set-inline';
33776         }
33777         
33778         var settings=this;
33779         ['xs','sm','md','lg'].map(function(size){
33780             if (settings[size]) {
33781                 cfg.cls += ' col-' + size + '-' + settings[size];
33782             }
33783         });
33784         
33785         return cfg;
33786         
33787     },
33788
33789     initEvents : function()
33790     {
33791         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33792         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33793         
33794         if(!this.fieldLabel.length){
33795             this.labelEl.hide();
33796         }
33797         
33798         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33799         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33800         
33801         this.indicator = this.indicatorEl();
33802         
33803         if(this.indicator){
33804             this.indicator.addClass('invisible');
33805         }
33806         
33807         this.originalValue = this.getValue();
33808         
33809     },
33810     
33811     inputEl: function ()
33812     {
33813         return this.el.select('.roo-radio-set-input', true).first();
33814     },
33815     
33816     getChildContainer : function()
33817     {
33818         return this.itemsEl;
33819     },
33820     
33821     register : function(item)
33822     {
33823         this.radioes.push(item);
33824         
33825     },
33826     
33827     validate : function()
33828     {   
33829         if(this.getVisibilityEl().hasClass('hidden')){
33830             return true;
33831         }
33832         
33833         var valid = false;
33834         
33835         Roo.each(this.radioes, function(i){
33836             if(!i.checked){
33837                 return;
33838             }
33839             
33840             valid = true;
33841             return false;
33842         });
33843         
33844         if(this.allowBlank) {
33845             return true;
33846         }
33847         
33848         if(this.disabled || valid){
33849             this.markValid();
33850             return true;
33851         }
33852         
33853         this.markInvalid();
33854         return false;
33855         
33856     },
33857     
33858     markValid : function()
33859     {
33860         if(this.labelEl.isVisible(true)){
33861             this.indicatorEl().removeClass('visible');
33862             this.indicatorEl().addClass('invisible');
33863         }
33864         
33865         this.el.removeClass([this.invalidClass, this.validClass]);
33866         this.el.addClass(this.validClass);
33867         
33868         this.fireEvent('valid', this);
33869     },
33870     
33871     markInvalid : function(msg)
33872     {
33873         if(this.allowBlank || this.disabled){
33874             return;
33875         }
33876         
33877         if(this.labelEl.isVisible(true)){
33878             this.indicatorEl().removeClass('invisible');
33879             this.indicatorEl().addClass('visible');
33880         }
33881         
33882         this.el.removeClass([this.invalidClass, this.validClass]);
33883         this.el.addClass(this.invalidClass);
33884         
33885         this.fireEvent('invalid', this, msg);
33886         
33887     },
33888     
33889     setValue : function(v, suppressEvent)
33890     {   
33891         if(this.value === v){
33892             return;
33893         }
33894         
33895         this.value = v;
33896         
33897         if(this.rendered){
33898             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33899         }
33900         
33901         Roo.each(this.radioes, function(i){
33902             i.checked = false;
33903             i.el.removeClass('checked');
33904         });
33905         
33906         Roo.each(this.radioes, function(i){
33907             
33908             if(i.value === v || i.value.toString() === v.toString()){
33909                 i.checked = true;
33910                 i.el.addClass('checked');
33911                 
33912                 if(suppressEvent !== true){
33913                     this.fireEvent('check', this, i);
33914                 }
33915                 
33916                 return false;
33917             }
33918             
33919         }, this);
33920         
33921         this.validate();
33922     },
33923     
33924     clearInvalid : function(){
33925         
33926         if(!this.el || this.preventMark){
33927             return;
33928         }
33929         
33930         this.el.removeClass([this.invalidClass]);
33931         
33932         this.fireEvent('valid', this);
33933     }
33934     
33935 });
33936
33937 Roo.apply(Roo.bootstrap.RadioSet, {
33938     
33939     groups: {},
33940     
33941     register : function(set)
33942     {
33943         this.groups[set.name] = set;
33944     },
33945     
33946     get: function(name) 
33947     {
33948         if (typeof(this.groups[name]) == 'undefined') {
33949             return false;
33950         }
33951         
33952         return this.groups[name] ;
33953     }
33954     
33955 });
33956 /*
33957  * Based on:
33958  * Ext JS Library 1.1.1
33959  * Copyright(c) 2006-2007, Ext JS, LLC.
33960  *
33961  * Originally Released Under LGPL - original licence link has changed is not relivant.
33962  *
33963  * Fork - LGPL
33964  * <script type="text/javascript">
33965  */
33966
33967
33968 /**
33969  * @class Roo.bootstrap.SplitBar
33970  * @extends Roo.util.Observable
33971  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33972  * <br><br>
33973  * Usage:
33974  * <pre><code>
33975 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33976                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33977 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33978 split.minSize = 100;
33979 split.maxSize = 600;
33980 split.animate = true;
33981 split.on('moved', splitterMoved);
33982 </code></pre>
33983  * @constructor
33984  * Create a new SplitBar
33985  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33986  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33987  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33988  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33989                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33990                         position of the SplitBar).
33991  */
33992 Roo.bootstrap.SplitBar = function(cfg){
33993     
33994     /** @private */
33995     
33996     //{
33997     //  dragElement : elm
33998     //  resizingElement: el,
33999         // optional..
34000     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34001     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34002         // existingProxy ???
34003     //}
34004     
34005     this.el = Roo.get(cfg.dragElement, true);
34006     this.el.dom.unselectable = "on";
34007     /** @private */
34008     this.resizingEl = Roo.get(cfg.resizingElement, true);
34009
34010     /**
34011      * @private
34012      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34013      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34014      * @type Number
34015      */
34016     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34017     
34018     /**
34019      * The minimum size of the resizing element. (Defaults to 0)
34020      * @type Number
34021      */
34022     this.minSize = 0;
34023     
34024     /**
34025      * The maximum size of the resizing element. (Defaults to 2000)
34026      * @type Number
34027      */
34028     this.maxSize = 2000;
34029     
34030     /**
34031      * Whether to animate the transition to the new size
34032      * @type Boolean
34033      */
34034     this.animate = false;
34035     
34036     /**
34037      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34038      * @type Boolean
34039      */
34040     this.useShim = false;
34041     
34042     /** @private */
34043     this.shim = null;
34044     
34045     if(!cfg.existingProxy){
34046         /** @private */
34047         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34048     }else{
34049         this.proxy = Roo.get(cfg.existingProxy).dom;
34050     }
34051     /** @private */
34052     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34053     
34054     /** @private */
34055     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34056     
34057     /** @private */
34058     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34059     
34060     /** @private */
34061     this.dragSpecs = {};
34062     
34063     /**
34064      * @private The adapter to use to positon and resize elements
34065      */
34066     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34067     this.adapter.init(this);
34068     
34069     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34070         /** @private */
34071         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34072         this.el.addClass("roo-splitbar-h");
34073     }else{
34074         /** @private */
34075         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34076         this.el.addClass("roo-splitbar-v");
34077     }
34078     
34079     this.addEvents({
34080         /**
34081          * @event resize
34082          * Fires when the splitter is moved (alias for {@link #event-moved})
34083          * @param {Roo.bootstrap.SplitBar} this
34084          * @param {Number} newSize the new width or height
34085          */
34086         "resize" : true,
34087         /**
34088          * @event moved
34089          * Fires when the splitter is moved
34090          * @param {Roo.bootstrap.SplitBar} this
34091          * @param {Number} newSize the new width or height
34092          */
34093         "moved" : true,
34094         /**
34095          * @event beforeresize
34096          * Fires before the splitter is dragged
34097          * @param {Roo.bootstrap.SplitBar} this
34098          */
34099         "beforeresize" : true,
34100
34101         "beforeapply" : true
34102     });
34103
34104     Roo.util.Observable.call(this);
34105 };
34106
34107 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34108     onStartProxyDrag : function(x, y){
34109         this.fireEvent("beforeresize", this);
34110         if(!this.overlay){
34111             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34112             o.unselectable();
34113             o.enableDisplayMode("block");
34114             // all splitbars share the same overlay
34115             Roo.bootstrap.SplitBar.prototype.overlay = o;
34116         }
34117         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34118         this.overlay.show();
34119         Roo.get(this.proxy).setDisplayed("block");
34120         var size = this.adapter.getElementSize(this);
34121         this.activeMinSize = this.getMinimumSize();;
34122         this.activeMaxSize = this.getMaximumSize();;
34123         var c1 = size - this.activeMinSize;
34124         var c2 = Math.max(this.activeMaxSize - size, 0);
34125         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34126             this.dd.resetConstraints();
34127             this.dd.setXConstraint(
34128                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34129                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34130             );
34131             this.dd.setYConstraint(0, 0);
34132         }else{
34133             this.dd.resetConstraints();
34134             this.dd.setXConstraint(0, 0);
34135             this.dd.setYConstraint(
34136                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34137                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34138             );
34139          }
34140         this.dragSpecs.startSize = size;
34141         this.dragSpecs.startPoint = [x, y];
34142         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34143     },
34144     
34145     /** 
34146      * @private Called after the drag operation by the DDProxy
34147      */
34148     onEndProxyDrag : function(e){
34149         Roo.get(this.proxy).setDisplayed(false);
34150         var endPoint = Roo.lib.Event.getXY(e);
34151         if(this.overlay){
34152             this.overlay.hide();
34153         }
34154         var newSize;
34155         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34156             newSize = this.dragSpecs.startSize + 
34157                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34158                     endPoint[0] - this.dragSpecs.startPoint[0] :
34159                     this.dragSpecs.startPoint[0] - endPoint[0]
34160                 );
34161         }else{
34162             newSize = this.dragSpecs.startSize + 
34163                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34164                     endPoint[1] - this.dragSpecs.startPoint[1] :
34165                     this.dragSpecs.startPoint[1] - endPoint[1]
34166                 );
34167         }
34168         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34169         if(newSize != this.dragSpecs.startSize){
34170             if(this.fireEvent('beforeapply', this, newSize) !== false){
34171                 this.adapter.setElementSize(this, newSize);
34172                 this.fireEvent("moved", this, newSize);
34173                 this.fireEvent("resize", this, newSize);
34174             }
34175         }
34176     },
34177     
34178     /**
34179      * Get the adapter this SplitBar uses
34180      * @return The adapter object
34181      */
34182     getAdapter : function(){
34183         return this.adapter;
34184     },
34185     
34186     /**
34187      * Set the adapter this SplitBar uses
34188      * @param {Object} adapter A SplitBar adapter object
34189      */
34190     setAdapter : function(adapter){
34191         this.adapter = adapter;
34192         this.adapter.init(this);
34193     },
34194     
34195     /**
34196      * Gets the minimum size for the resizing element
34197      * @return {Number} The minimum size
34198      */
34199     getMinimumSize : function(){
34200         return this.minSize;
34201     },
34202     
34203     /**
34204      * Sets the minimum size for the resizing element
34205      * @param {Number} minSize The minimum size
34206      */
34207     setMinimumSize : function(minSize){
34208         this.minSize = minSize;
34209     },
34210     
34211     /**
34212      * Gets the maximum size for the resizing element
34213      * @return {Number} The maximum size
34214      */
34215     getMaximumSize : function(){
34216         return this.maxSize;
34217     },
34218     
34219     /**
34220      * Sets the maximum size for the resizing element
34221      * @param {Number} maxSize The maximum size
34222      */
34223     setMaximumSize : function(maxSize){
34224         this.maxSize = maxSize;
34225     },
34226     
34227     /**
34228      * Sets the initialize size for the resizing element
34229      * @param {Number} size The initial size
34230      */
34231     setCurrentSize : function(size){
34232         var oldAnimate = this.animate;
34233         this.animate = false;
34234         this.adapter.setElementSize(this, size);
34235         this.animate = oldAnimate;
34236     },
34237     
34238     /**
34239      * Destroy this splitbar. 
34240      * @param {Boolean} removeEl True to remove the element
34241      */
34242     destroy : function(removeEl){
34243         if(this.shim){
34244             this.shim.remove();
34245         }
34246         this.dd.unreg();
34247         this.proxy.parentNode.removeChild(this.proxy);
34248         if(removeEl){
34249             this.el.remove();
34250         }
34251     }
34252 });
34253
34254 /**
34255  * @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.
34256  */
34257 Roo.bootstrap.SplitBar.createProxy = function(dir){
34258     var proxy = new Roo.Element(document.createElement("div"));
34259     proxy.unselectable();
34260     var cls = 'roo-splitbar-proxy';
34261     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34262     document.body.appendChild(proxy.dom);
34263     return proxy.dom;
34264 };
34265
34266 /** 
34267  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34268  * Default Adapter. It assumes the splitter and resizing element are not positioned
34269  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34270  */
34271 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34272 };
34273
34274 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34275     // do nothing for now
34276     init : function(s){
34277     
34278     },
34279     /**
34280      * Called before drag operations to get the current size of the resizing element. 
34281      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34282      */
34283      getElementSize : function(s){
34284         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34285             return s.resizingEl.getWidth();
34286         }else{
34287             return s.resizingEl.getHeight();
34288         }
34289     },
34290     
34291     /**
34292      * Called after drag operations to set the size of the resizing element.
34293      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34294      * @param {Number} newSize The new size to set
34295      * @param {Function} onComplete A function to be invoked when resizing is complete
34296      */
34297     setElementSize : function(s, newSize, onComplete){
34298         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34299             if(!s.animate){
34300                 s.resizingEl.setWidth(newSize);
34301                 if(onComplete){
34302                     onComplete(s, newSize);
34303                 }
34304             }else{
34305                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34306             }
34307         }else{
34308             
34309             if(!s.animate){
34310                 s.resizingEl.setHeight(newSize);
34311                 if(onComplete){
34312                     onComplete(s, newSize);
34313                 }
34314             }else{
34315                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34316             }
34317         }
34318     }
34319 };
34320
34321 /** 
34322  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34323  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34324  * Adapter that  moves the splitter element to align with the resized sizing element. 
34325  * Used with an absolute positioned SplitBar.
34326  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34327  * document.body, make sure you assign an id to the body element.
34328  */
34329 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34330     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34331     this.container = Roo.get(container);
34332 };
34333
34334 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34335     init : function(s){
34336         this.basic.init(s);
34337     },
34338     
34339     getElementSize : function(s){
34340         return this.basic.getElementSize(s);
34341     },
34342     
34343     setElementSize : function(s, newSize, onComplete){
34344         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34345     },
34346     
34347     moveSplitter : function(s){
34348         var yes = Roo.bootstrap.SplitBar;
34349         switch(s.placement){
34350             case yes.LEFT:
34351                 s.el.setX(s.resizingEl.getRight());
34352                 break;
34353             case yes.RIGHT:
34354                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34355                 break;
34356             case yes.TOP:
34357                 s.el.setY(s.resizingEl.getBottom());
34358                 break;
34359             case yes.BOTTOM:
34360                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34361                 break;
34362         }
34363     }
34364 };
34365
34366 /**
34367  * Orientation constant - Create a vertical SplitBar
34368  * @static
34369  * @type Number
34370  */
34371 Roo.bootstrap.SplitBar.VERTICAL = 1;
34372
34373 /**
34374  * Orientation constant - Create a horizontal SplitBar
34375  * @static
34376  * @type Number
34377  */
34378 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34379
34380 /**
34381  * Placement constant - The resizing element is to the left of the splitter element
34382  * @static
34383  * @type Number
34384  */
34385 Roo.bootstrap.SplitBar.LEFT = 1;
34386
34387 /**
34388  * Placement constant - The resizing element is to the right of the splitter element
34389  * @static
34390  * @type Number
34391  */
34392 Roo.bootstrap.SplitBar.RIGHT = 2;
34393
34394 /**
34395  * Placement constant - The resizing element is positioned above the splitter element
34396  * @static
34397  * @type Number
34398  */
34399 Roo.bootstrap.SplitBar.TOP = 3;
34400
34401 /**
34402  * Placement constant - The resizing element is positioned under splitter element
34403  * @static
34404  * @type Number
34405  */
34406 Roo.bootstrap.SplitBar.BOTTOM = 4;
34407 Roo.namespace("Roo.bootstrap.layout");/*
34408  * Based on:
34409  * Ext JS Library 1.1.1
34410  * Copyright(c) 2006-2007, Ext JS, LLC.
34411  *
34412  * Originally Released Under LGPL - original licence link has changed is not relivant.
34413  *
34414  * Fork - LGPL
34415  * <script type="text/javascript">
34416  */
34417
34418 /**
34419  * @class Roo.bootstrap.layout.Manager
34420  * @extends Roo.bootstrap.Component
34421  * Base class for layout managers.
34422  */
34423 Roo.bootstrap.layout.Manager = function(config)
34424 {
34425     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34426
34427
34428
34429
34430
34431     /** false to disable window resize monitoring @type Boolean */
34432     this.monitorWindowResize = true;
34433     this.regions = {};
34434     this.addEvents({
34435         /**
34436          * @event layout
34437          * Fires when a layout is performed.
34438          * @param {Roo.LayoutManager} this
34439          */
34440         "layout" : true,
34441         /**
34442          * @event regionresized
34443          * Fires when the user resizes a region.
34444          * @param {Roo.LayoutRegion} region The resized region
34445          * @param {Number} newSize The new size (width for east/west, height for north/south)
34446          */
34447         "regionresized" : true,
34448         /**
34449          * @event regioncollapsed
34450          * Fires when a region is collapsed.
34451          * @param {Roo.LayoutRegion} region The collapsed region
34452          */
34453         "regioncollapsed" : true,
34454         /**
34455          * @event regionexpanded
34456          * Fires when a region is expanded.
34457          * @param {Roo.LayoutRegion} region The expanded region
34458          */
34459         "regionexpanded" : true
34460     });
34461     this.updating = false;
34462
34463     if (config.el) {
34464         this.el = Roo.get(config.el);
34465         this.initEvents();
34466     }
34467
34468 };
34469
34470 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34471
34472
34473     regions : null,
34474
34475     monitorWindowResize : true,
34476
34477
34478     updating : false,
34479
34480
34481     onRender : function(ct, position)
34482     {
34483         if(!this.el){
34484             this.el = Roo.get(ct);
34485             this.initEvents();
34486         }
34487         //this.fireEvent('render',this);
34488     },
34489
34490
34491     initEvents: function()
34492     {
34493
34494
34495         // ie scrollbar fix
34496         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34497             document.body.scroll = "no";
34498         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34499             this.el.position('relative');
34500         }
34501         this.id = this.el.id;
34502         this.el.addClass("roo-layout-container");
34503         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34504         if(this.el.dom != document.body ) {
34505             this.el.on('resize', this.layout,this);
34506             this.el.on('show', this.layout,this);
34507         }
34508
34509     },
34510
34511     /**
34512      * Returns true if this layout is currently being updated
34513      * @return {Boolean}
34514      */
34515     isUpdating : function(){
34516         return this.updating;
34517     },
34518
34519     /**
34520      * Suspend the LayoutManager from doing auto-layouts while
34521      * making multiple add or remove calls
34522      */
34523     beginUpdate : function(){
34524         this.updating = true;
34525     },
34526
34527     /**
34528      * Restore auto-layouts and optionally disable the manager from performing a layout
34529      * @param {Boolean} noLayout true to disable a layout update
34530      */
34531     endUpdate : function(noLayout){
34532         this.updating = false;
34533         if(!noLayout){
34534             this.layout();
34535         }
34536     },
34537
34538     layout: function(){
34539         // abstract...
34540     },
34541
34542     onRegionResized : function(region, newSize){
34543         this.fireEvent("regionresized", region, newSize);
34544         this.layout();
34545     },
34546
34547     onRegionCollapsed : function(region){
34548         this.fireEvent("regioncollapsed", region);
34549     },
34550
34551     onRegionExpanded : function(region){
34552         this.fireEvent("regionexpanded", region);
34553     },
34554
34555     /**
34556      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34557      * performs box-model adjustments.
34558      * @return {Object} The size as an object {width: (the width), height: (the height)}
34559      */
34560     getViewSize : function()
34561     {
34562         var size;
34563         if(this.el.dom != document.body){
34564             size = this.el.getSize();
34565         }else{
34566             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34567         }
34568         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34569         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34570         return size;
34571     },
34572
34573     /**
34574      * Returns the Element this layout is bound to.
34575      * @return {Roo.Element}
34576      */
34577     getEl : function(){
34578         return this.el;
34579     },
34580
34581     /**
34582      * Returns the specified region.
34583      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34584      * @return {Roo.LayoutRegion}
34585      */
34586     getRegion : function(target){
34587         return this.regions[target.toLowerCase()];
34588     },
34589
34590     onWindowResize : function(){
34591         if(this.monitorWindowResize){
34592             this.layout();
34593         }
34594     }
34595 });
34596 /*
34597  * Based on:
34598  * Ext JS Library 1.1.1
34599  * Copyright(c) 2006-2007, Ext JS, LLC.
34600  *
34601  * Originally Released Under LGPL - original licence link has changed is not relivant.
34602  *
34603  * Fork - LGPL
34604  * <script type="text/javascript">
34605  */
34606 /**
34607  * @class Roo.bootstrap.layout.Border
34608  * @extends Roo.bootstrap.layout.Manager
34609  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34610  * please see: examples/bootstrap/nested.html<br><br>
34611  
34612 <b>The container the layout is rendered into can be either the body element or any other element.
34613 If it is not the body element, the container needs to either be an absolute positioned element,
34614 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34615 the container size if it is not the body element.</b>
34616
34617 * @constructor
34618 * Create a new Border
34619 * @param {Object} config Configuration options
34620  */
34621 Roo.bootstrap.layout.Border = function(config){
34622     config = config || {};
34623     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34624     
34625     
34626     
34627     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34628         if(config[region]){
34629             config[region].region = region;
34630             this.addRegion(config[region]);
34631         }
34632     },this);
34633     
34634 };
34635
34636 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34637
34638 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34639     /**
34640      * Creates and adds a new region if it doesn't already exist.
34641      * @param {String} target The target region key (north, south, east, west or center).
34642      * @param {Object} config The regions config object
34643      * @return {BorderLayoutRegion} The new region
34644      */
34645     addRegion : function(config)
34646     {
34647         if(!this.regions[config.region]){
34648             var r = this.factory(config);
34649             this.bindRegion(r);
34650         }
34651         return this.regions[config.region];
34652     },
34653
34654     // private (kinda)
34655     bindRegion : function(r){
34656         this.regions[r.config.region] = r;
34657         
34658         r.on("visibilitychange",    this.layout, this);
34659         r.on("paneladded",          this.layout, this);
34660         r.on("panelremoved",        this.layout, this);
34661         r.on("invalidated",         this.layout, this);
34662         r.on("resized",             this.onRegionResized, this);
34663         r.on("collapsed",           this.onRegionCollapsed, this);
34664         r.on("expanded",            this.onRegionExpanded, this);
34665     },
34666
34667     /**
34668      * Performs a layout update.
34669      */
34670     layout : function()
34671     {
34672         if(this.updating) {
34673             return;
34674         }
34675         
34676         // render all the rebions if they have not been done alreayd?
34677         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34678             if(this.regions[region] && !this.regions[region].bodyEl){
34679                 this.regions[region].onRender(this.el)
34680             }
34681         },this);
34682         
34683         var size = this.getViewSize();
34684         var w = size.width;
34685         var h = size.height;
34686         var centerW = w;
34687         var centerH = h;
34688         var centerY = 0;
34689         var centerX = 0;
34690         //var x = 0, y = 0;
34691
34692         var rs = this.regions;
34693         var north = rs["north"];
34694         var south = rs["south"]; 
34695         var west = rs["west"];
34696         var east = rs["east"];
34697         var center = rs["center"];
34698         //if(this.hideOnLayout){ // not supported anymore
34699             //c.el.setStyle("display", "none");
34700         //}
34701         if(north && north.isVisible()){
34702             var b = north.getBox();
34703             var m = north.getMargins();
34704             b.width = w - (m.left+m.right);
34705             b.x = m.left;
34706             b.y = m.top;
34707             centerY = b.height + b.y + m.bottom;
34708             centerH -= centerY;
34709             north.updateBox(this.safeBox(b));
34710         }
34711         if(south && south.isVisible()){
34712             var b = south.getBox();
34713             var m = south.getMargins();
34714             b.width = w - (m.left+m.right);
34715             b.x = m.left;
34716             var totalHeight = (b.height + m.top + m.bottom);
34717             b.y = h - totalHeight + m.top;
34718             centerH -= totalHeight;
34719             south.updateBox(this.safeBox(b));
34720         }
34721         if(west && west.isVisible()){
34722             var b = west.getBox();
34723             var m = west.getMargins();
34724             b.height = centerH - (m.top+m.bottom);
34725             b.x = m.left;
34726             b.y = centerY + m.top;
34727             var totalWidth = (b.width + m.left + m.right);
34728             centerX += totalWidth;
34729             centerW -= totalWidth;
34730             west.updateBox(this.safeBox(b));
34731         }
34732         if(east && east.isVisible()){
34733             var b = east.getBox();
34734             var m = east.getMargins();
34735             b.height = centerH - (m.top+m.bottom);
34736             var totalWidth = (b.width + m.left + m.right);
34737             b.x = w - totalWidth + m.left;
34738             b.y = centerY + m.top;
34739             centerW -= totalWidth;
34740             east.updateBox(this.safeBox(b));
34741         }
34742         if(center){
34743             var m = center.getMargins();
34744             var centerBox = {
34745                 x: centerX + m.left,
34746                 y: centerY + m.top,
34747                 width: centerW - (m.left+m.right),
34748                 height: centerH - (m.top+m.bottom)
34749             };
34750             //if(this.hideOnLayout){
34751                 //center.el.setStyle("display", "block");
34752             //}
34753             center.updateBox(this.safeBox(centerBox));
34754         }
34755         this.el.repaint();
34756         this.fireEvent("layout", this);
34757     },
34758
34759     // private
34760     safeBox : function(box){
34761         box.width = Math.max(0, box.width);
34762         box.height = Math.max(0, box.height);
34763         return box;
34764     },
34765
34766     /**
34767      * Adds a ContentPanel (or subclass) to this layout.
34768      * @param {String} target The target region key (north, south, east, west or center).
34769      * @param {Roo.ContentPanel} panel The panel to add
34770      * @return {Roo.ContentPanel} The added panel
34771      */
34772     add : function(target, panel){
34773          
34774         target = target.toLowerCase();
34775         return this.regions[target].add(panel);
34776     },
34777
34778     /**
34779      * Remove a ContentPanel (or subclass) to this layout.
34780      * @param {String} target The target region key (north, south, east, west or center).
34781      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34782      * @return {Roo.ContentPanel} The removed panel
34783      */
34784     remove : function(target, panel){
34785         target = target.toLowerCase();
34786         return this.regions[target].remove(panel);
34787     },
34788
34789     /**
34790      * Searches all regions for a panel with the specified id
34791      * @param {String} panelId
34792      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34793      */
34794     findPanel : function(panelId){
34795         var rs = this.regions;
34796         for(var target in rs){
34797             if(typeof rs[target] != "function"){
34798                 var p = rs[target].getPanel(panelId);
34799                 if(p){
34800                     return p;
34801                 }
34802             }
34803         }
34804         return null;
34805     },
34806
34807     /**
34808      * Searches all regions for a panel with the specified id and activates (shows) it.
34809      * @param {String/ContentPanel} panelId The panels id or the panel itself
34810      * @return {Roo.ContentPanel} The shown panel or null
34811      */
34812     showPanel : function(panelId) {
34813       var rs = this.regions;
34814       for(var target in rs){
34815          var r = rs[target];
34816          if(typeof r != "function"){
34817             if(r.hasPanel(panelId)){
34818                return r.showPanel(panelId);
34819             }
34820          }
34821       }
34822       return null;
34823    },
34824
34825    /**
34826      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34827      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34828      */
34829    /*
34830     restoreState : function(provider){
34831         if(!provider){
34832             provider = Roo.state.Manager;
34833         }
34834         var sm = new Roo.LayoutStateManager();
34835         sm.init(this, provider);
34836     },
34837 */
34838  
34839  
34840     /**
34841      * Adds a xtype elements to the layout.
34842      * <pre><code>
34843
34844 layout.addxtype({
34845        xtype : 'ContentPanel',
34846        region: 'west',
34847        items: [ .... ]
34848    }
34849 );
34850
34851 layout.addxtype({
34852         xtype : 'NestedLayoutPanel',
34853         region: 'west',
34854         layout: {
34855            center: { },
34856            west: { }   
34857         },
34858         items : [ ... list of content panels or nested layout panels.. ]
34859    }
34860 );
34861 </code></pre>
34862      * @param {Object} cfg Xtype definition of item to add.
34863      */
34864     addxtype : function(cfg)
34865     {
34866         // basically accepts a pannel...
34867         // can accept a layout region..!?!?
34868         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34869         
34870         
34871         // theory?  children can only be panels??
34872         
34873         //if (!cfg.xtype.match(/Panel$/)) {
34874         //    return false;
34875         //}
34876         var ret = false;
34877         
34878         if (typeof(cfg.region) == 'undefined') {
34879             Roo.log("Failed to add Panel, region was not set");
34880             Roo.log(cfg);
34881             return false;
34882         }
34883         var region = cfg.region;
34884         delete cfg.region;
34885         
34886           
34887         var xitems = [];
34888         if (cfg.items) {
34889             xitems = cfg.items;
34890             delete cfg.items;
34891         }
34892         var nb = false;
34893         
34894         switch(cfg.xtype) 
34895         {
34896             case 'Content':  // ContentPanel (el, cfg)
34897             case 'Scroll':  // ContentPanel (el, cfg)
34898             case 'View': 
34899                 cfg.autoCreate = true;
34900                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34901                 //} else {
34902                 //    var el = this.el.createChild();
34903                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34904                 //}
34905                 
34906                 this.add(region, ret);
34907                 break;
34908             
34909             /*
34910             case 'TreePanel': // our new panel!
34911                 cfg.el = this.el.createChild();
34912                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34913                 this.add(region, ret);
34914                 break;
34915             */
34916             
34917             case 'Nest': 
34918                 // create a new Layout (which is  a Border Layout...
34919                 
34920                 var clayout = cfg.layout;
34921                 clayout.el  = this.el.createChild();
34922                 clayout.items   = clayout.items  || [];
34923                 
34924                 delete cfg.layout;
34925                 
34926                 // replace this exitems with the clayout ones..
34927                 xitems = clayout.items;
34928                  
34929                 // force background off if it's in center...
34930                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34931                     cfg.background = false;
34932                 }
34933                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34934                 
34935                 
34936                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34937                 //console.log('adding nested layout panel '  + cfg.toSource());
34938                 this.add(region, ret);
34939                 nb = {}; /// find first...
34940                 break;
34941             
34942             case 'Grid':
34943                 
34944                 // needs grid and region
34945                 
34946                 //var el = this.getRegion(region).el.createChild();
34947                 /*
34948                  *var el = this.el.createChild();
34949                 // create the grid first...
34950                 cfg.grid.container = el;
34951                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34952                 */
34953                 
34954                 if (region == 'center' && this.active ) {
34955                     cfg.background = false;
34956                 }
34957                 
34958                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34959                 
34960                 this.add(region, ret);
34961                 /*
34962                 if (cfg.background) {
34963                     // render grid on panel activation (if panel background)
34964                     ret.on('activate', function(gp) {
34965                         if (!gp.grid.rendered) {
34966                     //        gp.grid.render(el);
34967                         }
34968                     });
34969                 } else {
34970                   //  cfg.grid.render(el);
34971                 }
34972                 */
34973                 break;
34974            
34975            
34976             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34977                 // it was the old xcomponent building that caused this before.
34978                 // espeically if border is the top element in the tree.
34979                 ret = this;
34980                 break; 
34981                 
34982                     
34983                 
34984                 
34985                 
34986             default:
34987                 /*
34988                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34989                     
34990                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34991                     this.add(region, ret);
34992                 } else {
34993                 */
34994                     Roo.log(cfg);
34995                     throw "Can not add '" + cfg.xtype + "' to Border";
34996                     return null;
34997              
34998                                 
34999              
35000         }
35001         this.beginUpdate();
35002         // add children..
35003         var region = '';
35004         var abn = {};
35005         Roo.each(xitems, function(i)  {
35006             region = nb && i.region ? i.region : false;
35007             
35008             var add = ret.addxtype(i);
35009            
35010             if (region) {
35011                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35012                 if (!i.background) {
35013                     abn[region] = nb[region] ;
35014                 }
35015             }
35016             
35017         });
35018         this.endUpdate();
35019
35020         // make the last non-background panel active..
35021         //if (nb) { Roo.log(abn); }
35022         if (nb) {
35023             
35024             for(var r in abn) {
35025                 region = this.getRegion(r);
35026                 if (region) {
35027                     // tried using nb[r], but it does not work..
35028                      
35029                     region.showPanel(abn[r]);
35030                    
35031                 }
35032             }
35033         }
35034         return ret;
35035         
35036     },
35037     
35038     
35039 // private
35040     factory : function(cfg)
35041     {
35042         
35043         var validRegions = Roo.bootstrap.layout.Border.regions;
35044
35045         var target = cfg.region;
35046         cfg.mgr = this;
35047         
35048         var r = Roo.bootstrap.layout;
35049         Roo.log(target);
35050         switch(target){
35051             case "north":
35052                 return new r.North(cfg);
35053             case "south":
35054                 return new r.South(cfg);
35055             case "east":
35056                 return new r.East(cfg);
35057             case "west":
35058                 return new r.West(cfg);
35059             case "center":
35060                 return new r.Center(cfg);
35061         }
35062         throw 'Layout region "'+target+'" not supported.';
35063     }
35064     
35065     
35066 });
35067  /*
35068  * Based on:
35069  * Ext JS Library 1.1.1
35070  * Copyright(c) 2006-2007, Ext JS, LLC.
35071  *
35072  * Originally Released Under LGPL - original licence link has changed is not relivant.
35073  *
35074  * Fork - LGPL
35075  * <script type="text/javascript">
35076  */
35077  
35078 /**
35079  * @class Roo.bootstrap.layout.Basic
35080  * @extends Roo.util.Observable
35081  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35082  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35083  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35084  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35085  * @cfg {string}   region  the region that it inhabits..
35086  * @cfg {bool}   skipConfig skip config?
35087  * 
35088
35089  */
35090 Roo.bootstrap.layout.Basic = function(config){
35091     
35092     this.mgr = config.mgr;
35093     
35094     this.position = config.region;
35095     
35096     var skipConfig = config.skipConfig;
35097     
35098     this.events = {
35099         /**
35100          * @scope Roo.BasicLayoutRegion
35101          */
35102         
35103         /**
35104          * @event beforeremove
35105          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35106          * @param {Roo.LayoutRegion} this
35107          * @param {Roo.ContentPanel} panel The panel
35108          * @param {Object} e The cancel event object
35109          */
35110         "beforeremove" : true,
35111         /**
35112          * @event invalidated
35113          * Fires when the layout for this region is changed.
35114          * @param {Roo.LayoutRegion} this
35115          */
35116         "invalidated" : true,
35117         /**
35118          * @event visibilitychange
35119          * Fires when this region is shown or hidden 
35120          * @param {Roo.LayoutRegion} this
35121          * @param {Boolean} visibility true or false
35122          */
35123         "visibilitychange" : true,
35124         /**
35125          * @event paneladded
35126          * Fires when a panel is added. 
35127          * @param {Roo.LayoutRegion} this
35128          * @param {Roo.ContentPanel} panel The panel
35129          */
35130         "paneladded" : true,
35131         /**
35132          * @event panelremoved
35133          * Fires when a panel is removed. 
35134          * @param {Roo.LayoutRegion} this
35135          * @param {Roo.ContentPanel} panel The panel
35136          */
35137         "panelremoved" : true,
35138         /**
35139          * @event beforecollapse
35140          * Fires when this region before collapse.
35141          * @param {Roo.LayoutRegion} this
35142          */
35143         "beforecollapse" : true,
35144         /**
35145          * @event collapsed
35146          * Fires when this region is collapsed.
35147          * @param {Roo.LayoutRegion} this
35148          */
35149         "collapsed" : true,
35150         /**
35151          * @event expanded
35152          * Fires when this region is expanded.
35153          * @param {Roo.LayoutRegion} this
35154          */
35155         "expanded" : true,
35156         /**
35157          * @event slideshow
35158          * Fires when this region is slid into view.
35159          * @param {Roo.LayoutRegion} this
35160          */
35161         "slideshow" : true,
35162         /**
35163          * @event slidehide
35164          * Fires when this region slides out of view. 
35165          * @param {Roo.LayoutRegion} this
35166          */
35167         "slidehide" : true,
35168         /**
35169          * @event panelactivated
35170          * Fires when a panel is activated. 
35171          * @param {Roo.LayoutRegion} this
35172          * @param {Roo.ContentPanel} panel The activated panel
35173          */
35174         "panelactivated" : true,
35175         /**
35176          * @event resized
35177          * Fires when the user resizes this region. 
35178          * @param {Roo.LayoutRegion} this
35179          * @param {Number} newSize The new size (width for east/west, height for north/south)
35180          */
35181         "resized" : true
35182     };
35183     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35184     this.panels = new Roo.util.MixedCollection();
35185     this.panels.getKey = this.getPanelId.createDelegate(this);
35186     this.box = null;
35187     this.activePanel = null;
35188     // ensure listeners are added...
35189     
35190     if (config.listeners || config.events) {
35191         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35192             listeners : config.listeners || {},
35193             events : config.events || {}
35194         });
35195     }
35196     
35197     if(skipConfig !== true){
35198         this.applyConfig(config);
35199     }
35200 };
35201
35202 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35203 {
35204     getPanelId : function(p){
35205         return p.getId();
35206     },
35207     
35208     applyConfig : function(config){
35209         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35210         this.config = config;
35211         
35212     },
35213     
35214     /**
35215      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35216      * the width, for horizontal (north, south) the height.
35217      * @param {Number} newSize The new width or height
35218      */
35219     resizeTo : function(newSize){
35220         var el = this.el ? this.el :
35221                  (this.activePanel ? this.activePanel.getEl() : null);
35222         if(el){
35223             switch(this.position){
35224                 case "east":
35225                 case "west":
35226                     el.setWidth(newSize);
35227                     this.fireEvent("resized", this, newSize);
35228                 break;
35229                 case "north":
35230                 case "south":
35231                     el.setHeight(newSize);
35232                     this.fireEvent("resized", this, newSize);
35233                 break;                
35234             }
35235         }
35236     },
35237     
35238     getBox : function(){
35239         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35240     },
35241     
35242     getMargins : function(){
35243         return this.margins;
35244     },
35245     
35246     updateBox : function(box){
35247         this.box = box;
35248         var el = this.activePanel.getEl();
35249         el.dom.style.left = box.x + "px";
35250         el.dom.style.top = box.y + "px";
35251         this.activePanel.setSize(box.width, box.height);
35252     },
35253     
35254     /**
35255      * Returns the container element for this region.
35256      * @return {Roo.Element}
35257      */
35258     getEl : function(){
35259         return this.activePanel;
35260     },
35261     
35262     /**
35263      * Returns true if this region is currently visible.
35264      * @return {Boolean}
35265      */
35266     isVisible : function(){
35267         return this.activePanel ? true : false;
35268     },
35269     
35270     setActivePanel : function(panel){
35271         panel = this.getPanel(panel);
35272         if(this.activePanel && this.activePanel != panel){
35273             this.activePanel.setActiveState(false);
35274             this.activePanel.getEl().setLeftTop(-10000,-10000);
35275         }
35276         this.activePanel = panel;
35277         panel.setActiveState(true);
35278         if(this.box){
35279             panel.setSize(this.box.width, this.box.height);
35280         }
35281         this.fireEvent("panelactivated", this, panel);
35282         this.fireEvent("invalidated");
35283     },
35284     
35285     /**
35286      * Show the specified panel.
35287      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35288      * @return {Roo.ContentPanel} The shown panel or null
35289      */
35290     showPanel : function(panel){
35291         panel = this.getPanel(panel);
35292         if(panel){
35293             this.setActivePanel(panel);
35294         }
35295         return panel;
35296     },
35297     
35298     /**
35299      * Get the active panel for this region.
35300      * @return {Roo.ContentPanel} The active panel or null
35301      */
35302     getActivePanel : function(){
35303         return this.activePanel;
35304     },
35305     
35306     /**
35307      * Add the passed ContentPanel(s)
35308      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35309      * @return {Roo.ContentPanel} The panel added (if only one was added)
35310      */
35311     add : function(panel){
35312         if(arguments.length > 1){
35313             for(var i = 0, len = arguments.length; i < len; i++) {
35314                 this.add(arguments[i]);
35315             }
35316             return null;
35317         }
35318         if(this.hasPanel(panel)){
35319             this.showPanel(panel);
35320             return panel;
35321         }
35322         var el = panel.getEl();
35323         if(el.dom.parentNode != this.mgr.el.dom){
35324             this.mgr.el.dom.appendChild(el.dom);
35325         }
35326         if(panel.setRegion){
35327             panel.setRegion(this);
35328         }
35329         this.panels.add(panel);
35330         el.setStyle("position", "absolute");
35331         if(!panel.background){
35332             this.setActivePanel(panel);
35333             if(this.config.initialSize && this.panels.getCount()==1){
35334                 this.resizeTo(this.config.initialSize);
35335             }
35336         }
35337         this.fireEvent("paneladded", this, panel);
35338         return panel;
35339     },
35340     
35341     /**
35342      * Returns true if the panel is in this region.
35343      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35344      * @return {Boolean}
35345      */
35346     hasPanel : function(panel){
35347         if(typeof panel == "object"){ // must be panel obj
35348             panel = panel.getId();
35349         }
35350         return this.getPanel(panel) ? true : false;
35351     },
35352     
35353     /**
35354      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35355      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35356      * @param {Boolean} preservePanel Overrides the config preservePanel option
35357      * @return {Roo.ContentPanel} The panel that was removed
35358      */
35359     remove : function(panel, preservePanel){
35360         panel = this.getPanel(panel);
35361         if(!panel){
35362             return null;
35363         }
35364         var e = {};
35365         this.fireEvent("beforeremove", this, panel, e);
35366         if(e.cancel === true){
35367             return null;
35368         }
35369         var panelId = panel.getId();
35370         this.panels.removeKey(panelId);
35371         return panel;
35372     },
35373     
35374     /**
35375      * Returns the panel specified or null if it's not in this region.
35376      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35377      * @return {Roo.ContentPanel}
35378      */
35379     getPanel : function(id){
35380         if(typeof id == "object"){ // must be panel obj
35381             return id;
35382         }
35383         return this.panels.get(id);
35384     },
35385     
35386     /**
35387      * Returns this regions position (north/south/east/west/center).
35388      * @return {String} 
35389      */
35390     getPosition: function(){
35391         return this.position;    
35392     }
35393 });/*
35394  * Based on:
35395  * Ext JS Library 1.1.1
35396  * Copyright(c) 2006-2007, Ext JS, LLC.
35397  *
35398  * Originally Released Under LGPL - original licence link has changed is not relivant.
35399  *
35400  * Fork - LGPL
35401  * <script type="text/javascript">
35402  */
35403  
35404 /**
35405  * @class Roo.bootstrap.layout.Region
35406  * @extends Roo.bootstrap.layout.Basic
35407  * This class represents a region in a layout manager.
35408  
35409  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35410  * @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})
35411  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35412  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35413  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35414  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35415  * @cfg {String}    title           The title for the region (overrides panel titles)
35416  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35417  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35418  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35419  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35420  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35421  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35422  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35423  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35424  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35425  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35426
35427  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35428  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35429  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35430  * @cfg {Number}    width           For East/West panels
35431  * @cfg {Number}    height          For North/South panels
35432  * @cfg {Boolean}   split           To show the splitter
35433  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35434  * 
35435  * @cfg {string}   cls             Extra CSS classes to add to region
35436  * 
35437  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35438  * @cfg {string}   region  the region that it inhabits..
35439  *
35440
35441  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35442  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35443
35444  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35445  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35446  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35447  */
35448 Roo.bootstrap.layout.Region = function(config)
35449 {
35450     this.applyConfig(config);
35451
35452     var mgr = config.mgr;
35453     var pos = config.region;
35454     config.skipConfig = true;
35455     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35456     
35457     if (mgr.el) {
35458         this.onRender(mgr.el);   
35459     }
35460      
35461     this.visible = true;
35462     this.collapsed = false;
35463     this.unrendered_panels = [];
35464 };
35465
35466 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35467
35468     position: '', // set by wrapper (eg. north/south etc..)
35469     unrendered_panels : null,  // unrendered panels.
35470     createBody : function(){
35471         /** This region's body element 
35472         * @type Roo.Element */
35473         this.bodyEl = this.el.createChild({
35474                 tag: "div",
35475                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35476         });
35477     },
35478
35479     onRender: function(ctr, pos)
35480     {
35481         var dh = Roo.DomHelper;
35482         /** This region's container element 
35483         * @type Roo.Element */
35484         this.el = dh.append(ctr.dom, {
35485                 tag: "div",
35486                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35487             }, true);
35488         /** This region's title element 
35489         * @type Roo.Element */
35490     
35491         this.titleEl = dh.append(this.el.dom,
35492             {
35493                     tag: "div",
35494                     unselectable: "on",
35495                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35496                     children:[
35497                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35498                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35499                     ]}, true);
35500         
35501         this.titleEl.enableDisplayMode();
35502         /** This region's title text element 
35503         * @type HTMLElement */
35504         this.titleTextEl = this.titleEl.dom.firstChild;
35505         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35506         /*
35507         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35508         this.closeBtn.enableDisplayMode();
35509         this.closeBtn.on("click", this.closeClicked, this);
35510         this.closeBtn.hide();
35511     */
35512         this.createBody(this.config);
35513         if(this.config.hideWhenEmpty){
35514             this.hide();
35515             this.on("paneladded", this.validateVisibility, this);
35516             this.on("panelremoved", this.validateVisibility, this);
35517         }
35518         if(this.autoScroll){
35519             this.bodyEl.setStyle("overflow", "auto");
35520         }else{
35521             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35522         }
35523         //if(c.titlebar !== false){
35524             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35525                 this.titleEl.hide();
35526             }else{
35527                 this.titleEl.show();
35528                 if(this.config.title){
35529                     this.titleTextEl.innerHTML = this.config.title;
35530                 }
35531             }
35532         //}
35533         if(this.config.collapsed){
35534             this.collapse(true);
35535         }
35536         if(this.config.hidden){
35537             this.hide();
35538         }
35539         
35540         if (this.unrendered_panels && this.unrendered_panels.length) {
35541             for (var i =0;i< this.unrendered_panels.length; i++) {
35542                 this.add(this.unrendered_panels[i]);
35543             }
35544             this.unrendered_panels = null;
35545             
35546         }
35547         
35548     },
35549     
35550     applyConfig : function(c)
35551     {
35552         /*
35553          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35554             var dh = Roo.DomHelper;
35555             if(c.titlebar !== false){
35556                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35557                 this.collapseBtn.on("click", this.collapse, this);
35558                 this.collapseBtn.enableDisplayMode();
35559                 /*
35560                 if(c.showPin === true || this.showPin){
35561                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35562                     this.stickBtn.enableDisplayMode();
35563                     this.stickBtn.on("click", this.expand, this);
35564                     this.stickBtn.hide();
35565                 }
35566                 
35567             }
35568             */
35569             /** This region's collapsed element
35570             * @type Roo.Element */
35571             /*
35572              *
35573             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35574                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35575             ]}, true);
35576             
35577             if(c.floatable !== false){
35578                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35579                this.collapsedEl.on("click", this.collapseClick, this);
35580             }
35581
35582             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35583                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35584                    id: "message", unselectable: "on", style:{"float":"left"}});
35585                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35586              }
35587             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35588             this.expandBtn.on("click", this.expand, this);
35589             
35590         }
35591         
35592         if(this.collapseBtn){
35593             this.collapseBtn.setVisible(c.collapsible == true);
35594         }
35595         
35596         this.cmargins = c.cmargins || this.cmargins ||
35597                          (this.position == "west" || this.position == "east" ?
35598                              {top: 0, left: 2, right:2, bottom: 0} :
35599                              {top: 2, left: 0, right:0, bottom: 2});
35600         */
35601         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35602         
35603         
35604         this.bottomTabs = c.tabPosition != "top";
35605         
35606         this.autoScroll = c.autoScroll || false;
35607         
35608         
35609        
35610         
35611         this.duration = c.duration || .30;
35612         this.slideDuration = c.slideDuration || .45;
35613         this.config = c;
35614        
35615     },
35616     /**
35617      * Returns true if this region is currently visible.
35618      * @return {Boolean}
35619      */
35620     isVisible : function(){
35621         return this.visible;
35622     },
35623
35624     /**
35625      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35626      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35627      */
35628     //setCollapsedTitle : function(title){
35629     //    title = title || "&#160;";
35630      //   if(this.collapsedTitleTextEl){
35631       //      this.collapsedTitleTextEl.innerHTML = title;
35632        // }
35633     //},
35634
35635     getBox : function(){
35636         var b;
35637       //  if(!this.collapsed){
35638             b = this.el.getBox(false, true);
35639        // }else{
35640           //  b = this.collapsedEl.getBox(false, true);
35641         //}
35642         return b;
35643     },
35644
35645     getMargins : function(){
35646         return this.margins;
35647         //return this.collapsed ? this.cmargins : this.margins;
35648     },
35649 /*
35650     highlight : function(){
35651         this.el.addClass("x-layout-panel-dragover");
35652     },
35653
35654     unhighlight : function(){
35655         this.el.removeClass("x-layout-panel-dragover");
35656     },
35657 */
35658     updateBox : function(box)
35659     {
35660         if (!this.bodyEl) {
35661             return; // not rendered yet..
35662         }
35663         
35664         this.box = box;
35665         if(!this.collapsed){
35666             this.el.dom.style.left = box.x + "px";
35667             this.el.dom.style.top = box.y + "px";
35668             this.updateBody(box.width, box.height);
35669         }else{
35670             this.collapsedEl.dom.style.left = box.x + "px";
35671             this.collapsedEl.dom.style.top = box.y + "px";
35672             this.collapsedEl.setSize(box.width, box.height);
35673         }
35674         if(this.tabs){
35675             this.tabs.autoSizeTabs();
35676         }
35677     },
35678
35679     updateBody : function(w, h)
35680     {
35681         if(w !== null){
35682             this.el.setWidth(w);
35683             w -= this.el.getBorderWidth("rl");
35684             if(this.config.adjustments){
35685                 w += this.config.adjustments[0];
35686             }
35687         }
35688         if(h !== null && h > 0){
35689             this.el.setHeight(h);
35690             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35691             h -= this.el.getBorderWidth("tb");
35692             if(this.config.adjustments){
35693                 h += this.config.adjustments[1];
35694             }
35695             this.bodyEl.setHeight(h);
35696             if(this.tabs){
35697                 h = this.tabs.syncHeight(h);
35698             }
35699         }
35700         if(this.panelSize){
35701             w = w !== null ? w : this.panelSize.width;
35702             h = h !== null ? h : this.panelSize.height;
35703         }
35704         if(this.activePanel){
35705             var el = this.activePanel.getEl();
35706             w = w !== null ? w : el.getWidth();
35707             h = h !== null ? h : el.getHeight();
35708             this.panelSize = {width: w, height: h};
35709             this.activePanel.setSize(w, h);
35710         }
35711         if(Roo.isIE && this.tabs){
35712             this.tabs.el.repaint();
35713         }
35714     },
35715
35716     /**
35717      * Returns the container element for this region.
35718      * @return {Roo.Element}
35719      */
35720     getEl : function(){
35721         return this.el;
35722     },
35723
35724     /**
35725      * Hides this region.
35726      */
35727     hide : function(){
35728         //if(!this.collapsed){
35729             this.el.dom.style.left = "-2000px";
35730             this.el.hide();
35731         //}else{
35732          //   this.collapsedEl.dom.style.left = "-2000px";
35733          //   this.collapsedEl.hide();
35734        // }
35735         this.visible = false;
35736         this.fireEvent("visibilitychange", this, false);
35737     },
35738
35739     /**
35740      * Shows this region if it was previously hidden.
35741      */
35742     show : function(){
35743         //if(!this.collapsed){
35744             this.el.show();
35745         //}else{
35746         //    this.collapsedEl.show();
35747        // }
35748         this.visible = true;
35749         this.fireEvent("visibilitychange", this, true);
35750     },
35751 /*
35752     closeClicked : function(){
35753         if(this.activePanel){
35754             this.remove(this.activePanel);
35755         }
35756     },
35757
35758     collapseClick : function(e){
35759         if(this.isSlid){
35760            e.stopPropagation();
35761            this.slideIn();
35762         }else{
35763            e.stopPropagation();
35764            this.slideOut();
35765         }
35766     },
35767 */
35768     /**
35769      * Collapses this region.
35770      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35771      */
35772     /*
35773     collapse : function(skipAnim, skipCheck = false){
35774         if(this.collapsed) {
35775             return;
35776         }
35777         
35778         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35779             
35780             this.collapsed = true;
35781             if(this.split){
35782                 this.split.el.hide();
35783             }
35784             if(this.config.animate && skipAnim !== true){
35785                 this.fireEvent("invalidated", this);
35786                 this.animateCollapse();
35787             }else{
35788                 this.el.setLocation(-20000,-20000);
35789                 this.el.hide();
35790                 this.collapsedEl.show();
35791                 this.fireEvent("collapsed", this);
35792                 this.fireEvent("invalidated", this);
35793             }
35794         }
35795         
35796     },
35797 */
35798     animateCollapse : function(){
35799         // overridden
35800     },
35801
35802     /**
35803      * Expands this region if it was previously collapsed.
35804      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35805      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35806      */
35807     /*
35808     expand : function(e, skipAnim){
35809         if(e) {
35810             e.stopPropagation();
35811         }
35812         if(!this.collapsed || this.el.hasActiveFx()) {
35813             return;
35814         }
35815         if(this.isSlid){
35816             this.afterSlideIn();
35817             skipAnim = true;
35818         }
35819         this.collapsed = false;
35820         if(this.config.animate && skipAnim !== true){
35821             this.animateExpand();
35822         }else{
35823             this.el.show();
35824             if(this.split){
35825                 this.split.el.show();
35826             }
35827             this.collapsedEl.setLocation(-2000,-2000);
35828             this.collapsedEl.hide();
35829             this.fireEvent("invalidated", this);
35830             this.fireEvent("expanded", this);
35831         }
35832     },
35833 */
35834     animateExpand : function(){
35835         // overridden
35836     },
35837
35838     initTabs : function()
35839     {
35840         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35841         
35842         var ts = new Roo.bootstrap.panel.Tabs({
35843                 el: this.bodyEl.dom,
35844                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35845                 disableTooltips: this.config.disableTabTips,
35846                 toolbar : this.config.toolbar
35847             });
35848         
35849         if(this.config.hideTabs){
35850             ts.stripWrap.setDisplayed(false);
35851         }
35852         this.tabs = ts;
35853         ts.resizeTabs = this.config.resizeTabs === true;
35854         ts.minTabWidth = this.config.minTabWidth || 40;
35855         ts.maxTabWidth = this.config.maxTabWidth || 250;
35856         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35857         ts.monitorResize = false;
35858         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35859         ts.bodyEl.addClass('roo-layout-tabs-body');
35860         this.panels.each(this.initPanelAsTab, this);
35861     },
35862
35863     initPanelAsTab : function(panel){
35864         var ti = this.tabs.addTab(
35865             panel.getEl().id,
35866             panel.getTitle(),
35867             null,
35868             this.config.closeOnTab && panel.isClosable(),
35869             panel.tpl
35870         );
35871         if(panel.tabTip !== undefined){
35872             ti.setTooltip(panel.tabTip);
35873         }
35874         ti.on("activate", function(){
35875               this.setActivePanel(panel);
35876         }, this);
35877         
35878         if(this.config.closeOnTab){
35879             ti.on("beforeclose", function(t, e){
35880                 e.cancel = true;
35881                 this.remove(panel);
35882             }, this);
35883         }
35884         
35885         panel.tabItem = ti;
35886         
35887         return ti;
35888     },
35889
35890     updatePanelTitle : function(panel, title)
35891     {
35892         if(this.activePanel == panel){
35893             this.updateTitle(title);
35894         }
35895         if(this.tabs){
35896             var ti = this.tabs.getTab(panel.getEl().id);
35897             ti.setText(title);
35898             if(panel.tabTip !== undefined){
35899                 ti.setTooltip(panel.tabTip);
35900             }
35901         }
35902     },
35903
35904     updateTitle : function(title){
35905         if(this.titleTextEl && !this.config.title){
35906             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35907         }
35908     },
35909
35910     setActivePanel : function(panel)
35911     {
35912         panel = this.getPanel(panel);
35913         if(this.activePanel && this.activePanel != panel){
35914             if(this.activePanel.setActiveState(false) === false){
35915                 return;
35916             }
35917         }
35918         this.activePanel = panel;
35919         panel.setActiveState(true);
35920         if(this.panelSize){
35921             panel.setSize(this.panelSize.width, this.panelSize.height);
35922         }
35923         if(this.closeBtn){
35924             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35925         }
35926         this.updateTitle(panel.getTitle());
35927         if(this.tabs){
35928             this.fireEvent("invalidated", this);
35929         }
35930         this.fireEvent("panelactivated", this, panel);
35931     },
35932
35933     /**
35934      * Shows the specified panel.
35935      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35936      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35937      */
35938     showPanel : function(panel)
35939     {
35940         panel = this.getPanel(panel);
35941         if(panel){
35942             if(this.tabs){
35943                 var tab = this.tabs.getTab(panel.getEl().id);
35944                 if(tab.isHidden()){
35945                     this.tabs.unhideTab(tab.id);
35946                 }
35947                 tab.activate();
35948             }else{
35949                 this.setActivePanel(panel);
35950             }
35951         }
35952         return panel;
35953     },
35954
35955     /**
35956      * Get the active panel for this region.
35957      * @return {Roo.ContentPanel} The active panel or null
35958      */
35959     getActivePanel : function(){
35960         return this.activePanel;
35961     },
35962
35963     validateVisibility : function(){
35964         if(this.panels.getCount() < 1){
35965             this.updateTitle("&#160;");
35966             this.closeBtn.hide();
35967             this.hide();
35968         }else{
35969             if(!this.isVisible()){
35970                 this.show();
35971             }
35972         }
35973     },
35974
35975     /**
35976      * Adds the passed ContentPanel(s) to this region.
35977      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35978      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35979      */
35980     add : function(panel)
35981     {
35982         if(arguments.length > 1){
35983             for(var i = 0, len = arguments.length; i < len; i++) {
35984                 this.add(arguments[i]);
35985             }
35986             return null;
35987         }
35988         
35989         // if we have not been rendered yet, then we can not really do much of this..
35990         if (!this.bodyEl) {
35991             this.unrendered_panels.push(panel);
35992             return panel;
35993         }
35994         
35995         
35996         
35997         
35998         if(this.hasPanel(panel)){
35999             this.showPanel(panel);
36000             return panel;
36001         }
36002         panel.setRegion(this);
36003         this.panels.add(panel);
36004        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36005             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36006             // and hide them... ???
36007             this.bodyEl.dom.appendChild(panel.getEl().dom);
36008             if(panel.background !== true){
36009                 this.setActivePanel(panel);
36010             }
36011             this.fireEvent("paneladded", this, panel);
36012             return panel;
36013         }
36014         */
36015         if(!this.tabs){
36016             this.initTabs();
36017         }else{
36018             this.initPanelAsTab(panel);
36019         }
36020         
36021         
36022         if(panel.background !== true){
36023             this.tabs.activate(panel.getEl().id);
36024         }
36025         this.fireEvent("paneladded", this, panel);
36026         return panel;
36027     },
36028
36029     /**
36030      * Hides the tab for the specified panel.
36031      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36032      */
36033     hidePanel : function(panel){
36034         if(this.tabs && (panel = this.getPanel(panel))){
36035             this.tabs.hideTab(panel.getEl().id);
36036         }
36037     },
36038
36039     /**
36040      * Unhides the tab for a previously hidden panel.
36041      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36042      */
36043     unhidePanel : function(panel){
36044         if(this.tabs && (panel = this.getPanel(panel))){
36045             this.tabs.unhideTab(panel.getEl().id);
36046         }
36047     },
36048
36049     clearPanels : function(){
36050         while(this.panels.getCount() > 0){
36051              this.remove(this.panels.first());
36052         }
36053     },
36054
36055     /**
36056      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36057      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36058      * @param {Boolean} preservePanel Overrides the config preservePanel option
36059      * @return {Roo.ContentPanel} The panel that was removed
36060      */
36061     remove : function(panel, preservePanel)
36062     {
36063         panel = this.getPanel(panel);
36064         if(!panel){
36065             return null;
36066         }
36067         var e = {};
36068         this.fireEvent("beforeremove", this, panel, e);
36069         if(e.cancel === true){
36070             return null;
36071         }
36072         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36073         var panelId = panel.getId();
36074         this.panels.removeKey(panelId);
36075         if(preservePanel){
36076             document.body.appendChild(panel.getEl().dom);
36077         }
36078         if(this.tabs){
36079             this.tabs.removeTab(panel.getEl().id);
36080         }else if (!preservePanel){
36081             this.bodyEl.dom.removeChild(panel.getEl().dom);
36082         }
36083         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36084             var p = this.panels.first();
36085             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36086             tempEl.appendChild(p.getEl().dom);
36087             this.bodyEl.update("");
36088             this.bodyEl.dom.appendChild(p.getEl().dom);
36089             tempEl = null;
36090             this.updateTitle(p.getTitle());
36091             this.tabs = null;
36092             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36093             this.setActivePanel(p);
36094         }
36095         panel.setRegion(null);
36096         if(this.activePanel == panel){
36097             this.activePanel = null;
36098         }
36099         if(this.config.autoDestroy !== false && preservePanel !== true){
36100             try{panel.destroy();}catch(e){}
36101         }
36102         this.fireEvent("panelremoved", this, panel);
36103         return panel;
36104     },
36105
36106     /**
36107      * Returns the TabPanel component used by this region
36108      * @return {Roo.TabPanel}
36109      */
36110     getTabs : function(){
36111         return this.tabs;
36112     },
36113
36114     createTool : function(parentEl, className){
36115         var btn = Roo.DomHelper.append(parentEl, {
36116             tag: "div",
36117             cls: "x-layout-tools-button",
36118             children: [ {
36119                 tag: "div",
36120                 cls: "roo-layout-tools-button-inner " + className,
36121                 html: "&#160;"
36122             }]
36123         }, true);
36124         btn.addClassOnOver("roo-layout-tools-button-over");
36125         return btn;
36126     }
36127 });/*
36128  * Based on:
36129  * Ext JS Library 1.1.1
36130  * Copyright(c) 2006-2007, Ext JS, LLC.
36131  *
36132  * Originally Released Under LGPL - original licence link has changed is not relivant.
36133  *
36134  * Fork - LGPL
36135  * <script type="text/javascript">
36136  */
36137  
36138
36139
36140 /**
36141  * @class Roo.SplitLayoutRegion
36142  * @extends Roo.LayoutRegion
36143  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36144  */
36145 Roo.bootstrap.layout.Split = function(config){
36146     this.cursor = config.cursor;
36147     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36148 };
36149
36150 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36151 {
36152     splitTip : "Drag to resize.",
36153     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36154     useSplitTips : false,
36155
36156     applyConfig : function(config){
36157         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36158     },
36159     
36160     onRender : function(ctr,pos) {
36161         
36162         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36163         if(!this.config.split){
36164             return;
36165         }
36166         if(!this.split){
36167             
36168             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36169                             tag: "div",
36170                             id: this.el.id + "-split",
36171                             cls: "roo-layout-split roo-layout-split-"+this.position,
36172                             html: "&#160;"
36173             });
36174             /** The SplitBar for this region 
36175             * @type Roo.SplitBar */
36176             // does not exist yet...
36177             Roo.log([this.position, this.orientation]);
36178             
36179             this.split = new Roo.bootstrap.SplitBar({
36180                 dragElement : splitEl,
36181                 resizingElement: this.el,
36182                 orientation : this.orientation
36183             });
36184             
36185             this.split.on("moved", this.onSplitMove, this);
36186             this.split.useShim = this.config.useShim === true;
36187             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36188             if(this.useSplitTips){
36189                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36190             }
36191             //if(config.collapsible){
36192             //    this.split.el.on("dblclick", this.collapse,  this);
36193             //}
36194         }
36195         if(typeof this.config.minSize != "undefined"){
36196             this.split.minSize = this.config.minSize;
36197         }
36198         if(typeof this.config.maxSize != "undefined"){
36199             this.split.maxSize = this.config.maxSize;
36200         }
36201         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36202             this.hideSplitter();
36203         }
36204         
36205     },
36206
36207     getHMaxSize : function(){
36208          var cmax = this.config.maxSize || 10000;
36209          var center = this.mgr.getRegion("center");
36210          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36211     },
36212
36213     getVMaxSize : function(){
36214          var cmax = this.config.maxSize || 10000;
36215          var center = this.mgr.getRegion("center");
36216          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36217     },
36218
36219     onSplitMove : function(split, newSize){
36220         this.fireEvent("resized", this, newSize);
36221     },
36222     
36223     /** 
36224      * Returns the {@link Roo.SplitBar} for this region.
36225      * @return {Roo.SplitBar}
36226      */
36227     getSplitBar : function(){
36228         return this.split;
36229     },
36230     
36231     hide : function(){
36232         this.hideSplitter();
36233         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36234     },
36235
36236     hideSplitter : function(){
36237         if(this.split){
36238             this.split.el.setLocation(-2000,-2000);
36239             this.split.el.hide();
36240         }
36241     },
36242
36243     show : function(){
36244         if(this.split){
36245             this.split.el.show();
36246         }
36247         Roo.bootstrap.layout.Split.superclass.show.call(this);
36248     },
36249     
36250     beforeSlide: function(){
36251         if(Roo.isGecko){// firefox overflow auto bug workaround
36252             this.bodyEl.clip();
36253             if(this.tabs) {
36254                 this.tabs.bodyEl.clip();
36255             }
36256             if(this.activePanel){
36257                 this.activePanel.getEl().clip();
36258                 
36259                 if(this.activePanel.beforeSlide){
36260                     this.activePanel.beforeSlide();
36261                 }
36262             }
36263         }
36264     },
36265     
36266     afterSlide : function(){
36267         if(Roo.isGecko){// firefox overflow auto bug workaround
36268             this.bodyEl.unclip();
36269             if(this.tabs) {
36270                 this.tabs.bodyEl.unclip();
36271             }
36272             if(this.activePanel){
36273                 this.activePanel.getEl().unclip();
36274                 if(this.activePanel.afterSlide){
36275                     this.activePanel.afterSlide();
36276                 }
36277             }
36278         }
36279     },
36280
36281     initAutoHide : function(){
36282         if(this.autoHide !== false){
36283             if(!this.autoHideHd){
36284                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36285                 this.autoHideHd = {
36286                     "mouseout": function(e){
36287                         if(!e.within(this.el, true)){
36288                             st.delay(500);
36289                         }
36290                     },
36291                     "mouseover" : function(e){
36292                         st.cancel();
36293                     },
36294                     scope : this
36295                 };
36296             }
36297             this.el.on(this.autoHideHd);
36298         }
36299     },
36300
36301     clearAutoHide : function(){
36302         if(this.autoHide !== false){
36303             this.el.un("mouseout", this.autoHideHd.mouseout);
36304             this.el.un("mouseover", this.autoHideHd.mouseover);
36305         }
36306     },
36307
36308     clearMonitor : function(){
36309         Roo.get(document).un("click", this.slideInIf, this);
36310     },
36311
36312     // these names are backwards but not changed for compat
36313     slideOut : function(){
36314         if(this.isSlid || this.el.hasActiveFx()){
36315             return;
36316         }
36317         this.isSlid = true;
36318         if(this.collapseBtn){
36319             this.collapseBtn.hide();
36320         }
36321         this.closeBtnState = this.closeBtn.getStyle('display');
36322         this.closeBtn.hide();
36323         if(this.stickBtn){
36324             this.stickBtn.show();
36325         }
36326         this.el.show();
36327         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36328         this.beforeSlide();
36329         this.el.setStyle("z-index", 10001);
36330         this.el.slideIn(this.getSlideAnchor(), {
36331             callback: function(){
36332                 this.afterSlide();
36333                 this.initAutoHide();
36334                 Roo.get(document).on("click", this.slideInIf, this);
36335                 this.fireEvent("slideshow", this);
36336             },
36337             scope: this,
36338             block: true
36339         });
36340     },
36341
36342     afterSlideIn : function(){
36343         this.clearAutoHide();
36344         this.isSlid = false;
36345         this.clearMonitor();
36346         this.el.setStyle("z-index", "");
36347         if(this.collapseBtn){
36348             this.collapseBtn.show();
36349         }
36350         this.closeBtn.setStyle('display', this.closeBtnState);
36351         if(this.stickBtn){
36352             this.stickBtn.hide();
36353         }
36354         this.fireEvent("slidehide", this);
36355     },
36356
36357     slideIn : function(cb){
36358         if(!this.isSlid || this.el.hasActiveFx()){
36359             Roo.callback(cb);
36360             return;
36361         }
36362         this.isSlid = false;
36363         this.beforeSlide();
36364         this.el.slideOut(this.getSlideAnchor(), {
36365             callback: function(){
36366                 this.el.setLeftTop(-10000, -10000);
36367                 this.afterSlide();
36368                 this.afterSlideIn();
36369                 Roo.callback(cb);
36370             },
36371             scope: this,
36372             block: true
36373         });
36374     },
36375     
36376     slideInIf : function(e){
36377         if(!e.within(this.el)){
36378             this.slideIn();
36379         }
36380     },
36381
36382     animateCollapse : function(){
36383         this.beforeSlide();
36384         this.el.setStyle("z-index", 20000);
36385         var anchor = this.getSlideAnchor();
36386         this.el.slideOut(anchor, {
36387             callback : function(){
36388                 this.el.setStyle("z-index", "");
36389                 this.collapsedEl.slideIn(anchor, {duration:.3});
36390                 this.afterSlide();
36391                 this.el.setLocation(-10000,-10000);
36392                 this.el.hide();
36393                 this.fireEvent("collapsed", this);
36394             },
36395             scope: this,
36396             block: true
36397         });
36398     },
36399
36400     animateExpand : function(){
36401         this.beforeSlide();
36402         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36403         this.el.setStyle("z-index", 20000);
36404         this.collapsedEl.hide({
36405             duration:.1
36406         });
36407         this.el.slideIn(this.getSlideAnchor(), {
36408             callback : function(){
36409                 this.el.setStyle("z-index", "");
36410                 this.afterSlide();
36411                 if(this.split){
36412                     this.split.el.show();
36413                 }
36414                 this.fireEvent("invalidated", this);
36415                 this.fireEvent("expanded", this);
36416             },
36417             scope: this,
36418             block: true
36419         });
36420     },
36421
36422     anchors : {
36423         "west" : "left",
36424         "east" : "right",
36425         "north" : "top",
36426         "south" : "bottom"
36427     },
36428
36429     sanchors : {
36430         "west" : "l",
36431         "east" : "r",
36432         "north" : "t",
36433         "south" : "b"
36434     },
36435
36436     canchors : {
36437         "west" : "tl-tr",
36438         "east" : "tr-tl",
36439         "north" : "tl-bl",
36440         "south" : "bl-tl"
36441     },
36442
36443     getAnchor : function(){
36444         return this.anchors[this.position];
36445     },
36446
36447     getCollapseAnchor : function(){
36448         return this.canchors[this.position];
36449     },
36450
36451     getSlideAnchor : function(){
36452         return this.sanchors[this.position];
36453     },
36454
36455     getAlignAdj : function(){
36456         var cm = this.cmargins;
36457         switch(this.position){
36458             case "west":
36459                 return [0, 0];
36460             break;
36461             case "east":
36462                 return [0, 0];
36463             break;
36464             case "north":
36465                 return [0, 0];
36466             break;
36467             case "south":
36468                 return [0, 0];
36469             break;
36470         }
36471     },
36472
36473     getExpandAdj : function(){
36474         var c = this.collapsedEl, cm = this.cmargins;
36475         switch(this.position){
36476             case "west":
36477                 return [-(cm.right+c.getWidth()+cm.left), 0];
36478             break;
36479             case "east":
36480                 return [cm.right+c.getWidth()+cm.left, 0];
36481             break;
36482             case "north":
36483                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36484             break;
36485             case "south":
36486                 return [0, cm.top+cm.bottom+c.getHeight()];
36487             break;
36488         }
36489     }
36490 });/*
36491  * Based on:
36492  * Ext JS Library 1.1.1
36493  * Copyright(c) 2006-2007, Ext JS, LLC.
36494  *
36495  * Originally Released Under LGPL - original licence link has changed is not relivant.
36496  *
36497  * Fork - LGPL
36498  * <script type="text/javascript">
36499  */
36500 /*
36501  * These classes are private internal classes
36502  */
36503 Roo.bootstrap.layout.Center = function(config){
36504     config.region = "center";
36505     Roo.bootstrap.layout.Region.call(this, config);
36506     this.visible = true;
36507     this.minWidth = config.minWidth || 20;
36508     this.minHeight = config.minHeight || 20;
36509 };
36510
36511 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36512     hide : function(){
36513         // center panel can't be hidden
36514     },
36515     
36516     show : function(){
36517         // center panel can't be hidden
36518     },
36519     
36520     getMinWidth: function(){
36521         return this.minWidth;
36522     },
36523     
36524     getMinHeight: function(){
36525         return this.minHeight;
36526     }
36527 });
36528
36529
36530
36531
36532  
36533
36534
36535
36536
36537
36538 Roo.bootstrap.layout.North = function(config)
36539 {
36540     config.region = 'north';
36541     config.cursor = 'n-resize';
36542     
36543     Roo.bootstrap.layout.Split.call(this, config);
36544     
36545     
36546     if(this.split){
36547         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36548         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36549         this.split.el.addClass("roo-layout-split-v");
36550     }
36551     var size = config.initialSize || config.height;
36552     if(typeof size != "undefined"){
36553         this.el.setHeight(size);
36554     }
36555 };
36556 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36557 {
36558     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36559     
36560     
36561     
36562     getBox : function(){
36563         if(this.collapsed){
36564             return this.collapsedEl.getBox();
36565         }
36566         var box = this.el.getBox();
36567         if(this.split){
36568             box.height += this.split.el.getHeight();
36569         }
36570         return box;
36571     },
36572     
36573     updateBox : function(box){
36574         if(this.split && !this.collapsed){
36575             box.height -= this.split.el.getHeight();
36576             this.split.el.setLeft(box.x);
36577             this.split.el.setTop(box.y+box.height);
36578             this.split.el.setWidth(box.width);
36579         }
36580         if(this.collapsed){
36581             this.updateBody(box.width, null);
36582         }
36583         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36584     }
36585 });
36586
36587
36588
36589
36590
36591 Roo.bootstrap.layout.South = function(config){
36592     config.region = 'south';
36593     config.cursor = 's-resize';
36594     Roo.bootstrap.layout.Split.call(this, config);
36595     if(this.split){
36596         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36597         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36598         this.split.el.addClass("roo-layout-split-v");
36599     }
36600     var size = config.initialSize || config.height;
36601     if(typeof size != "undefined"){
36602         this.el.setHeight(size);
36603     }
36604 };
36605
36606 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36607     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36608     getBox : function(){
36609         if(this.collapsed){
36610             return this.collapsedEl.getBox();
36611         }
36612         var box = this.el.getBox();
36613         if(this.split){
36614             var sh = this.split.el.getHeight();
36615             box.height += sh;
36616             box.y -= sh;
36617         }
36618         return box;
36619     },
36620     
36621     updateBox : function(box){
36622         if(this.split && !this.collapsed){
36623             var sh = this.split.el.getHeight();
36624             box.height -= sh;
36625             box.y += sh;
36626             this.split.el.setLeft(box.x);
36627             this.split.el.setTop(box.y-sh);
36628             this.split.el.setWidth(box.width);
36629         }
36630         if(this.collapsed){
36631             this.updateBody(box.width, null);
36632         }
36633         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36634     }
36635 });
36636
36637 Roo.bootstrap.layout.East = function(config){
36638     config.region = "east";
36639     config.cursor = "e-resize";
36640     Roo.bootstrap.layout.Split.call(this, config);
36641     if(this.split){
36642         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36643         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36644         this.split.el.addClass("roo-layout-split-h");
36645     }
36646     var size = config.initialSize || config.width;
36647     if(typeof size != "undefined"){
36648         this.el.setWidth(size);
36649     }
36650 };
36651 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36652     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36653     getBox : function(){
36654         if(this.collapsed){
36655             return this.collapsedEl.getBox();
36656         }
36657         var box = this.el.getBox();
36658         if(this.split){
36659             var sw = this.split.el.getWidth();
36660             box.width += sw;
36661             box.x -= sw;
36662         }
36663         return box;
36664     },
36665
36666     updateBox : function(box){
36667         if(this.split && !this.collapsed){
36668             var sw = this.split.el.getWidth();
36669             box.width -= sw;
36670             this.split.el.setLeft(box.x);
36671             this.split.el.setTop(box.y);
36672             this.split.el.setHeight(box.height);
36673             box.x += sw;
36674         }
36675         if(this.collapsed){
36676             this.updateBody(null, box.height);
36677         }
36678         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36679     }
36680 });
36681
36682 Roo.bootstrap.layout.West = function(config){
36683     config.region = "west";
36684     config.cursor = "w-resize";
36685     
36686     Roo.bootstrap.layout.Split.call(this, config);
36687     if(this.split){
36688         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36689         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36690         this.split.el.addClass("roo-layout-split-h");
36691     }
36692     
36693 };
36694 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36695     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36696     
36697     onRender: function(ctr, pos)
36698     {
36699         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36700         var size = this.config.initialSize || this.config.width;
36701         if(typeof size != "undefined"){
36702             this.el.setWidth(size);
36703         }
36704     },
36705     
36706     getBox : function(){
36707         if(this.collapsed){
36708             return this.collapsedEl.getBox();
36709         }
36710         var box = this.el.getBox();
36711         if(this.split){
36712             box.width += this.split.el.getWidth();
36713         }
36714         return box;
36715     },
36716     
36717     updateBox : function(box){
36718         if(this.split && !this.collapsed){
36719             var sw = this.split.el.getWidth();
36720             box.width -= sw;
36721             this.split.el.setLeft(box.x+box.width);
36722             this.split.el.setTop(box.y);
36723             this.split.el.setHeight(box.height);
36724         }
36725         if(this.collapsed){
36726             this.updateBody(null, box.height);
36727         }
36728         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36729     }
36730 });
36731 Roo.namespace("Roo.bootstrap.panel");/*
36732  * Based on:
36733  * Ext JS Library 1.1.1
36734  * Copyright(c) 2006-2007, Ext JS, LLC.
36735  *
36736  * Originally Released Under LGPL - original licence link has changed is not relivant.
36737  *
36738  * Fork - LGPL
36739  * <script type="text/javascript">
36740  */
36741 /**
36742  * @class Roo.ContentPanel
36743  * @extends Roo.util.Observable
36744  * A basic ContentPanel element.
36745  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36746  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36747  * @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
36748  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36749  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36750  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36751  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36752  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36753  * @cfg {String} title          The title for this panel
36754  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36755  * @cfg {String} url            Calls {@link #setUrl} with this value
36756  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36757  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36758  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36759  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36760  * @cfg {Boolean} badges render the badges
36761
36762  * @constructor
36763  * Create a new ContentPanel.
36764  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36765  * @param {String/Object} config A string to set only the title or a config object
36766  * @param {String} content (optional) Set the HTML content for this panel
36767  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36768  */
36769 Roo.bootstrap.panel.Content = function( config){
36770     
36771     this.tpl = config.tpl || false;
36772     
36773     var el = config.el;
36774     var content = config.content;
36775
36776     if(config.autoCreate){ // xtype is available if this is called from factory
36777         el = Roo.id();
36778     }
36779     this.el = Roo.get(el);
36780     if(!this.el && config && config.autoCreate){
36781         if(typeof config.autoCreate == "object"){
36782             if(!config.autoCreate.id){
36783                 config.autoCreate.id = config.id||el;
36784             }
36785             this.el = Roo.DomHelper.append(document.body,
36786                         config.autoCreate, true);
36787         }else{
36788             var elcfg =  {   tag: "div",
36789                             cls: "roo-layout-inactive-content",
36790                             id: config.id||el
36791                             };
36792             if (config.html) {
36793                 elcfg.html = config.html;
36794                 
36795             }
36796                         
36797             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36798         }
36799     } 
36800     this.closable = false;
36801     this.loaded = false;
36802     this.active = false;
36803    
36804       
36805     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36806         
36807         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36808         
36809         this.wrapEl = this.el; //this.el.wrap();
36810         var ti = [];
36811         if (config.toolbar.items) {
36812             ti = config.toolbar.items ;
36813             delete config.toolbar.items ;
36814         }
36815         
36816         var nitems = [];
36817         this.toolbar.render(this.wrapEl, 'before');
36818         for(var i =0;i < ti.length;i++) {
36819           //  Roo.log(['add child', items[i]]);
36820             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36821         }
36822         this.toolbar.items = nitems;
36823         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36824         delete config.toolbar;
36825         
36826     }
36827     /*
36828     // xtype created footer. - not sure if will work as we normally have to render first..
36829     if (this.footer && !this.footer.el && this.footer.xtype) {
36830         if (!this.wrapEl) {
36831             this.wrapEl = this.el.wrap();
36832         }
36833     
36834         this.footer.container = this.wrapEl.createChild();
36835          
36836         this.footer = Roo.factory(this.footer, Roo);
36837         
36838     }
36839     */
36840     
36841      if(typeof config == "string"){
36842         this.title = config;
36843     }else{
36844         Roo.apply(this, config);
36845     }
36846     
36847     if(this.resizeEl){
36848         this.resizeEl = Roo.get(this.resizeEl, true);
36849     }else{
36850         this.resizeEl = this.el;
36851     }
36852     // handle view.xtype
36853     
36854  
36855     
36856     
36857     this.addEvents({
36858         /**
36859          * @event activate
36860          * Fires when this panel is activated. 
36861          * @param {Roo.ContentPanel} this
36862          */
36863         "activate" : true,
36864         /**
36865          * @event deactivate
36866          * Fires when this panel is activated. 
36867          * @param {Roo.ContentPanel} this
36868          */
36869         "deactivate" : true,
36870
36871         /**
36872          * @event resize
36873          * Fires when this panel is resized if fitToFrame is true.
36874          * @param {Roo.ContentPanel} this
36875          * @param {Number} width The width after any component adjustments
36876          * @param {Number} height The height after any component adjustments
36877          */
36878         "resize" : true,
36879         
36880          /**
36881          * @event render
36882          * Fires when this tab is created
36883          * @param {Roo.ContentPanel} this
36884          */
36885         "render" : true
36886         
36887         
36888         
36889     });
36890     
36891
36892     
36893     
36894     if(this.autoScroll){
36895         this.resizeEl.setStyle("overflow", "auto");
36896     } else {
36897         // fix randome scrolling
36898         //this.el.on('scroll', function() {
36899         //    Roo.log('fix random scolling');
36900         //    this.scrollTo('top',0); 
36901         //});
36902     }
36903     content = content || this.content;
36904     if(content){
36905         this.setContent(content);
36906     }
36907     if(config && config.url){
36908         this.setUrl(this.url, this.params, this.loadOnce);
36909     }
36910     
36911     
36912     
36913     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36914     
36915     if (this.view && typeof(this.view.xtype) != 'undefined') {
36916         this.view.el = this.el.appendChild(document.createElement("div"));
36917         this.view = Roo.factory(this.view); 
36918         this.view.render  &&  this.view.render(false, '');  
36919     }
36920     
36921     
36922     this.fireEvent('render', this);
36923 };
36924
36925 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36926     
36927     tabTip : '',
36928     
36929     setRegion : function(region){
36930         this.region = region;
36931         this.setActiveClass(region && !this.background);
36932     },
36933     
36934     
36935     setActiveClass: function(state)
36936     {
36937         if(state){
36938            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36939            this.el.setStyle('position','relative');
36940         }else{
36941            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36942            this.el.setStyle('position', 'absolute');
36943         } 
36944     },
36945     
36946     /**
36947      * Returns the toolbar for this Panel if one was configured. 
36948      * @return {Roo.Toolbar} 
36949      */
36950     getToolbar : function(){
36951         return this.toolbar;
36952     },
36953     
36954     setActiveState : function(active)
36955     {
36956         this.active = active;
36957         this.setActiveClass(active);
36958         if(!active){
36959             if(this.fireEvent("deactivate", this) === false){
36960                 return false;
36961             }
36962             return true;
36963         }
36964         this.fireEvent("activate", this);
36965         return true;
36966     },
36967     /**
36968      * Updates this panel's element
36969      * @param {String} content The new content
36970      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36971     */
36972     setContent : function(content, loadScripts){
36973         this.el.update(content, loadScripts);
36974     },
36975
36976     ignoreResize : function(w, h){
36977         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36978             return true;
36979         }else{
36980             this.lastSize = {width: w, height: h};
36981             return false;
36982         }
36983     },
36984     /**
36985      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36986      * @return {Roo.UpdateManager} The UpdateManager
36987      */
36988     getUpdateManager : function(){
36989         return this.el.getUpdateManager();
36990     },
36991      /**
36992      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36993      * @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:
36994 <pre><code>
36995 panel.load({
36996     url: "your-url.php",
36997     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36998     callback: yourFunction,
36999     scope: yourObject, //(optional scope)
37000     discardUrl: false,
37001     nocache: false,
37002     text: "Loading...",
37003     timeout: 30,
37004     scripts: false
37005 });
37006 </code></pre>
37007      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37008      * 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.
37009      * @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}
37010      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37011      * @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.
37012      * @return {Roo.ContentPanel} this
37013      */
37014     load : function(){
37015         var um = this.el.getUpdateManager();
37016         um.update.apply(um, arguments);
37017         return this;
37018     },
37019
37020
37021     /**
37022      * 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.
37023      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37024      * @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)
37025      * @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)
37026      * @return {Roo.UpdateManager} The UpdateManager
37027      */
37028     setUrl : function(url, params, loadOnce){
37029         if(this.refreshDelegate){
37030             this.removeListener("activate", this.refreshDelegate);
37031         }
37032         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37033         this.on("activate", this.refreshDelegate);
37034         return this.el.getUpdateManager();
37035     },
37036     
37037     _handleRefresh : function(url, params, loadOnce){
37038         if(!loadOnce || !this.loaded){
37039             var updater = this.el.getUpdateManager();
37040             updater.update(url, params, this._setLoaded.createDelegate(this));
37041         }
37042     },
37043     
37044     _setLoaded : function(){
37045         this.loaded = true;
37046     }, 
37047     
37048     /**
37049      * Returns this panel's id
37050      * @return {String} 
37051      */
37052     getId : function(){
37053         return this.el.id;
37054     },
37055     
37056     /** 
37057      * Returns this panel's element - used by regiosn to add.
37058      * @return {Roo.Element} 
37059      */
37060     getEl : function(){
37061         return this.wrapEl || this.el;
37062     },
37063     
37064    
37065     
37066     adjustForComponents : function(width, height)
37067     {
37068         //Roo.log('adjustForComponents ');
37069         if(this.resizeEl != this.el){
37070             width -= this.el.getFrameWidth('lr');
37071             height -= this.el.getFrameWidth('tb');
37072         }
37073         if(this.toolbar){
37074             var te = this.toolbar.getEl();
37075             te.setWidth(width);
37076             height -= te.getHeight();
37077         }
37078         if(this.footer){
37079             var te = this.footer.getEl();
37080             te.setWidth(width);
37081             height -= te.getHeight();
37082         }
37083         
37084         
37085         if(this.adjustments){
37086             width += this.adjustments[0];
37087             height += this.adjustments[1];
37088         }
37089         return {"width": width, "height": height};
37090     },
37091     
37092     setSize : function(width, height){
37093         if(this.fitToFrame && !this.ignoreResize(width, height)){
37094             if(this.fitContainer && this.resizeEl != this.el){
37095                 this.el.setSize(width, height);
37096             }
37097             var size = this.adjustForComponents(width, height);
37098             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37099             this.fireEvent('resize', this, size.width, size.height);
37100         }
37101     },
37102     
37103     /**
37104      * Returns this panel's title
37105      * @return {String} 
37106      */
37107     getTitle : function(){
37108         
37109         if (typeof(this.title) != 'object') {
37110             return this.title;
37111         }
37112         
37113         var t = '';
37114         for (var k in this.title) {
37115             if (!this.title.hasOwnProperty(k)) {
37116                 continue;
37117             }
37118             
37119             if (k.indexOf('-') >= 0) {
37120                 var s = k.split('-');
37121                 for (var i = 0; i<s.length; i++) {
37122                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37123                 }
37124             } else {
37125                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37126             }
37127         }
37128         return t;
37129     },
37130     
37131     /**
37132      * Set this panel's title
37133      * @param {String} title
37134      */
37135     setTitle : function(title){
37136         this.title = title;
37137         if(this.region){
37138             this.region.updatePanelTitle(this, title);
37139         }
37140     },
37141     
37142     /**
37143      * Returns true is this panel was configured to be closable
37144      * @return {Boolean} 
37145      */
37146     isClosable : function(){
37147         return this.closable;
37148     },
37149     
37150     beforeSlide : function(){
37151         this.el.clip();
37152         this.resizeEl.clip();
37153     },
37154     
37155     afterSlide : function(){
37156         this.el.unclip();
37157         this.resizeEl.unclip();
37158     },
37159     
37160     /**
37161      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37162      *   Will fail silently if the {@link #setUrl} method has not been called.
37163      *   This does not activate the panel, just updates its content.
37164      */
37165     refresh : function(){
37166         if(this.refreshDelegate){
37167            this.loaded = false;
37168            this.refreshDelegate();
37169         }
37170     },
37171     
37172     /**
37173      * Destroys this panel
37174      */
37175     destroy : function(){
37176         this.el.removeAllListeners();
37177         var tempEl = document.createElement("span");
37178         tempEl.appendChild(this.el.dom);
37179         tempEl.innerHTML = "";
37180         this.el.remove();
37181         this.el = null;
37182     },
37183     
37184     /**
37185      * form - if the content panel contains a form - this is a reference to it.
37186      * @type {Roo.form.Form}
37187      */
37188     form : false,
37189     /**
37190      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37191      *    This contains a reference to it.
37192      * @type {Roo.View}
37193      */
37194     view : false,
37195     
37196       /**
37197      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37198      * <pre><code>
37199
37200 layout.addxtype({
37201        xtype : 'Form',
37202        items: [ .... ]
37203    }
37204 );
37205
37206 </code></pre>
37207      * @param {Object} cfg Xtype definition of item to add.
37208      */
37209     
37210     
37211     getChildContainer: function () {
37212         return this.getEl();
37213     }
37214     
37215     
37216     /*
37217         var  ret = new Roo.factory(cfg);
37218         return ret;
37219         
37220         
37221         // add form..
37222         if (cfg.xtype.match(/^Form$/)) {
37223             
37224             var el;
37225             //if (this.footer) {
37226             //    el = this.footer.container.insertSibling(false, 'before');
37227             //} else {
37228                 el = this.el.createChild();
37229             //}
37230
37231             this.form = new  Roo.form.Form(cfg);
37232             
37233             
37234             if ( this.form.allItems.length) {
37235                 this.form.render(el.dom);
37236             }
37237             return this.form;
37238         }
37239         // should only have one of theses..
37240         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37241             // views.. should not be just added - used named prop 'view''
37242             
37243             cfg.el = this.el.appendChild(document.createElement("div"));
37244             // factory?
37245             
37246             var ret = new Roo.factory(cfg);
37247              
37248              ret.render && ret.render(false, ''); // render blank..
37249             this.view = ret;
37250             return ret;
37251         }
37252         return false;
37253     }
37254     \*/
37255 });
37256  
37257 /**
37258  * @class Roo.bootstrap.panel.Grid
37259  * @extends Roo.bootstrap.panel.Content
37260  * @constructor
37261  * Create a new GridPanel.
37262  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37263  * @param {Object} config A the config object
37264   
37265  */
37266
37267
37268
37269 Roo.bootstrap.panel.Grid = function(config)
37270 {
37271     
37272       
37273     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37274         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37275
37276     config.el = this.wrapper;
37277     //this.el = this.wrapper;
37278     
37279       if (config.container) {
37280         // ctor'ed from a Border/panel.grid
37281         
37282         
37283         this.wrapper.setStyle("overflow", "hidden");
37284         this.wrapper.addClass('roo-grid-container');
37285
37286     }
37287     
37288     
37289     if(config.toolbar){
37290         var tool_el = this.wrapper.createChild();    
37291         this.toolbar = Roo.factory(config.toolbar);
37292         var ti = [];
37293         if (config.toolbar.items) {
37294             ti = config.toolbar.items ;
37295             delete config.toolbar.items ;
37296         }
37297         
37298         var nitems = [];
37299         this.toolbar.render(tool_el);
37300         for(var i =0;i < ti.length;i++) {
37301           //  Roo.log(['add child', items[i]]);
37302             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37303         }
37304         this.toolbar.items = nitems;
37305         
37306         delete config.toolbar;
37307     }
37308     
37309     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37310     config.grid.scrollBody = true;;
37311     config.grid.monitorWindowResize = false; // turn off autosizing
37312     config.grid.autoHeight = false;
37313     config.grid.autoWidth = false;
37314     
37315     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37316     
37317     if (config.background) {
37318         // render grid on panel activation (if panel background)
37319         this.on('activate', function(gp) {
37320             if (!gp.grid.rendered) {
37321                 gp.grid.render(this.wrapper);
37322                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37323             }
37324         });
37325             
37326     } else {
37327         this.grid.render(this.wrapper);
37328         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37329
37330     }
37331     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37332     // ??? needed ??? config.el = this.wrapper;
37333     
37334     
37335     
37336   
37337     // xtype created footer. - not sure if will work as we normally have to render first..
37338     if (this.footer && !this.footer.el && this.footer.xtype) {
37339         
37340         var ctr = this.grid.getView().getFooterPanel(true);
37341         this.footer.dataSource = this.grid.dataSource;
37342         this.footer = Roo.factory(this.footer, Roo);
37343         this.footer.render(ctr);
37344         
37345     }
37346     
37347     
37348     
37349     
37350      
37351 };
37352
37353 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37354     getId : function(){
37355         return this.grid.id;
37356     },
37357     
37358     /**
37359      * Returns the grid for this panel
37360      * @return {Roo.bootstrap.Table} 
37361      */
37362     getGrid : function(){
37363         return this.grid;    
37364     },
37365     
37366     setSize : function(width, height){
37367         if(!this.ignoreResize(width, height)){
37368             var grid = this.grid;
37369             var size = this.adjustForComponents(width, height);
37370             var gridel = grid.getGridEl();
37371             gridel.setSize(size.width, size.height);
37372             /*
37373             var thd = grid.getGridEl().select('thead',true).first();
37374             var tbd = grid.getGridEl().select('tbody', true).first();
37375             if (tbd) {
37376                 tbd.setSize(width, height - thd.getHeight());
37377             }
37378             */
37379             grid.autoSize();
37380         }
37381     },
37382      
37383     
37384     
37385     beforeSlide : function(){
37386         this.grid.getView().scroller.clip();
37387     },
37388     
37389     afterSlide : function(){
37390         this.grid.getView().scroller.unclip();
37391     },
37392     
37393     destroy : function(){
37394         this.grid.destroy();
37395         delete this.grid;
37396         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37397     }
37398 });
37399
37400 /**
37401  * @class Roo.bootstrap.panel.Nest
37402  * @extends Roo.bootstrap.panel.Content
37403  * @constructor
37404  * Create a new Panel, that can contain a layout.Border.
37405  * 
37406  * 
37407  * @param {Roo.BorderLayout} layout The layout for this panel
37408  * @param {String/Object} config A string to set only the title or a config object
37409  */
37410 Roo.bootstrap.panel.Nest = function(config)
37411 {
37412     // construct with only one argument..
37413     /* FIXME - implement nicer consturctors
37414     if (layout.layout) {
37415         config = layout;
37416         layout = config.layout;
37417         delete config.layout;
37418     }
37419     if (layout.xtype && !layout.getEl) {
37420         // then layout needs constructing..
37421         layout = Roo.factory(layout, Roo);
37422     }
37423     */
37424     
37425     config.el =  config.layout.getEl();
37426     
37427     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37428     
37429     config.layout.monitorWindowResize = false; // turn off autosizing
37430     this.layout = config.layout;
37431     this.layout.getEl().addClass("roo-layout-nested-layout");
37432     
37433     
37434     
37435     
37436 };
37437
37438 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37439
37440     setSize : function(width, height){
37441         if(!this.ignoreResize(width, height)){
37442             var size = this.adjustForComponents(width, height);
37443             var el = this.layout.getEl();
37444             if (size.height < 1) {
37445                 el.setWidth(size.width);   
37446             } else {
37447                 el.setSize(size.width, size.height);
37448             }
37449             var touch = el.dom.offsetWidth;
37450             this.layout.layout();
37451             // ie requires a double layout on the first pass
37452             if(Roo.isIE && !this.initialized){
37453                 this.initialized = true;
37454                 this.layout.layout();
37455             }
37456         }
37457     },
37458     
37459     // activate all subpanels if not currently active..
37460     
37461     setActiveState : function(active){
37462         this.active = active;
37463         this.setActiveClass(active);
37464         
37465         if(!active){
37466             this.fireEvent("deactivate", this);
37467             return;
37468         }
37469         
37470         this.fireEvent("activate", this);
37471         // not sure if this should happen before or after..
37472         if (!this.layout) {
37473             return; // should not happen..
37474         }
37475         var reg = false;
37476         for (var r in this.layout.regions) {
37477             reg = this.layout.getRegion(r);
37478             if (reg.getActivePanel()) {
37479                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37480                 reg.setActivePanel(reg.getActivePanel());
37481                 continue;
37482             }
37483             if (!reg.panels.length) {
37484                 continue;
37485             }
37486             reg.showPanel(reg.getPanel(0));
37487         }
37488         
37489         
37490         
37491         
37492     },
37493     
37494     /**
37495      * Returns the nested BorderLayout for this panel
37496      * @return {Roo.BorderLayout} 
37497      */
37498     getLayout : function(){
37499         return this.layout;
37500     },
37501     
37502      /**
37503      * Adds a xtype elements to the layout of the nested panel
37504      * <pre><code>
37505
37506 panel.addxtype({
37507        xtype : 'ContentPanel',
37508        region: 'west',
37509        items: [ .... ]
37510    }
37511 );
37512
37513 panel.addxtype({
37514         xtype : 'NestedLayoutPanel',
37515         region: 'west',
37516         layout: {
37517            center: { },
37518            west: { }   
37519         },
37520         items : [ ... list of content panels or nested layout panels.. ]
37521    }
37522 );
37523 </code></pre>
37524      * @param {Object} cfg Xtype definition of item to add.
37525      */
37526     addxtype : function(cfg) {
37527         return this.layout.addxtype(cfg);
37528     
37529     }
37530 });        /*
37531  * Based on:
37532  * Ext JS Library 1.1.1
37533  * Copyright(c) 2006-2007, Ext JS, LLC.
37534  *
37535  * Originally Released Under LGPL - original licence link has changed is not relivant.
37536  *
37537  * Fork - LGPL
37538  * <script type="text/javascript">
37539  */
37540 /**
37541  * @class Roo.TabPanel
37542  * @extends Roo.util.Observable
37543  * A lightweight tab container.
37544  * <br><br>
37545  * Usage:
37546  * <pre><code>
37547 // basic tabs 1, built from existing content
37548 var tabs = new Roo.TabPanel("tabs1");
37549 tabs.addTab("script", "View Script");
37550 tabs.addTab("markup", "View Markup");
37551 tabs.activate("script");
37552
37553 // more advanced tabs, built from javascript
37554 var jtabs = new Roo.TabPanel("jtabs");
37555 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37556
37557 // set up the UpdateManager
37558 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37559 var updater = tab2.getUpdateManager();
37560 updater.setDefaultUrl("ajax1.htm");
37561 tab2.on('activate', updater.refresh, updater, true);
37562
37563 // Use setUrl for Ajax loading
37564 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37565 tab3.setUrl("ajax2.htm", null, true);
37566
37567 // Disabled tab
37568 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37569 tab4.disable();
37570
37571 jtabs.activate("jtabs-1");
37572  * </code></pre>
37573  * @constructor
37574  * Create a new TabPanel.
37575  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37576  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37577  */
37578 Roo.bootstrap.panel.Tabs = function(config){
37579     /**
37580     * The container element for this TabPanel.
37581     * @type Roo.Element
37582     */
37583     this.el = Roo.get(config.el);
37584     delete config.el;
37585     if(config){
37586         if(typeof config == "boolean"){
37587             this.tabPosition = config ? "bottom" : "top";
37588         }else{
37589             Roo.apply(this, config);
37590         }
37591     }
37592     
37593     if(this.tabPosition == "bottom"){
37594         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37595         this.el.addClass("roo-tabs-bottom");
37596     }
37597     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37598     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37599     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37600     if(Roo.isIE){
37601         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37602     }
37603     if(this.tabPosition != "bottom"){
37604         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37605          * @type Roo.Element
37606          */
37607         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37608         this.el.addClass("roo-tabs-top");
37609     }
37610     this.items = [];
37611
37612     this.bodyEl.setStyle("position", "relative");
37613
37614     this.active = null;
37615     this.activateDelegate = this.activate.createDelegate(this);
37616
37617     this.addEvents({
37618         /**
37619          * @event tabchange
37620          * Fires when the active tab changes
37621          * @param {Roo.TabPanel} this
37622          * @param {Roo.TabPanelItem} activePanel The new active tab
37623          */
37624         "tabchange": true,
37625         /**
37626          * @event beforetabchange
37627          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37628          * @param {Roo.TabPanel} this
37629          * @param {Object} e Set cancel to true on this object to cancel the tab change
37630          * @param {Roo.TabPanelItem} tab The tab being changed to
37631          */
37632         "beforetabchange" : true
37633     });
37634
37635     Roo.EventManager.onWindowResize(this.onResize, this);
37636     this.cpad = this.el.getPadding("lr");
37637     this.hiddenCount = 0;
37638
37639
37640     // toolbar on the tabbar support...
37641     if (this.toolbar) {
37642         alert("no toolbar support yet");
37643         this.toolbar  = false;
37644         /*
37645         var tcfg = this.toolbar;
37646         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37647         this.toolbar = new Roo.Toolbar(tcfg);
37648         if (Roo.isSafari) {
37649             var tbl = tcfg.container.child('table', true);
37650             tbl.setAttribute('width', '100%');
37651         }
37652         */
37653         
37654     }
37655    
37656
37657
37658     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37659 };
37660
37661 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37662     /*
37663      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37664      */
37665     tabPosition : "top",
37666     /*
37667      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37668      */
37669     currentTabWidth : 0,
37670     /*
37671      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37672      */
37673     minTabWidth : 40,
37674     /*
37675      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37676      */
37677     maxTabWidth : 250,
37678     /*
37679      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37680      */
37681     preferredTabWidth : 175,
37682     /*
37683      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37684      */
37685     resizeTabs : false,
37686     /*
37687      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37688      */
37689     monitorResize : true,
37690     /*
37691      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37692      */
37693     toolbar : false,
37694
37695     /**
37696      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37697      * @param {String} id The id of the div to use <b>or create</b>
37698      * @param {String} text The text for the tab
37699      * @param {String} content (optional) Content to put in the TabPanelItem body
37700      * @param {Boolean} closable (optional) True to create a close icon on the tab
37701      * @return {Roo.TabPanelItem} The created TabPanelItem
37702      */
37703     addTab : function(id, text, content, closable, tpl)
37704     {
37705         var item = new Roo.bootstrap.panel.TabItem({
37706             panel: this,
37707             id : id,
37708             text : text,
37709             closable : closable,
37710             tpl : tpl
37711         });
37712         this.addTabItem(item);
37713         if(content){
37714             item.setContent(content);
37715         }
37716         return item;
37717     },
37718
37719     /**
37720      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37721      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37722      * @return {Roo.TabPanelItem}
37723      */
37724     getTab : function(id){
37725         return this.items[id];
37726     },
37727
37728     /**
37729      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37730      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37731      */
37732     hideTab : function(id){
37733         var t = this.items[id];
37734         if(!t.isHidden()){
37735            t.setHidden(true);
37736            this.hiddenCount++;
37737            this.autoSizeTabs();
37738         }
37739     },
37740
37741     /**
37742      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37743      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37744      */
37745     unhideTab : function(id){
37746         var t = this.items[id];
37747         if(t.isHidden()){
37748            t.setHidden(false);
37749            this.hiddenCount--;
37750            this.autoSizeTabs();
37751         }
37752     },
37753
37754     /**
37755      * Adds an existing {@link Roo.TabPanelItem}.
37756      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37757      */
37758     addTabItem : function(item){
37759         this.items[item.id] = item;
37760         this.items.push(item);
37761       //  if(this.resizeTabs){
37762     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37763   //         this.autoSizeTabs();
37764 //        }else{
37765 //            item.autoSize();
37766        // }
37767     },
37768
37769     /**
37770      * Removes a {@link Roo.TabPanelItem}.
37771      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37772      */
37773     removeTab : function(id){
37774         var items = this.items;
37775         var tab = items[id];
37776         if(!tab) { return; }
37777         var index = items.indexOf(tab);
37778         if(this.active == tab && items.length > 1){
37779             var newTab = this.getNextAvailable(index);
37780             if(newTab) {
37781                 newTab.activate();
37782             }
37783         }
37784         this.stripEl.dom.removeChild(tab.pnode.dom);
37785         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37786             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37787         }
37788         items.splice(index, 1);
37789         delete this.items[tab.id];
37790         tab.fireEvent("close", tab);
37791         tab.purgeListeners();
37792         this.autoSizeTabs();
37793     },
37794
37795     getNextAvailable : function(start){
37796         var items = this.items;
37797         var index = start;
37798         // look for a next tab that will slide over to
37799         // replace the one being removed
37800         while(index < items.length){
37801             var item = items[++index];
37802             if(item && !item.isHidden()){
37803                 return item;
37804             }
37805         }
37806         // if one isn't found select the previous tab (on the left)
37807         index = start;
37808         while(index >= 0){
37809             var item = items[--index];
37810             if(item && !item.isHidden()){
37811                 return item;
37812             }
37813         }
37814         return null;
37815     },
37816
37817     /**
37818      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37819      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37820      */
37821     disableTab : function(id){
37822         var tab = this.items[id];
37823         if(tab && this.active != tab){
37824             tab.disable();
37825         }
37826     },
37827
37828     /**
37829      * Enables a {@link Roo.TabPanelItem} that is disabled.
37830      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37831      */
37832     enableTab : function(id){
37833         var tab = this.items[id];
37834         tab.enable();
37835     },
37836
37837     /**
37838      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37839      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37840      * @return {Roo.TabPanelItem} The TabPanelItem.
37841      */
37842     activate : function(id){
37843         var tab = this.items[id];
37844         if(!tab){
37845             return null;
37846         }
37847         if(tab == this.active || tab.disabled){
37848             return tab;
37849         }
37850         var e = {};
37851         this.fireEvent("beforetabchange", this, e, tab);
37852         if(e.cancel !== true && !tab.disabled){
37853             if(this.active){
37854                 this.active.hide();
37855             }
37856             this.active = this.items[id];
37857             this.active.show();
37858             this.fireEvent("tabchange", this, this.active);
37859         }
37860         return tab;
37861     },
37862
37863     /**
37864      * Gets the active {@link Roo.TabPanelItem}.
37865      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37866      */
37867     getActiveTab : function(){
37868         return this.active;
37869     },
37870
37871     /**
37872      * Updates the tab body element to fit the height of the container element
37873      * for overflow scrolling
37874      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37875      */
37876     syncHeight : function(targetHeight){
37877         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37878         var bm = this.bodyEl.getMargins();
37879         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37880         this.bodyEl.setHeight(newHeight);
37881         return newHeight;
37882     },
37883
37884     onResize : function(){
37885         if(this.monitorResize){
37886             this.autoSizeTabs();
37887         }
37888     },
37889
37890     /**
37891      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37892      */
37893     beginUpdate : function(){
37894         this.updating = true;
37895     },
37896
37897     /**
37898      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37899      */
37900     endUpdate : function(){
37901         this.updating = false;
37902         this.autoSizeTabs();
37903     },
37904
37905     /**
37906      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37907      */
37908     autoSizeTabs : function(){
37909         var count = this.items.length;
37910         var vcount = count - this.hiddenCount;
37911         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37912             return;
37913         }
37914         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37915         var availWidth = Math.floor(w / vcount);
37916         var b = this.stripBody;
37917         if(b.getWidth() > w){
37918             var tabs = this.items;
37919             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37920             if(availWidth < this.minTabWidth){
37921                 /*if(!this.sleft){    // incomplete scrolling code
37922                     this.createScrollButtons();
37923                 }
37924                 this.showScroll();
37925                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37926             }
37927         }else{
37928             if(this.currentTabWidth < this.preferredTabWidth){
37929                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37930             }
37931         }
37932     },
37933
37934     /**
37935      * Returns the number of tabs in this TabPanel.
37936      * @return {Number}
37937      */
37938      getCount : function(){
37939          return this.items.length;
37940      },
37941
37942     /**
37943      * Resizes all the tabs to the passed width
37944      * @param {Number} The new width
37945      */
37946     setTabWidth : function(width){
37947         this.currentTabWidth = width;
37948         for(var i = 0, len = this.items.length; i < len; i++) {
37949                 if(!this.items[i].isHidden()) {
37950                 this.items[i].setWidth(width);
37951             }
37952         }
37953     },
37954
37955     /**
37956      * Destroys this TabPanel
37957      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37958      */
37959     destroy : function(removeEl){
37960         Roo.EventManager.removeResizeListener(this.onResize, this);
37961         for(var i = 0, len = this.items.length; i < len; i++){
37962             this.items[i].purgeListeners();
37963         }
37964         if(removeEl === true){
37965             this.el.update("");
37966             this.el.remove();
37967         }
37968     },
37969     
37970     createStrip : function(container)
37971     {
37972         var strip = document.createElement("nav");
37973         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37974         container.appendChild(strip);
37975         return strip;
37976     },
37977     
37978     createStripList : function(strip)
37979     {
37980         // div wrapper for retard IE
37981         // returns the "tr" element.
37982         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37983         //'<div class="x-tabs-strip-wrap">'+
37984           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37985           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37986         return strip.firstChild; //.firstChild.firstChild.firstChild;
37987     },
37988     createBody : function(container)
37989     {
37990         var body = document.createElement("div");
37991         Roo.id(body, "tab-body");
37992         //Roo.fly(body).addClass("x-tabs-body");
37993         Roo.fly(body).addClass("tab-content");
37994         container.appendChild(body);
37995         return body;
37996     },
37997     createItemBody :function(bodyEl, id){
37998         var body = Roo.getDom(id);
37999         if(!body){
38000             body = document.createElement("div");
38001             body.id = id;
38002         }
38003         //Roo.fly(body).addClass("x-tabs-item-body");
38004         Roo.fly(body).addClass("tab-pane");
38005          bodyEl.insertBefore(body, bodyEl.firstChild);
38006         return body;
38007     },
38008     /** @private */
38009     createStripElements :  function(stripEl, text, closable, tpl)
38010     {
38011         var td = document.createElement("li"); // was td..
38012         
38013         
38014         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38015         
38016         
38017         stripEl.appendChild(td);
38018         /*if(closable){
38019             td.className = "x-tabs-closable";
38020             if(!this.closeTpl){
38021                 this.closeTpl = new Roo.Template(
38022                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38023                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38024                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38025                 );
38026             }
38027             var el = this.closeTpl.overwrite(td, {"text": text});
38028             var close = el.getElementsByTagName("div")[0];
38029             var inner = el.getElementsByTagName("em")[0];
38030             return {"el": el, "close": close, "inner": inner};
38031         } else {
38032         */
38033         // not sure what this is..
38034 //            if(!this.tabTpl){
38035                 //this.tabTpl = new Roo.Template(
38036                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38037                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38038                 //);
38039 //                this.tabTpl = new Roo.Template(
38040 //                   '<a href="#">' +
38041 //                   '<span unselectable="on"' +
38042 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38043 //                            ' >{text}</span></a>'
38044 //                );
38045 //                
38046 //            }
38047
38048
38049             var template = tpl || this.tabTpl || false;
38050             
38051             if(!template){
38052                 
38053                 template = new Roo.Template(
38054                    '<a href="#">' +
38055                    '<span unselectable="on"' +
38056                             (this.disableTooltips ? '' : ' title="{text}"') +
38057                             ' >{text}</span></a>'
38058                 );
38059             }
38060             
38061             switch (typeof(template)) {
38062                 case 'object' :
38063                     break;
38064                 case 'string' :
38065                     template = new Roo.Template(template);
38066                     break;
38067                 default :
38068                     break;
38069             }
38070             
38071             var el = template.overwrite(td, {"text": text});
38072             
38073             var inner = el.getElementsByTagName("span")[0];
38074             
38075             return {"el": el, "inner": inner};
38076             
38077     }
38078         
38079     
38080 });
38081
38082 /**
38083  * @class Roo.TabPanelItem
38084  * @extends Roo.util.Observable
38085  * Represents an individual item (tab plus body) in a TabPanel.
38086  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38087  * @param {String} id The id of this TabPanelItem
38088  * @param {String} text The text for the tab of this TabPanelItem
38089  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38090  */
38091 Roo.bootstrap.panel.TabItem = function(config){
38092     /**
38093      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38094      * @type Roo.TabPanel
38095      */
38096     this.tabPanel = config.panel;
38097     /**
38098      * The id for this TabPanelItem
38099      * @type String
38100      */
38101     this.id = config.id;
38102     /** @private */
38103     this.disabled = false;
38104     /** @private */
38105     this.text = config.text;
38106     /** @private */
38107     this.loaded = false;
38108     this.closable = config.closable;
38109
38110     /**
38111      * The body element for this TabPanelItem.
38112      * @type Roo.Element
38113      */
38114     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38115     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38116     this.bodyEl.setStyle("display", "block");
38117     this.bodyEl.setStyle("zoom", "1");
38118     //this.hideAction();
38119
38120     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38121     /** @private */
38122     this.el = Roo.get(els.el);
38123     this.inner = Roo.get(els.inner, true);
38124     this.textEl = Roo.get(this.el.dom.firstChild, true);
38125     this.pnode = Roo.get(els.el.parentNode, true);
38126 //    this.el.on("mousedown", this.onTabMouseDown, this);
38127     this.el.on("click", this.onTabClick, this);
38128     /** @private */
38129     if(config.closable){
38130         var c = Roo.get(els.close, true);
38131         c.dom.title = this.closeText;
38132         c.addClassOnOver("close-over");
38133         c.on("click", this.closeClick, this);
38134      }
38135
38136     this.addEvents({
38137          /**
38138          * @event activate
38139          * Fires when this tab becomes the active tab.
38140          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38141          * @param {Roo.TabPanelItem} this
38142          */
38143         "activate": true,
38144         /**
38145          * @event beforeclose
38146          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38147          * @param {Roo.TabPanelItem} this
38148          * @param {Object} e Set cancel to true on this object to cancel the close.
38149          */
38150         "beforeclose": true,
38151         /**
38152          * @event close
38153          * Fires when this tab is closed.
38154          * @param {Roo.TabPanelItem} this
38155          */
38156          "close": true,
38157         /**
38158          * @event deactivate
38159          * Fires when this tab is no longer the active tab.
38160          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38161          * @param {Roo.TabPanelItem} this
38162          */
38163          "deactivate" : true
38164     });
38165     this.hidden = false;
38166
38167     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38168 };
38169
38170 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38171            {
38172     purgeListeners : function(){
38173        Roo.util.Observable.prototype.purgeListeners.call(this);
38174        this.el.removeAllListeners();
38175     },
38176     /**
38177      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38178      */
38179     show : function(){
38180         this.pnode.addClass("active");
38181         this.showAction();
38182         if(Roo.isOpera){
38183             this.tabPanel.stripWrap.repaint();
38184         }
38185         this.fireEvent("activate", this.tabPanel, this);
38186     },
38187
38188     /**
38189      * Returns true if this tab is the active tab.
38190      * @return {Boolean}
38191      */
38192     isActive : function(){
38193         return this.tabPanel.getActiveTab() == this;
38194     },
38195
38196     /**
38197      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38198      */
38199     hide : function(){
38200         this.pnode.removeClass("active");
38201         this.hideAction();
38202         this.fireEvent("deactivate", this.tabPanel, this);
38203     },
38204
38205     hideAction : function(){
38206         this.bodyEl.hide();
38207         this.bodyEl.setStyle("position", "absolute");
38208         this.bodyEl.setLeft("-20000px");
38209         this.bodyEl.setTop("-20000px");
38210     },
38211
38212     showAction : function(){
38213         this.bodyEl.setStyle("position", "relative");
38214         this.bodyEl.setTop("");
38215         this.bodyEl.setLeft("");
38216         this.bodyEl.show();
38217     },
38218
38219     /**
38220      * Set the tooltip for the tab.
38221      * @param {String} tooltip The tab's tooltip
38222      */
38223     setTooltip : function(text){
38224         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38225             this.textEl.dom.qtip = text;
38226             this.textEl.dom.removeAttribute('title');
38227         }else{
38228             this.textEl.dom.title = text;
38229         }
38230     },
38231
38232     onTabClick : function(e){
38233         e.preventDefault();
38234         this.tabPanel.activate(this.id);
38235     },
38236
38237     onTabMouseDown : function(e){
38238         e.preventDefault();
38239         this.tabPanel.activate(this.id);
38240     },
38241 /*
38242     getWidth : function(){
38243         return this.inner.getWidth();
38244     },
38245
38246     setWidth : function(width){
38247         var iwidth = width - this.pnode.getPadding("lr");
38248         this.inner.setWidth(iwidth);
38249         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38250         this.pnode.setWidth(width);
38251     },
38252 */
38253     /**
38254      * Show or hide the tab
38255      * @param {Boolean} hidden True to hide or false to show.
38256      */
38257     setHidden : function(hidden){
38258         this.hidden = hidden;
38259         this.pnode.setStyle("display", hidden ? "none" : "");
38260     },
38261
38262     /**
38263      * Returns true if this tab is "hidden"
38264      * @return {Boolean}
38265      */
38266     isHidden : function(){
38267         return this.hidden;
38268     },
38269
38270     /**
38271      * Returns the text for this tab
38272      * @return {String}
38273      */
38274     getText : function(){
38275         return this.text;
38276     },
38277     /*
38278     autoSize : function(){
38279         //this.el.beginMeasure();
38280         this.textEl.setWidth(1);
38281         /*
38282          *  #2804 [new] Tabs in Roojs
38283          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38284          */
38285         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38286         //this.el.endMeasure();
38287     //},
38288
38289     /**
38290      * Sets the text for the tab (Note: this also sets the tooltip text)
38291      * @param {String} text The tab's text and tooltip
38292      */
38293     setText : function(text){
38294         this.text = text;
38295         this.textEl.update(text);
38296         this.setTooltip(text);
38297         //if(!this.tabPanel.resizeTabs){
38298         //    this.autoSize();
38299         //}
38300     },
38301     /**
38302      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38303      */
38304     activate : function(){
38305         this.tabPanel.activate(this.id);
38306     },
38307
38308     /**
38309      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38310      */
38311     disable : function(){
38312         if(this.tabPanel.active != this){
38313             this.disabled = true;
38314             this.pnode.addClass("disabled");
38315         }
38316     },
38317
38318     /**
38319      * Enables this TabPanelItem if it was previously disabled.
38320      */
38321     enable : function(){
38322         this.disabled = false;
38323         this.pnode.removeClass("disabled");
38324     },
38325
38326     /**
38327      * Sets the content for this TabPanelItem.
38328      * @param {String} content The content
38329      * @param {Boolean} loadScripts true to look for and load scripts
38330      */
38331     setContent : function(content, loadScripts){
38332         this.bodyEl.update(content, loadScripts);
38333     },
38334
38335     /**
38336      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38337      * @return {Roo.UpdateManager} The UpdateManager
38338      */
38339     getUpdateManager : function(){
38340         return this.bodyEl.getUpdateManager();
38341     },
38342
38343     /**
38344      * Set a URL to be used to load the content for this TabPanelItem.
38345      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38346      * @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)
38347      * @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)
38348      * @return {Roo.UpdateManager} The UpdateManager
38349      */
38350     setUrl : function(url, params, loadOnce){
38351         if(this.refreshDelegate){
38352             this.un('activate', this.refreshDelegate);
38353         }
38354         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38355         this.on("activate", this.refreshDelegate);
38356         return this.bodyEl.getUpdateManager();
38357     },
38358
38359     /** @private */
38360     _handleRefresh : function(url, params, loadOnce){
38361         if(!loadOnce || !this.loaded){
38362             var updater = this.bodyEl.getUpdateManager();
38363             updater.update(url, params, this._setLoaded.createDelegate(this));
38364         }
38365     },
38366
38367     /**
38368      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38369      *   Will fail silently if the setUrl method has not been called.
38370      *   This does not activate the panel, just updates its content.
38371      */
38372     refresh : function(){
38373         if(this.refreshDelegate){
38374            this.loaded = false;
38375            this.refreshDelegate();
38376         }
38377     },
38378
38379     /** @private */
38380     _setLoaded : function(){
38381         this.loaded = true;
38382     },
38383
38384     /** @private */
38385     closeClick : function(e){
38386         var o = {};
38387         e.stopEvent();
38388         this.fireEvent("beforeclose", this, o);
38389         if(o.cancel !== true){
38390             this.tabPanel.removeTab(this.id);
38391         }
38392     },
38393     /**
38394      * The text displayed in the tooltip for the close icon.
38395      * @type String
38396      */
38397     closeText : "Close this tab"
38398 });
38399 /**
38400 *    This script refer to:
38401 *    Title: International Telephone Input
38402 *    Author: Jack O'Connor
38403 *    Code version:  v12.1.12
38404 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38405 **/
38406
38407 Roo.bootstrap.PhoneInputData = function() {
38408     var d = [
38409       [
38410         "Afghanistan (‫افغانستان‬‎)",
38411         "af",
38412         "93"
38413       ],
38414       [
38415         "Albania (Shqipëri)",
38416         "al",
38417         "355"
38418       ],
38419       [
38420         "Algeria (‫الجزائر‬‎)",
38421         "dz",
38422         "213"
38423       ],
38424       [
38425         "American Samoa",
38426         "as",
38427         "1684"
38428       ],
38429       [
38430         "Andorra",
38431         "ad",
38432         "376"
38433       ],
38434       [
38435         "Angola",
38436         "ao",
38437         "244"
38438       ],
38439       [
38440         "Anguilla",
38441         "ai",
38442         "1264"
38443       ],
38444       [
38445         "Antigua and Barbuda",
38446         "ag",
38447         "1268"
38448       ],
38449       [
38450         "Argentina",
38451         "ar",
38452         "54"
38453       ],
38454       [
38455         "Armenia (Հայաստան)",
38456         "am",
38457         "374"
38458       ],
38459       [
38460         "Aruba",
38461         "aw",
38462         "297"
38463       ],
38464       [
38465         "Australia",
38466         "au",
38467         "61",
38468         0
38469       ],
38470       [
38471         "Austria (Österreich)",
38472         "at",
38473         "43"
38474       ],
38475       [
38476         "Azerbaijan (Azərbaycan)",
38477         "az",
38478         "994"
38479       ],
38480       [
38481         "Bahamas",
38482         "bs",
38483         "1242"
38484       ],
38485       [
38486         "Bahrain (‫البحرين‬‎)",
38487         "bh",
38488         "973"
38489       ],
38490       [
38491         "Bangladesh (বাংলাদেশ)",
38492         "bd",
38493         "880"
38494       ],
38495       [
38496         "Barbados",
38497         "bb",
38498         "1246"
38499       ],
38500       [
38501         "Belarus (Беларусь)",
38502         "by",
38503         "375"
38504       ],
38505       [
38506         "Belgium (België)",
38507         "be",
38508         "32"
38509       ],
38510       [
38511         "Belize",
38512         "bz",
38513         "501"
38514       ],
38515       [
38516         "Benin (Bénin)",
38517         "bj",
38518         "229"
38519       ],
38520       [
38521         "Bermuda",
38522         "bm",
38523         "1441"
38524       ],
38525       [
38526         "Bhutan (འབྲུག)",
38527         "bt",
38528         "975"
38529       ],
38530       [
38531         "Bolivia",
38532         "bo",
38533         "591"
38534       ],
38535       [
38536         "Bosnia and Herzegovina (Босна и Херцеговина)",
38537         "ba",
38538         "387"
38539       ],
38540       [
38541         "Botswana",
38542         "bw",
38543         "267"
38544       ],
38545       [
38546         "Brazil (Brasil)",
38547         "br",
38548         "55"
38549       ],
38550       [
38551         "British Indian Ocean Territory",
38552         "io",
38553         "246"
38554       ],
38555       [
38556         "British Virgin Islands",
38557         "vg",
38558         "1284"
38559       ],
38560       [
38561         "Brunei",
38562         "bn",
38563         "673"
38564       ],
38565       [
38566         "Bulgaria (България)",
38567         "bg",
38568         "359"
38569       ],
38570       [
38571         "Burkina Faso",
38572         "bf",
38573         "226"
38574       ],
38575       [
38576         "Burundi (Uburundi)",
38577         "bi",
38578         "257"
38579       ],
38580       [
38581         "Cambodia (កម្ពុជា)",
38582         "kh",
38583         "855"
38584       ],
38585       [
38586         "Cameroon (Cameroun)",
38587         "cm",
38588         "237"
38589       ],
38590       [
38591         "Canada",
38592         "ca",
38593         "1",
38594         1,
38595         ["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"]
38596       ],
38597       [
38598         "Cape Verde (Kabu Verdi)",
38599         "cv",
38600         "238"
38601       ],
38602       [
38603         "Caribbean Netherlands",
38604         "bq",
38605         "599",
38606         1
38607       ],
38608       [
38609         "Cayman Islands",
38610         "ky",
38611         "1345"
38612       ],
38613       [
38614         "Central African Republic (République centrafricaine)",
38615         "cf",
38616         "236"
38617       ],
38618       [
38619         "Chad (Tchad)",
38620         "td",
38621         "235"
38622       ],
38623       [
38624         "Chile",
38625         "cl",
38626         "56"
38627       ],
38628       [
38629         "China (中国)",
38630         "cn",
38631         "86"
38632       ],
38633       [
38634         "Christmas Island",
38635         "cx",
38636         "61",
38637         2
38638       ],
38639       [
38640         "Cocos (Keeling) Islands",
38641         "cc",
38642         "61",
38643         1
38644       ],
38645       [
38646         "Colombia",
38647         "co",
38648         "57"
38649       ],
38650       [
38651         "Comoros (‫جزر القمر‬‎)",
38652         "km",
38653         "269"
38654       ],
38655       [
38656         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38657         "cd",
38658         "243"
38659       ],
38660       [
38661         "Congo (Republic) (Congo-Brazzaville)",
38662         "cg",
38663         "242"
38664       ],
38665       [
38666         "Cook Islands",
38667         "ck",
38668         "682"
38669       ],
38670       [
38671         "Costa Rica",
38672         "cr",
38673         "506"
38674       ],
38675       [
38676         "Côte d’Ivoire",
38677         "ci",
38678         "225"
38679       ],
38680       [
38681         "Croatia (Hrvatska)",
38682         "hr",
38683         "385"
38684       ],
38685       [
38686         "Cuba",
38687         "cu",
38688         "53"
38689       ],
38690       [
38691         "Curaçao",
38692         "cw",
38693         "599",
38694         0
38695       ],
38696       [
38697         "Cyprus (Κύπρος)",
38698         "cy",
38699         "357"
38700       ],
38701       [
38702         "Czech Republic (Česká republika)",
38703         "cz",
38704         "420"
38705       ],
38706       [
38707         "Denmark (Danmark)",
38708         "dk",
38709         "45"
38710       ],
38711       [
38712         "Djibouti",
38713         "dj",
38714         "253"
38715       ],
38716       [
38717         "Dominica",
38718         "dm",
38719         "1767"
38720       ],
38721       [
38722         "Dominican Republic (República Dominicana)",
38723         "do",
38724         "1",
38725         2,
38726         ["809", "829", "849"]
38727       ],
38728       [
38729         "Ecuador",
38730         "ec",
38731         "593"
38732       ],
38733       [
38734         "Egypt (‫مصر‬‎)",
38735         "eg",
38736         "20"
38737       ],
38738       [
38739         "El Salvador",
38740         "sv",
38741         "503"
38742       ],
38743       [
38744         "Equatorial Guinea (Guinea Ecuatorial)",
38745         "gq",
38746         "240"
38747       ],
38748       [
38749         "Eritrea",
38750         "er",
38751         "291"
38752       ],
38753       [
38754         "Estonia (Eesti)",
38755         "ee",
38756         "372"
38757       ],
38758       [
38759         "Ethiopia",
38760         "et",
38761         "251"
38762       ],
38763       [
38764         "Falkland Islands (Islas Malvinas)",
38765         "fk",
38766         "500"
38767       ],
38768       [
38769         "Faroe Islands (Føroyar)",
38770         "fo",
38771         "298"
38772       ],
38773       [
38774         "Fiji",
38775         "fj",
38776         "679"
38777       ],
38778       [
38779         "Finland (Suomi)",
38780         "fi",
38781         "358",
38782         0
38783       ],
38784       [
38785         "France",
38786         "fr",
38787         "33"
38788       ],
38789       [
38790         "French Guiana (Guyane française)",
38791         "gf",
38792         "594"
38793       ],
38794       [
38795         "French Polynesia (Polynésie française)",
38796         "pf",
38797         "689"
38798       ],
38799       [
38800         "Gabon",
38801         "ga",
38802         "241"
38803       ],
38804       [
38805         "Gambia",
38806         "gm",
38807         "220"
38808       ],
38809       [
38810         "Georgia (საქართველო)",
38811         "ge",
38812         "995"
38813       ],
38814       [
38815         "Germany (Deutschland)",
38816         "de",
38817         "49"
38818       ],
38819       [
38820         "Ghana (Gaana)",
38821         "gh",
38822         "233"
38823       ],
38824       [
38825         "Gibraltar",
38826         "gi",
38827         "350"
38828       ],
38829       [
38830         "Greece (Ελλάδα)",
38831         "gr",
38832         "30"
38833       ],
38834       [
38835         "Greenland (Kalaallit Nunaat)",
38836         "gl",
38837         "299"
38838       ],
38839       [
38840         "Grenada",
38841         "gd",
38842         "1473"
38843       ],
38844       [
38845         "Guadeloupe",
38846         "gp",
38847         "590",
38848         0
38849       ],
38850       [
38851         "Guam",
38852         "gu",
38853         "1671"
38854       ],
38855       [
38856         "Guatemala",
38857         "gt",
38858         "502"
38859       ],
38860       [
38861         "Guernsey",
38862         "gg",
38863         "44",
38864         1
38865       ],
38866       [
38867         "Guinea (Guinée)",
38868         "gn",
38869         "224"
38870       ],
38871       [
38872         "Guinea-Bissau (Guiné Bissau)",
38873         "gw",
38874         "245"
38875       ],
38876       [
38877         "Guyana",
38878         "gy",
38879         "592"
38880       ],
38881       [
38882         "Haiti",
38883         "ht",
38884         "509"
38885       ],
38886       [
38887         "Honduras",
38888         "hn",
38889         "504"
38890       ],
38891       [
38892         "Hong Kong (香港)",
38893         "hk",
38894         "852"
38895       ],
38896       [
38897         "Hungary (Magyarország)",
38898         "hu",
38899         "36"
38900       ],
38901       [
38902         "Iceland (Ísland)",
38903         "is",
38904         "354"
38905       ],
38906       [
38907         "India (भारत)",
38908         "in",
38909         "91"
38910       ],
38911       [
38912         "Indonesia",
38913         "id",
38914         "62"
38915       ],
38916       [
38917         "Iran (‫ایران‬‎)",
38918         "ir",
38919         "98"
38920       ],
38921       [
38922         "Iraq (‫العراق‬‎)",
38923         "iq",
38924         "964"
38925       ],
38926       [
38927         "Ireland",
38928         "ie",
38929         "353"
38930       ],
38931       [
38932         "Isle of Man",
38933         "im",
38934         "44",
38935         2
38936       ],
38937       [
38938         "Israel (‫ישראל‬‎)",
38939         "il",
38940         "972"
38941       ],
38942       [
38943         "Italy (Italia)",
38944         "it",
38945         "39",
38946         0
38947       ],
38948       [
38949         "Jamaica",
38950         "jm",
38951         "1876"
38952       ],
38953       [
38954         "Japan (日本)",
38955         "jp",
38956         "81"
38957       ],
38958       [
38959         "Jersey",
38960         "je",
38961         "44",
38962         3
38963       ],
38964       [
38965         "Jordan (‫الأردن‬‎)",
38966         "jo",
38967         "962"
38968       ],
38969       [
38970         "Kazakhstan (Казахстан)",
38971         "kz",
38972         "7",
38973         1
38974       ],
38975       [
38976         "Kenya",
38977         "ke",
38978         "254"
38979       ],
38980       [
38981         "Kiribati",
38982         "ki",
38983         "686"
38984       ],
38985       [
38986         "Kosovo",
38987         "xk",
38988         "383"
38989       ],
38990       [
38991         "Kuwait (‫الكويت‬‎)",
38992         "kw",
38993         "965"
38994       ],
38995       [
38996         "Kyrgyzstan (Кыргызстан)",
38997         "kg",
38998         "996"
38999       ],
39000       [
39001         "Laos (ລາວ)",
39002         "la",
39003         "856"
39004       ],
39005       [
39006         "Latvia (Latvija)",
39007         "lv",
39008         "371"
39009       ],
39010       [
39011         "Lebanon (‫لبنان‬‎)",
39012         "lb",
39013         "961"
39014       ],
39015       [
39016         "Lesotho",
39017         "ls",
39018         "266"
39019       ],
39020       [
39021         "Liberia",
39022         "lr",
39023         "231"
39024       ],
39025       [
39026         "Libya (‫ليبيا‬‎)",
39027         "ly",
39028         "218"
39029       ],
39030       [
39031         "Liechtenstein",
39032         "li",
39033         "423"
39034       ],
39035       [
39036         "Lithuania (Lietuva)",
39037         "lt",
39038         "370"
39039       ],
39040       [
39041         "Luxembourg",
39042         "lu",
39043         "352"
39044       ],
39045       [
39046         "Macau (澳門)",
39047         "mo",
39048         "853"
39049       ],
39050       [
39051         "Macedonia (FYROM) (Македонија)",
39052         "mk",
39053         "389"
39054       ],
39055       [
39056         "Madagascar (Madagasikara)",
39057         "mg",
39058         "261"
39059       ],
39060       [
39061         "Malawi",
39062         "mw",
39063         "265"
39064       ],
39065       [
39066         "Malaysia",
39067         "my",
39068         "60"
39069       ],
39070       [
39071         "Maldives",
39072         "mv",
39073         "960"
39074       ],
39075       [
39076         "Mali",
39077         "ml",
39078         "223"
39079       ],
39080       [
39081         "Malta",
39082         "mt",
39083         "356"
39084       ],
39085       [
39086         "Marshall Islands",
39087         "mh",
39088         "692"
39089       ],
39090       [
39091         "Martinique",
39092         "mq",
39093         "596"
39094       ],
39095       [
39096         "Mauritania (‫موريتانيا‬‎)",
39097         "mr",
39098         "222"
39099       ],
39100       [
39101         "Mauritius (Moris)",
39102         "mu",
39103         "230"
39104       ],
39105       [
39106         "Mayotte",
39107         "yt",
39108         "262",
39109         1
39110       ],
39111       [
39112         "Mexico (México)",
39113         "mx",
39114         "52"
39115       ],
39116       [
39117         "Micronesia",
39118         "fm",
39119         "691"
39120       ],
39121       [
39122         "Moldova (Republica Moldova)",
39123         "md",
39124         "373"
39125       ],
39126       [
39127         "Monaco",
39128         "mc",
39129         "377"
39130       ],
39131       [
39132         "Mongolia (Монгол)",
39133         "mn",
39134         "976"
39135       ],
39136       [
39137         "Montenegro (Crna Gora)",
39138         "me",
39139         "382"
39140       ],
39141       [
39142         "Montserrat",
39143         "ms",
39144         "1664"
39145       ],
39146       [
39147         "Morocco (‫المغرب‬‎)",
39148         "ma",
39149         "212",
39150         0
39151       ],
39152       [
39153         "Mozambique (Moçambique)",
39154         "mz",
39155         "258"
39156       ],
39157       [
39158         "Myanmar (Burma) (မြန်မာ)",
39159         "mm",
39160         "95"
39161       ],
39162       [
39163         "Namibia (Namibië)",
39164         "na",
39165         "264"
39166       ],
39167       [
39168         "Nauru",
39169         "nr",
39170         "674"
39171       ],
39172       [
39173         "Nepal (नेपाल)",
39174         "np",
39175         "977"
39176       ],
39177       [
39178         "Netherlands (Nederland)",
39179         "nl",
39180         "31"
39181       ],
39182       [
39183         "New Caledonia (Nouvelle-Calédonie)",
39184         "nc",
39185         "687"
39186       ],
39187       [
39188         "New Zealand",
39189         "nz",
39190         "64"
39191       ],
39192       [
39193         "Nicaragua",
39194         "ni",
39195         "505"
39196       ],
39197       [
39198         "Niger (Nijar)",
39199         "ne",
39200         "227"
39201       ],
39202       [
39203         "Nigeria",
39204         "ng",
39205         "234"
39206       ],
39207       [
39208         "Niue",
39209         "nu",
39210         "683"
39211       ],
39212       [
39213         "Norfolk Island",
39214         "nf",
39215         "672"
39216       ],
39217       [
39218         "North Korea (조선 민주주의 인민 공화국)",
39219         "kp",
39220         "850"
39221       ],
39222       [
39223         "Northern Mariana Islands",
39224         "mp",
39225         "1670"
39226       ],
39227       [
39228         "Norway (Norge)",
39229         "no",
39230         "47",
39231         0
39232       ],
39233       [
39234         "Oman (‫عُمان‬‎)",
39235         "om",
39236         "968"
39237       ],
39238       [
39239         "Pakistan (‫پاکستان‬‎)",
39240         "pk",
39241         "92"
39242       ],
39243       [
39244         "Palau",
39245         "pw",
39246         "680"
39247       ],
39248       [
39249         "Palestine (‫فلسطين‬‎)",
39250         "ps",
39251         "970"
39252       ],
39253       [
39254         "Panama (Panamá)",
39255         "pa",
39256         "507"
39257       ],
39258       [
39259         "Papua New Guinea",
39260         "pg",
39261         "675"
39262       ],
39263       [
39264         "Paraguay",
39265         "py",
39266         "595"
39267       ],
39268       [
39269         "Peru (Perú)",
39270         "pe",
39271         "51"
39272       ],
39273       [
39274         "Philippines",
39275         "ph",
39276         "63"
39277       ],
39278       [
39279         "Poland (Polska)",
39280         "pl",
39281         "48"
39282       ],
39283       [
39284         "Portugal",
39285         "pt",
39286         "351"
39287       ],
39288       [
39289         "Puerto Rico",
39290         "pr",
39291         "1",
39292         3,
39293         ["787", "939"]
39294       ],
39295       [
39296         "Qatar (‫قطر‬‎)",
39297         "qa",
39298         "974"
39299       ],
39300       [
39301         "Réunion (La Réunion)",
39302         "re",
39303         "262",
39304         0
39305       ],
39306       [
39307         "Romania (România)",
39308         "ro",
39309         "40"
39310       ],
39311       [
39312         "Russia (Россия)",
39313         "ru",
39314         "7",
39315         0
39316       ],
39317       [
39318         "Rwanda",
39319         "rw",
39320         "250"
39321       ],
39322       [
39323         "Saint Barthélemy",
39324         "bl",
39325         "590",
39326         1
39327       ],
39328       [
39329         "Saint Helena",
39330         "sh",
39331         "290"
39332       ],
39333       [
39334         "Saint Kitts and Nevis",
39335         "kn",
39336         "1869"
39337       ],
39338       [
39339         "Saint Lucia",
39340         "lc",
39341         "1758"
39342       ],
39343       [
39344         "Saint Martin (Saint-Martin (partie française))",
39345         "mf",
39346         "590",
39347         2
39348       ],
39349       [
39350         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39351         "pm",
39352         "508"
39353       ],
39354       [
39355         "Saint Vincent and the Grenadines",
39356         "vc",
39357         "1784"
39358       ],
39359       [
39360         "Samoa",
39361         "ws",
39362         "685"
39363       ],
39364       [
39365         "San Marino",
39366         "sm",
39367         "378"
39368       ],
39369       [
39370         "São Tomé and Príncipe (São Tomé e Príncipe)",
39371         "st",
39372         "239"
39373       ],
39374       [
39375         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39376         "sa",
39377         "966"
39378       ],
39379       [
39380         "Senegal (Sénégal)",
39381         "sn",
39382         "221"
39383       ],
39384       [
39385         "Serbia (Србија)",
39386         "rs",
39387         "381"
39388       ],
39389       [
39390         "Seychelles",
39391         "sc",
39392         "248"
39393       ],
39394       [
39395         "Sierra Leone",
39396         "sl",
39397         "232"
39398       ],
39399       [
39400         "Singapore",
39401         "sg",
39402         "65"
39403       ],
39404       [
39405         "Sint Maarten",
39406         "sx",
39407         "1721"
39408       ],
39409       [
39410         "Slovakia (Slovensko)",
39411         "sk",
39412         "421"
39413       ],
39414       [
39415         "Slovenia (Slovenija)",
39416         "si",
39417         "386"
39418       ],
39419       [
39420         "Solomon Islands",
39421         "sb",
39422         "677"
39423       ],
39424       [
39425         "Somalia (Soomaaliya)",
39426         "so",
39427         "252"
39428       ],
39429       [
39430         "South Africa",
39431         "za",
39432         "27"
39433       ],
39434       [
39435         "South Korea (대한민국)",
39436         "kr",
39437         "82"
39438       ],
39439       [
39440         "South Sudan (‫جنوب السودان‬‎)",
39441         "ss",
39442         "211"
39443       ],
39444       [
39445         "Spain (España)",
39446         "es",
39447         "34"
39448       ],
39449       [
39450         "Sri Lanka (ශ්‍රී ලංකාව)",
39451         "lk",
39452         "94"
39453       ],
39454       [
39455         "Sudan (‫السودان‬‎)",
39456         "sd",
39457         "249"
39458       ],
39459       [
39460         "Suriname",
39461         "sr",
39462         "597"
39463       ],
39464       [
39465         "Svalbard and Jan Mayen",
39466         "sj",
39467         "47",
39468         1
39469       ],
39470       [
39471         "Swaziland",
39472         "sz",
39473         "268"
39474       ],
39475       [
39476         "Sweden (Sverige)",
39477         "se",
39478         "46"
39479       ],
39480       [
39481         "Switzerland (Schweiz)",
39482         "ch",
39483         "41"
39484       ],
39485       [
39486         "Syria (‫سوريا‬‎)",
39487         "sy",
39488         "963"
39489       ],
39490       [
39491         "Taiwan (台灣)",
39492         "tw",
39493         "886"
39494       ],
39495       [
39496         "Tajikistan",
39497         "tj",
39498         "992"
39499       ],
39500       [
39501         "Tanzania",
39502         "tz",
39503         "255"
39504       ],
39505       [
39506         "Thailand (ไทย)",
39507         "th",
39508         "66"
39509       ],
39510       [
39511         "Timor-Leste",
39512         "tl",
39513         "670"
39514       ],
39515       [
39516         "Togo",
39517         "tg",
39518         "228"
39519       ],
39520       [
39521         "Tokelau",
39522         "tk",
39523         "690"
39524       ],
39525       [
39526         "Tonga",
39527         "to",
39528         "676"
39529       ],
39530       [
39531         "Trinidad and Tobago",
39532         "tt",
39533         "1868"
39534       ],
39535       [
39536         "Tunisia (‫تونس‬‎)",
39537         "tn",
39538         "216"
39539       ],
39540       [
39541         "Turkey (Türkiye)",
39542         "tr",
39543         "90"
39544       ],
39545       [
39546         "Turkmenistan",
39547         "tm",
39548         "993"
39549       ],
39550       [
39551         "Turks and Caicos Islands",
39552         "tc",
39553         "1649"
39554       ],
39555       [
39556         "Tuvalu",
39557         "tv",
39558         "688"
39559       ],
39560       [
39561         "U.S. Virgin Islands",
39562         "vi",
39563         "1340"
39564       ],
39565       [
39566         "Uganda",
39567         "ug",
39568         "256"
39569       ],
39570       [
39571         "Ukraine (Україна)",
39572         "ua",
39573         "380"
39574       ],
39575       [
39576         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39577         "ae",
39578         "971"
39579       ],
39580       [
39581         "United Kingdom",
39582         "gb",
39583         "44",
39584         0
39585       ],
39586       [
39587         "United States",
39588         "us",
39589         "1",
39590         0
39591       ],
39592       [
39593         "Uruguay",
39594         "uy",
39595         "598"
39596       ],
39597       [
39598         "Uzbekistan (Oʻzbekiston)",
39599         "uz",
39600         "998"
39601       ],
39602       [
39603         "Vanuatu",
39604         "vu",
39605         "678"
39606       ],
39607       [
39608         "Vatican City (Città del Vaticano)",
39609         "va",
39610         "39",
39611         1
39612       ],
39613       [
39614         "Venezuela",
39615         "ve",
39616         "58"
39617       ],
39618       [
39619         "Vietnam (Việt Nam)",
39620         "vn",
39621         "84"
39622       ],
39623       [
39624         "Wallis and Futuna (Wallis-et-Futuna)",
39625         "wf",
39626         "681"
39627       ],
39628       [
39629         "Western Sahara (‫الصحراء الغربية‬‎)",
39630         "eh",
39631         "212",
39632         1
39633       ],
39634       [
39635         "Yemen (‫اليمن‬‎)",
39636         "ye",
39637         "967"
39638       ],
39639       [
39640         "Zambia",
39641         "zm",
39642         "260"
39643       ],
39644       [
39645         "Zimbabwe",
39646         "zw",
39647         "263"
39648       ],
39649       [
39650         "Åland Islands",
39651         "ax",
39652         "358",
39653         1
39654       ]
39655   ];
39656   
39657   return d;
39658 }/**
39659 *    This script refer to:
39660 *    Title: International Telephone Input
39661 *    Author: Jack O'Connor
39662 *    Code version:  v12.1.12
39663 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39664 **/
39665
39666 /**
39667  * @class Roo.bootstrap.PhoneInput
39668  * @extends Roo.bootstrap.TriggerField
39669  * An input with International dial-code selection
39670  
39671  * @cfg {String} defaultDialCode default '+852'
39672  * @cfg {Array} preferedCountries default []
39673   
39674  * @constructor
39675  * Create a new PhoneInput.
39676  * @param {Object} config Configuration options
39677  */
39678
39679 Roo.bootstrap.PhoneInput = function(config) {
39680     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39681 };
39682
39683 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39684         
39685         listWidth: undefined,
39686         
39687         selectedClass: 'active',
39688         
39689         invalidClass : "has-warning",
39690         
39691         validClass: 'has-success',
39692         
39693         allowed: '0123456789',
39694         
39695         /**
39696          * @cfg {String} defaultDialCode The default dial code when initializing the input
39697          */
39698         defaultDialCode: '+852',
39699         
39700         /**
39701          * @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
39702          */
39703         preferedCountries: false,
39704         
39705         getAutoCreate : function()
39706         {
39707             var data = Roo.bootstrap.PhoneInputData();
39708             var align = this.labelAlign || this.parentLabelAlign();
39709             var id = Roo.id();
39710             
39711             this.allCountries = [];
39712             this.dialCodeMapping = [];
39713             
39714             for (var i = 0; i < data.length; i++) {
39715               var c = data[i];
39716               this.allCountries[i] = {
39717                 name: c[0],
39718                 iso2: c[1],
39719                 dialCode: c[2],
39720                 priority: c[3] || 0,
39721                 areaCodes: c[4] || null
39722               };
39723               this.dialCodeMapping[c[2]] = {
39724                   name: c[0],
39725                   iso2: c[1],
39726                   priority: c[3] || 0,
39727                   areaCodes: c[4] || null
39728               };
39729             }
39730             
39731             var cfg = {
39732                 cls: 'form-group',
39733                 cn: []
39734             };
39735             
39736             var input =  {
39737                 tag: 'input',
39738                 id : id,
39739                 cls : 'form-control tel-input',
39740                 autocomplete: 'new-password'
39741             };
39742             
39743             var hiddenInput = {
39744                 tag: 'input',
39745                 type: 'hidden',
39746                 cls: 'hidden-tel-input'
39747             };
39748             
39749             if (this.name) {
39750                 hiddenInput.name = this.name;
39751             }
39752             
39753             if (this.disabled) {
39754                 input.disabled = true;
39755             }
39756             
39757             var flag_container = {
39758                 tag: 'div',
39759                 cls: 'flag-box',
39760                 cn: [
39761                     {
39762                         tag: 'div',
39763                         cls: 'flag'
39764                     },
39765                     {
39766                         tag: 'div',
39767                         cls: 'caret'
39768                     }
39769                 ]
39770             };
39771             
39772             var box = {
39773                 tag: 'div',
39774                 cls: this.hasFeedback ? 'has-feedback' : '',
39775                 cn: [
39776                     hiddenInput,
39777                     input,
39778                     {
39779                         tag: 'input',
39780                         cls: 'dial-code-holder',
39781                         disabled: true
39782                     }
39783                 ]
39784             };
39785             
39786             var container = {
39787                 cls: 'roo-select2-container input-group',
39788                 cn: [
39789                     flag_container,
39790                     box
39791                 ]
39792             };
39793             
39794             if (this.fieldLabel.length) {
39795                 var indicator = {
39796                     tag: 'i',
39797                     tooltip: 'This field is required'
39798                 };
39799                 
39800                 var label = {
39801                     tag: 'label',
39802                     'for':  id,
39803                     cls: 'control-label',
39804                     cn: []
39805                 };
39806                 
39807                 var label_text = {
39808                     tag: 'span',
39809                     html: this.fieldLabel
39810                 };
39811                 
39812                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39813                 label.cn = [
39814                     indicator,
39815                     label_text
39816                 ];
39817                 
39818                 if(this.indicatorpos == 'right') {
39819                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39820                     label.cn = [
39821                         label_text,
39822                         indicator
39823                     ];
39824                 }
39825                 
39826                 if(align == 'left') {
39827                     container = {
39828                         tag: 'div',
39829                         cn: [
39830                             container
39831                         ]
39832                     };
39833                     
39834                     if(this.labelWidth > 12){
39835                         label.style = "width: " + this.labelWidth + 'px';
39836                     }
39837                     if(this.labelWidth < 13 && this.labelmd == 0){
39838                         this.labelmd = this.labelWidth;
39839                     }
39840                     if(this.labellg > 0){
39841                         label.cls += ' col-lg-' + this.labellg;
39842                         input.cls += ' col-lg-' + (12 - this.labellg);
39843                     }
39844                     if(this.labelmd > 0){
39845                         label.cls += ' col-md-' + this.labelmd;
39846                         container.cls += ' col-md-' + (12 - this.labelmd);
39847                     }
39848                     if(this.labelsm > 0){
39849                         label.cls += ' col-sm-' + this.labelsm;
39850                         container.cls += ' col-sm-' + (12 - this.labelsm);
39851                     }
39852                     if(this.labelxs > 0){
39853                         label.cls += ' col-xs-' + this.labelxs;
39854                         container.cls += ' col-xs-' + (12 - this.labelxs);
39855                     }
39856                 }
39857             }
39858             
39859             cfg.cn = [
39860                 label,
39861                 container
39862             ];
39863             
39864             var settings = this;
39865             
39866             ['xs','sm','md','lg'].map(function(size){
39867                 if (settings[size]) {
39868                     cfg.cls += ' col-' + size + '-' + settings[size];
39869                 }
39870             });
39871             
39872             this.store = new Roo.data.Store({
39873                 proxy : new Roo.data.MemoryProxy({}),
39874                 reader : new Roo.data.JsonReader({
39875                     fields : [
39876                         {
39877                             'name' : 'name',
39878                             'type' : 'string'
39879                         },
39880                         {
39881                             'name' : 'iso2',
39882                             'type' : 'string'
39883                         },
39884                         {
39885                             'name' : 'dialCode',
39886                             'type' : 'string'
39887                         },
39888                         {
39889                             'name' : 'priority',
39890                             'type' : 'string'
39891                         },
39892                         {
39893                             'name' : 'areaCodes',
39894                             'type' : 'string'
39895                         }
39896                     ]
39897                 })
39898             });
39899             
39900             if(!this.preferedCountries) {
39901                 this.preferedCountries = [
39902                     'hk',
39903                     'gb',
39904                     'us'
39905                 ];
39906             }
39907             
39908             var p = this.preferedCountries.reverse();
39909             
39910             if(p) {
39911                 for (var i = 0; i < p.length; i++) {
39912                     for (var j = 0; j < this.allCountries.length; j++) {
39913                         if(this.allCountries[j].iso2 == p[i]) {
39914                             var t = this.allCountries[j];
39915                             this.allCountries.splice(j,1);
39916                             this.allCountries.unshift(t);
39917                         }
39918                     } 
39919                 }
39920             }
39921             
39922             this.store.proxy.data = {
39923                 success: true,
39924                 data: this.allCountries
39925             };
39926             
39927             return cfg;
39928         },
39929         
39930         initEvents : function()
39931         {
39932             this.createList();
39933             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39934             
39935             this.indicator = this.indicatorEl();
39936             this.flag = this.flagEl();
39937             this.dialCodeHolder = this.dialCodeHolderEl();
39938             
39939             this.trigger = this.el.select('div.flag-box',true).first();
39940             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39941             
39942             var _this = this;
39943             
39944             (function(){
39945                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39946                 _this.list.setWidth(lw);
39947             }).defer(100);
39948             
39949             this.list.on('mouseover', this.onViewOver, this);
39950             this.list.on('mousemove', this.onViewMove, this);
39951             this.inputEl().on("keyup", this.onKeyUp, this);
39952             
39953             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39954
39955             this.view = new Roo.View(this.list, this.tpl, {
39956                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39957             });
39958             
39959             this.view.on('click', this.onViewClick, this);
39960             this.setValue(this.defaultDialCode);
39961         },
39962         
39963         onTriggerClick : function(e)
39964         {
39965             Roo.log('trigger click');
39966             if(this.disabled){
39967                 return;
39968             }
39969             
39970             if(this.isExpanded()){
39971                 this.collapse();
39972                 this.hasFocus = false;
39973             }else {
39974                 this.store.load({});
39975                 this.hasFocus = true;
39976                 this.expand();
39977             }
39978         },
39979         
39980         isExpanded : function()
39981         {
39982             return this.list.isVisible();
39983         },
39984         
39985         collapse : function()
39986         {
39987             if(!this.isExpanded()){
39988                 return;
39989             }
39990             this.list.hide();
39991             Roo.get(document).un('mousedown', this.collapseIf, this);
39992             Roo.get(document).un('mousewheel', this.collapseIf, this);
39993             this.fireEvent('collapse', this);
39994             this.validate();
39995         },
39996         
39997         expand : function()
39998         {
39999             Roo.log('expand');
40000
40001             if(this.isExpanded() || !this.hasFocus){
40002                 return;
40003             }
40004             
40005             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40006             this.list.setWidth(lw);
40007             
40008             this.list.show();
40009             this.restrictHeight();
40010             
40011             Roo.get(document).on('mousedown', this.collapseIf, this);
40012             Roo.get(document).on('mousewheel', this.collapseIf, this);
40013             
40014             this.fireEvent('expand', this);
40015         },
40016         
40017         restrictHeight : function()
40018         {
40019             this.list.alignTo(this.inputEl(), this.listAlign);
40020             this.list.alignTo(this.inputEl(), this.listAlign);
40021         },
40022         
40023         onViewOver : function(e, t)
40024         {
40025             if(this.inKeyMode){
40026                 return;
40027             }
40028             var item = this.view.findItemFromChild(t);
40029             
40030             if(item){
40031                 var index = this.view.indexOf(item);
40032                 this.select(index, false);
40033             }
40034         },
40035
40036         // private
40037         onViewClick : function(view, doFocus, el, e)
40038         {
40039             var index = this.view.getSelectedIndexes()[0];
40040             
40041             var r = this.store.getAt(index);
40042             
40043             if(r){
40044                 this.onSelect(r, index);
40045             }
40046             if(doFocus !== false && !this.blockFocus){
40047                 this.inputEl().focus();
40048             }
40049         },
40050         
40051         onViewMove : function(e, t)
40052         {
40053             this.inKeyMode = false;
40054         },
40055         
40056         select : function(index, scrollIntoView)
40057         {
40058             this.selectedIndex = index;
40059             this.view.select(index);
40060             if(scrollIntoView !== false){
40061                 var el = this.view.getNode(index);
40062                 if(el){
40063                     this.list.scrollChildIntoView(el, false);
40064                 }
40065             }
40066         },
40067         
40068         createList : function()
40069         {
40070             this.list = Roo.get(document.body).createChild({
40071                 tag: 'ul',
40072                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40073                 style: 'display:none'
40074             });
40075             
40076             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40077         },
40078         
40079         collapseIf : function(e)
40080         {
40081             var in_combo  = e.within(this.el);
40082             var in_list =  e.within(this.list);
40083             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40084             
40085             if (in_combo || in_list || is_list) {
40086                 return;
40087             }
40088             this.collapse();
40089         },
40090         
40091         onSelect : function(record, index)
40092         {
40093             if(this.fireEvent('beforeselect', this, record, index) !== false){
40094                 
40095                 this.setFlagClass(record.data.iso2);
40096                 this.setDialCode(record.data.dialCode);
40097                 this.hasFocus = false;
40098                 this.collapse();
40099                 this.fireEvent('select', this, record, index);
40100             }
40101         },
40102         
40103         flagEl : function()
40104         {
40105             var flag = this.el.select('div.flag',true).first();
40106             if(!flag){
40107                 return false;
40108             }
40109             return flag;
40110         },
40111         
40112         dialCodeHolderEl : function()
40113         {
40114             var d = this.el.select('input.dial-code-holder',true).first();
40115             if(!d){
40116                 return false;
40117             }
40118             return d;
40119         },
40120         
40121         setDialCode : function(v)
40122         {
40123             this.dialCodeHolder.dom.value = '+'+v;
40124         },
40125         
40126         setFlagClass : function(n)
40127         {
40128             this.flag.dom.className = 'flag '+n;
40129         },
40130         
40131         getValue : function()
40132         {
40133             var v = this.inputEl().getValue();
40134             if(this.dialCodeHolder) {
40135                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40136             }
40137             return v;
40138         },
40139         
40140         setValue : function(v)
40141         {
40142             var d = this.getDialCode(v);
40143             
40144             //invalid dial code
40145             if(v.length == 0 || !d || d.length == 0) {
40146                 if(this.rendered){
40147                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40148                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40149                 }
40150                 return;
40151             }
40152             
40153             //valid dial code
40154             this.setFlagClass(this.dialCodeMapping[d].iso2);
40155             this.setDialCode(d);
40156             this.inputEl().dom.value = v.replace('+'+d,'');
40157             this.hiddenEl().dom.value = this.getValue();
40158             
40159             this.validate();
40160         },
40161         
40162         getDialCode : function(v)
40163         {
40164             v = v ||  '';
40165             
40166             if (v.length == 0) {
40167                 return this.dialCodeHolder.dom.value;
40168             }
40169             
40170             var dialCode = "";
40171             if (v.charAt(0) != "+") {
40172                 return false;
40173             }
40174             var numericChars = "";
40175             for (var i = 1; i < v.length; i++) {
40176               var c = v.charAt(i);
40177               if (!isNaN(c)) {
40178                 numericChars += c;
40179                 if (this.dialCodeMapping[numericChars]) {
40180                   dialCode = v.substr(1, i);
40181                 }
40182                 if (numericChars.length == 4) {
40183                   break;
40184                 }
40185               }
40186             }
40187             return dialCode;
40188         },
40189         
40190         reset : function()
40191         {
40192             this.setValue(this.defaultDialCode);
40193             this.validate();
40194         },
40195         
40196         hiddenEl : function()
40197         {
40198             return this.el.select('input.hidden-tel-input',true).first();
40199         },
40200         
40201         onKeyUp : function(e){
40202             
40203             var k = e.getKey();
40204             var c = e.getCharCode();
40205             
40206             if(
40207                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40208                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40209             ){
40210                 e.stopEvent();
40211             }
40212             
40213             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40214             //     return;
40215             // }
40216             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40217                 e.stopEvent();
40218             }
40219             
40220             this.setValue(this.getValue());
40221         }
40222         
40223 });
40224 /**
40225  * @class Roo.bootstrap.MoneyField
40226  * @extends Roo.bootstrap.ComboBox
40227  * Bootstrap MoneyField class
40228  * 
40229  * @constructor
40230  * Create a new MoneyField.
40231  * @param {Object} config Configuration options
40232  */
40233
40234 Roo.bootstrap.MoneyField = function(config) {
40235     
40236     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40237     
40238 };
40239
40240 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40241     
40242     /**
40243      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40244      */
40245     allowDecimals : true,
40246     /**
40247      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40248      */
40249     decimalSeparator : ".",
40250     /**
40251      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40252      */
40253     decimalPrecision : 0,
40254     /**
40255      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40256      */
40257     allowNegative : true,
40258     /**
40259      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40260      */
40261     allowZero: true,
40262     /**
40263      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40264      */
40265     minValue : Number.NEGATIVE_INFINITY,
40266     /**
40267      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40268      */
40269     maxValue : Number.MAX_VALUE,
40270     /**
40271      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40272      */
40273     minText : "The minimum value for this field is {0}",
40274     /**
40275      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40276      */
40277     maxText : "The maximum value for this field is {0}",
40278     /**
40279      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40280      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40281      */
40282     nanText : "{0} is not a valid number",
40283     /**
40284      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40285      */
40286     castInt : true,
40287     /**
40288      * @cfg {String} defaults currency of the MoneyField
40289      * value should be in lkey
40290      */
40291     defaultCurrency : false,
40292     /**
40293      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40294      */
40295     thousandsDelimiter : false,
40296     
40297     
40298     inputlg : 9,
40299     inputmd : 9,
40300     inputsm : 9,
40301     inputxs : 6,
40302     
40303     store : false,
40304     
40305     getAutoCreate : function()
40306     {
40307         var align = this.labelAlign || this.parentLabelAlign();
40308         
40309         var id = Roo.id();
40310
40311         var cfg = {
40312             cls: 'form-group',
40313             cn: []
40314         };
40315
40316         var input =  {
40317             tag: 'input',
40318             id : id,
40319             cls : 'form-control roo-money-amount-input',
40320             autocomplete: 'new-password'
40321         };
40322         
40323         var hiddenInput = {
40324             tag: 'input',
40325             type: 'hidden',
40326             id: Roo.id(),
40327             cls: 'hidden-number-input'
40328         };
40329         
40330         if (this.name) {
40331             hiddenInput.name = this.name;
40332         }
40333
40334         if (this.disabled) {
40335             input.disabled = true;
40336         }
40337
40338         var clg = 12 - this.inputlg;
40339         var cmd = 12 - this.inputmd;
40340         var csm = 12 - this.inputsm;
40341         var cxs = 12 - this.inputxs;
40342         
40343         var container = {
40344             tag : 'div',
40345             cls : 'row roo-money-field',
40346             cn : [
40347                 {
40348                     tag : 'div',
40349                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40350                     cn : [
40351                         {
40352                             tag : 'div',
40353                             cls: 'roo-select2-container input-group',
40354                             cn: [
40355                                 {
40356                                     tag : 'input',
40357                                     cls : 'form-control roo-money-currency-input',
40358                                     autocomplete: 'new-password',
40359                                     readOnly : 1,
40360                                     name : this.currencyName
40361                                 },
40362                                 {
40363                                     tag :'span',
40364                                     cls : 'input-group-addon',
40365                                     cn : [
40366                                         {
40367                                             tag: 'span',
40368                                             cls: 'caret'
40369                                         }
40370                                     ]
40371                                 }
40372                             ]
40373                         }
40374                     ]
40375                 },
40376                 {
40377                     tag : 'div',
40378                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40379                     cn : [
40380                         {
40381                             tag: 'div',
40382                             cls: this.hasFeedback ? 'has-feedback' : '',
40383                             cn: [
40384                                 input
40385                             ]
40386                         }
40387                     ]
40388                 }
40389             ]
40390             
40391         };
40392         
40393         if (this.fieldLabel.length) {
40394             var indicator = {
40395                 tag: 'i',
40396                 tooltip: 'This field is required'
40397             };
40398
40399             var label = {
40400                 tag: 'label',
40401                 'for':  id,
40402                 cls: 'control-label',
40403                 cn: []
40404             };
40405
40406             var label_text = {
40407                 tag: 'span',
40408                 html: this.fieldLabel
40409             };
40410
40411             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40412             label.cn = [
40413                 indicator,
40414                 label_text
40415             ];
40416
40417             if(this.indicatorpos == 'right') {
40418                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40419                 label.cn = [
40420                     label_text,
40421                     indicator
40422                 ];
40423             }
40424
40425             if(align == 'left') {
40426                 container = {
40427                     tag: 'div',
40428                     cn: [
40429                         container
40430                     ]
40431                 };
40432
40433                 if(this.labelWidth > 12){
40434                     label.style = "width: " + this.labelWidth + 'px';
40435                 }
40436                 if(this.labelWidth < 13 && this.labelmd == 0){
40437                     this.labelmd = this.labelWidth;
40438                 }
40439                 if(this.labellg > 0){
40440                     label.cls += ' col-lg-' + this.labellg;
40441                     input.cls += ' col-lg-' + (12 - this.labellg);
40442                 }
40443                 if(this.labelmd > 0){
40444                     label.cls += ' col-md-' + this.labelmd;
40445                     container.cls += ' col-md-' + (12 - this.labelmd);
40446                 }
40447                 if(this.labelsm > 0){
40448                     label.cls += ' col-sm-' + this.labelsm;
40449                     container.cls += ' col-sm-' + (12 - this.labelsm);
40450                 }
40451                 if(this.labelxs > 0){
40452                     label.cls += ' col-xs-' + this.labelxs;
40453                     container.cls += ' col-xs-' + (12 - this.labelxs);
40454                 }
40455             }
40456         }
40457
40458         cfg.cn = [
40459             label,
40460             container,
40461             hiddenInput
40462         ];
40463         
40464         var settings = this;
40465
40466         ['xs','sm','md','lg'].map(function(size){
40467             if (settings[size]) {
40468                 cfg.cls += ' col-' + size + '-' + settings[size];
40469             }
40470         });
40471         
40472         return cfg;
40473     },
40474     
40475     initEvents : function()
40476     {
40477         this.indicator = this.indicatorEl();
40478         
40479         this.initCurrencyEvent();
40480         
40481         this.initNumberEvent();
40482     },
40483     
40484     initCurrencyEvent : function()
40485     {
40486         if (!this.store) {
40487             throw "can not find store for combo";
40488         }
40489         
40490         this.store = Roo.factory(this.store, Roo.data);
40491         this.store.parent = this;
40492         
40493         this.createList();
40494         
40495         this.triggerEl = this.el.select('.input-group-addon', true).first();
40496         
40497         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40498         
40499         var _this = this;
40500         
40501         (function(){
40502             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40503             _this.list.setWidth(lw);
40504         }).defer(100);
40505         
40506         this.list.on('mouseover', this.onViewOver, this);
40507         this.list.on('mousemove', this.onViewMove, this);
40508         this.list.on('scroll', this.onViewScroll, this);
40509         
40510         if(!this.tpl){
40511             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40512         }
40513         
40514         this.view = new Roo.View(this.list, this.tpl, {
40515             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40516         });
40517         
40518         this.view.on('click', this.onViewClick, this);
40519         
40520         this.store.on('beforeload', this.onBeforeLoad, this);
40521         this.store.on('load', this.onLoad, this);
40522         this.store.on('loadexception', this.onLoadException, this);
40523         
40524         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40525             "up" : function(e){
40526                 this.inKeyMode = true;
40527                 this.selectPrev();
40528             },
40529
40530             "down" : function(e){
40531                 if(!this.isExpanded()){
40532                     this.onTriggerClick();
40533                 }else{
40534                     this.inKeyMode = true;
40535                     this.selectNext();
40536                 }
40537             },
40538
40539             "enter" : function(e){
40540                 this.collapse();
40541                 
40542                 if(this.fireEvent("specialkey", this, e)){
40543                     this.onViewClick(false);
40544                 }
40545                 
40546                 return true;
40547             },
40548
40549             "esc" : function(e){
40550                 this.collapse();
40551             },
40552
40553             "tab" : function(e){
40554                 this.collapse();
40555                 
40556                 if(this.fireEvent("specialkey", this, e)){
40557                     this.onViewClick(false);
40558                 }
40559                 
40560                 return true;
40561             },
40562
40563             scope : this,
40564
40565             doRelay : function(foo, bar, hname){
40566                 if(hname == 'down' || this.scope.isExpanded()){
40567                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40568                 }
40569                 return true;
40570             },
40571
40572             forceKeyDown: true
40573         });
40574         
40575         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40576         
40577     },
40578     
40579     initNumberEvent : function(e)
40580     {
40581         this.inputEl().on("keydown" , this.fireKey,  this);
40582         this.inputEl().on("focus", this.onFocus,  this);
40583         this.inputEl().on("blur", this.onBlur,  this);
40584         
40585         this.inputEl().relayEvent('keyup', this);
40586         
40587         if(this.indicator){
40588             this.indicator.addClass('invisible');
40589         }
40590  
40591         this.originalValue = this.getValue();
40592         
40593         if(this.validationEvent == 'keyup'){
40594             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40595             this.inputEl().on('keyup', this.filterValidation, this);
40596         }
40597         else if(this.validationEvent !== false){
40598             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40599         }
40600         
40601         if(this.selectOnFocus){
40602             this.on("focus", this.preFocus, this);
40603             
40604         }
40605         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40606             this.inputEl().on("keypress", this.filterKeys, this);
40607         } else {
40608             this.inputEl().relayEvent('keypress', this);
40609         }
40610         
40611         var allowed = "0123456789";
40612         
40613         if(this.allowDecimals){
40614             allowed += this.decimalSeparator;
40615         }
40616         
40617         if(this.allowNegative){
40618             allowed += "-";
40619         }
40620         
40621         if(this.thousandsDelimiter) {
40622             allowed += ",";
40623         }
40624         
40625         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40626         
40627         var keyPress = function(e){
40628             
40629             var k = e.getKey();
40630             
40631             var c = e.getCharCode();
40632             
40633             if(
40634                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40635                     allowed.indexOf(String.fromCharCode(c)) === -1
40636             ){
40637                 e.stopEvent();
40638                 return;
40639             }
40640             
40641             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40642                 return;
40643             }
40644             
40645             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40646                 e.stopEvent();
40647             }
40648         };
40649         
40650         this.inputEl().on("keypress", keyPress, this);
40651         
40652     },
40653     
40654     onTriggerClick : function(e)
40655     {   
40656         if(this.disabled){
40657             return;
40658         }
40659         
40660         this.page = 0;
40661         this.loadNext = false;
40662         
40663         if(this.isExpanded()){
40664             this.collapse();
40665             return;
40666         }
40667         
40668         this.hasFocus = true;
40669         
40670         if(this.triggerAction == 'all') {
40671             this.doQuery(this.allQuery, true);
40672             return;
40673         }
40674         
40675         this.doQuery(this.getRawValue());
40676     },
40677     
40678     getCurrency : function()
40679     {   
40680         var v = this.currencyEl().getValue();
40681         
40682         return v;
40683     },
40684     
40685     restrictHeight : function()
40686     {
40687         this.list.alignTo(this.currencyEl(), this.listAlign);
40688         this.list.alignTo(this.currencyEl(), this.listAlign);
40689     },
40690     
40691     onViewClick : function(view, doFocus, el, e)
40692     {
40693         var index = this.view.getSelectedIndexes()[0];
40694         
40695         var r = this.store.getAt(index);
40696         
40697         if(r){
40698             this.onSelect(r, index);
40699         }
40700     },
40701     
40702     onSelect : function(record, index){
40703         
40704         if(this.fireEvent('beforeselect', this, record, index) !== false){
40705         
40706             this.setFromCurrencyData(index > -1 ? record.data : false);
40707             
40708             this.collapse();
40709             
40710             this.fireEvent('select', this, record, index);
40711         }
40712     },
40713     
40714     setFromCurrencyData : function(o)
40715     {
40716         var currency = '';
40717         
40718         this.lastCurrency = o;
40719         
40720         if (this.currencyField) {
40721             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40722         } else {
40723             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40724         }
40725         
40726         this.lastSelectionText = currency;
40727         
40728         //setting default currency
40729         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40730             this.setCurrency(this.defaultCurrency);
40731             return;
40732         }
40733         
40734         this.setCurrency(currency);
40735     },
40736     
40737     setFromData : function(o)
40738     {
40739         var c = {};
40740         
40741         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40742         
40743         this.setFromCurrencyData(c);
40744         
40745         var value = '';
40746         
40747         if (this.name) {
40748             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40749         } else {
40750             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40751         }
40752         
40753         this.setValue(value);
40754         
40755     },
40756     
40757     setCurrency : function(v)
40758     {   
40759         this.currencyValue = v;
40760         
40761         if(this.rendered){
40762             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40763             this.validate();
40764         }
40765     },
40766     
40767     setValue : function(v)
40768     {
40769         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40770         
40771         this.value = v;
40772         
40773         if(this.rendered){
40774             
40775             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40776             
40777             this.inputEl().dom.value = (v == '') ? '' :
40778                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40779             
40780             if(!this.allowZero && v === '0') {
40781                 this.hiddenEl().dom.value = '';
40782                 this.inputEl().dom.value = '';
40783             }
40784             
40785             this.validate();
40786         }
40787     },
40788     
40789     getRawValue : function()
40790     {
40791         var v = this.inputEl().getValue();
40792         
40793         return v;
40794     },
40795     
40796     getValue : function()
40797     {
40798         return this.fixPrecision(this.parseValue(this.getRawValue()));
40799     },
40800     
40801     parseValue : function(value)
40802     {
40803         if(this.thousandsDelimiter) {
40804             value += "";
40805             r = new RegExp(",", "g");
40806             value = value.replace(r, "");
40807         }
40808         
40809         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40810         return isNaN(value) ? '' : value;
40811         
40812     },
40813     
40814     fixPrecision : function(value)
40815     {
40816         if(this.thousandsDelimiter) {
40817             value += "";
40818             r = new RegExp(",", "g");
40819             value = value.replace(r, "");
40820         }
40821         
40822         var nan = isNaN(value);
40823         
40824         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40825             return nan ? '' : value;
40826         }
40827         return parseFloat(value).toFixed(this.decimalPrecision);
40828     },
40829     
40830     decimalPrecisionFcn : function(v)
40831     {
40832         return Math.floor(v);
40833     },
40834     
40835     validateValue : function(value)
40836     {
40837         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40838             return false;
40839         }
40840         
40841         var num = this.parseValue(value);
40842         
40843         if(isNaN(num)){
40844             this.markInvalid(String.format(this.nanText, value));
40845             return false;
40846         }
40847         
40848         if(num < this.minValue){
40849             this.markInvalid(String.format(this.minText, this.minValue));
40850             return false;
40851         }
40852         
40853         if(num > this.maxValue){
40854             this.markInvalid(String.format(this.maxText, this.maxValue));
40855             return false;
40856         }
40857         
40858         return true;
40859     },
40860     
40861     validate : function()
40862     {
40863         if(this.disabled || this.allowBlank){
40864             this.markValid();
40865             return true;
40866         }
40867         
40868         var currency = this.getCurrency();
40869         
40870         if(this.validateValue(this.getRawValue()) && currency.length){
40871             this.markValid();
40872             return true;
40873         }
40874         
40875         this.markInvalid();
40876         return false;
40877     },
40878     
40879     getName: function()
40880     {
40881         return this.name;
40882     },
40883     
40884     beforeBlur : function()
40885     {
40886         if(!this.castInt){
40887             return;
40888         }
40889         
40890         var v = this.parseValue(this.getRawValue());
40891         
40892         if(v || v == 0){
40893             this.setValue(v);
40894         }
40895     },
40896     
40897     onBlur : function()
40898     {
40899         this.beforeBlur();
40900         
40901         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40902             //this.el.removeClass(this.focusClass);
40903         }
40904         
40905         this.hasFocus = false;
40906         
40907         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40908             this.validate();
40909         }
40910         
40911         var v = this.getValue();
40912         
40913         if(String(v) !== String(this.startValue)){
40914             this.fireEvent('change', this, v, this.startValue);
40915         }
40916         
40917         this.fireEvent("blur", this);
40918     },
40919     
40920     inputEl : function()
40921     {
40922         return this.el.select('.roo-money-amount-input', true).first();
40923     },
40924     
40925     currencyEl : function()
40926     {
40927         return this.el.select('.roo-money-currency-input', true).first();
40928     },
40929     
40930     hiddenEl : function()
40931     {
40932         return this.el.select('input.hidden-number-input',true).first();
40933     }
40934     
40935 });