sync
[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         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2228     },
2229      /**
2230      * Displays this menu at a specific xy position
2231      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2232      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2233      */
2234     showAt : function(xy, parentMenu, /* private: */_e){
2235         this.parentMenu = parentMenu;
2236         if(!this.el){
2237             this.render();
2238         }
2239         if(_e !== false){
2240             this.fireEvent("beforeshow", this);
2241             //xy = this.el.adjustForConstraints(xy);
2242         }
2243         
2244         //this.el.show();
2245         this.hideMenuItems();
2246         this.hidden = false;
2247         this.triggerEl.addClass('open');
2248         
2249         // reassign x when hitting right
2250         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2251             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2252         }
2253         
2254         // reassign y when hitting bottom
2255         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2256             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2257         }
2258         
2259         // but the list may align on trigger left or trigger top... should it be a properity?
2260         
2261         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2262             this.el.setXY(xy);
2263         }
2264         
2265         this.focus();
2266         this.fireEvent("show", this);
2267     },
2268     
2269     focus : function(){
2270         return;
2271         if(!this.hidden){
2272             this.doFocus.defer(50, this);
2273         }
2274     },
2275
2276     doFocus : function(){
2277         if(!this.hidden){
2278             this.focusEl.focus();
2279         }
2280     },
2281
2282     /**
2283      * Hides this menu and optionally all parent menus
2284      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2285      */
2286     hide : function(deep)
2287     {
2288         
2289         this.hideMenuItems();
2290         if(this.el && this.isVisible()){
2291             this.fireEvent("beforehide", this);
2292             if(this.activeItem){
2293                 this.activeItem.deactivate();
2294                 this.activeItem = null;
2295             }
2296             this.triggerEl.removeClass('open');;
2297             this.hidden = true;
2298             this.fireEvent("hide", this);
2299         }
2300         if(deep === true && this.parentMenu){
2301             this.parentMenu.hide(true);
2302         }
2303     },
2304     
2305     onTriggerClick : function(e)
2306     {
2307         Roo.log('trigger click');
2308         
2309         var target = e.getTarget();
2310         
2311         Roo.log(target.nodeName.toLowerCase());
2312         
2313         if(target.nodeName.toLowerCase() === 'i'){
2314             e.preventDefault();
2315         }
2316         
2317     },
2318     
2319     onTriggerPress  : function(e)
2320     {
2321         Roo.log('trigger press');
2322         //Roo.log(e.getTarget());
2323        // Roo.log(this.triggerEl.dom);
2324        
2325         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2326         var pel = Roo.get(e.getTarget());
2327         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2328             Roo.log('is treeview or dropdown?');
2329             return;
2330         }
2331         
2332         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2333             return;
2334         }
2335         
2336         if (this.isVisible()) {
2337             Roo.log('hide');
2338             this.hide();
2339         } else {
2340             Roo.log('show');
2341             this.show(this.triggerEl, false, false);
2342         }
2343         
2344         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2345             e.stopEvent();
2346         }
2347         
2348     },
2349        
2350     
2351     hideMenuItems : function()
2352     {
2353         Roo.log("hide Menu Items");
2354         if (!this.el) { 
2355             return;
2356         }
2357         //$(backdrop).remove()
2358         this.el.select('.open',true).each(function(aa) {
2359             
2360             aa.removeClass('open');
2361           //var parent = getParent($(this))
2362           //var relatedTarget = { relatedTarget: this }
2363           
2364            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2365           //if (e.isDefaultPrevented()) return
2366            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2367         });
2368     },
2369     addxtypeChild : function (tree, cntr) {
2370         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2371           
2372         this.menuitems.add(comp);
2373         return comp;
2374
2375     },
2376     getEl : function()
2377     {
2378         Roo.log(this.el);
2379         return this.el;
2380     },
2381     
2382     clear : function()
2383     {
2384         this.getEl().dom.innerHTML = '';
2385         this.menuitems.clear();
2386     }
2387 });
2388
2389  
2390  /*
2391  * - LGPL
2392  *
2393  * menu item
2394  * 
2395  */
2396
2397
2398 /**
2399  * @class Roo.bootstrap.MenuItem
2400  * @extends Roo.bootstrap.Component
2401  * Bootstrap MenuItem class
2402  * @cfg {String} html the menu label
2403  * @cfg {String} href the link
2404  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2405  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2406  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2407  * @cfg {String} fa favicon to show on left of menu item.
2408  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2409  * 
2410  * 
2411  * @constructor
2412  * Create a new MenuItem
2413  * @param {Object} config The config object
2414  */
2415
2416
2417 Roo.bootstrap.MenuItem = function(config){
2418     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2419     this.addEvents({
2420         // raw events
2421         /**
2422          * @event click
2423          * The raw click event for the entire grid.
2424          * @param {Roo.bootstrap.MenuItem} this
2425          * @param {Roo.EventObject} e
2426          */
2427         "click" : true
2428     });
2429 };
2430
2431 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2432     
2433     href : false,
2434     html : false,
2435     preventDefault: false,
2436     isContainer : false,
2437     active : false,
2438     fa: false,
2439     
2440     getAutoCreate : function(){
2441         
2442         if(this.isContainer){
2443             return {
2444                 tag: 'li',
2445                 cls: 'dropdown-menu-item'
2446             };
2447         }
2448         var ctag = {
2449             tag: 'span',
2450             html: 'Link'
2451         };
2452         
2453         var anc = {
2454             tag : 'a',
2455             href : '#',
2456             cn : [  ]
2457         };
2458         
2459         if (this.fa !== false) {
2460             anc.cn.push({
2461                 tag : 'i',
2462                 cls : 'fa fa-' + this.fa
2463             });
2464         }
2465         
2466         anc.cn.push(ctag);
2467         
2468         
2469         var cfg= {
2470             tag: 'li',
2471             cls: 'dropdown-menu-item',
2472             cn: [ anc ]
2473         };
2474         if (this.parent().type == 'treeview') {
2475             cfg.cls = 'treeview-menu';
2476         }
2477         if (this.active) {
2478             cfg.cls += ' active';
2479         }
2480         
2481         
2482         
2483         anc.href = this.href || cfg.cn[0].href ;
2484         ctag.html = this.html || cfg.cn[0].html ;
2485         return cfg;
2486     },
2487     
2488     initEvents: function()
2489     {
2490         if (this.parent().type == 'treeview') {
2491             this.el.select('a').on('click', this.onClick, this);
2492         }
2493         
2494         if (this.menu) {
2495             this.menu.parentType = this.xtype;
2496             this.menu.triggerEl = this.el;
2497             this.menu = this.addxtype(Roo.apply({}, this.menu));
2498         }
2499         
2500     },
2501     onClick : function(e)
2502     {
2503         Roo.log('item on click ');
2504         
2505         if(this.preventDefault){
2506             e.preventDefault();
2507         }
2508         //this.parent().hideMenuItems();
2509         
2510         this.fireEvent('click', this, e);
2511     },
2512     getEl : function()
2513     {
2514         return this.el;
2515     } 
2516 });
2517
2518  
2519
2520  /*
2521  * - LGPL
2522  *
2523  * menu separator
2524  * 
2525  */
2526
2527
2528 /**
2529  * @class Roo.bootstrap.MenuSeparator
2530  * @extends Roo.bootstrap.Component
2531  * Bootstrap MenuSeparator class
2532  * 
2533  * @constructor
2534  * Create a new MenuItem
2535  * @param {Object} config The config object
2536  */
2537
2538
2539 Roo.bootstrap.MenuSeparator = function(config){
2540     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2541 };
2542
2543 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2544     
2545     getAutoCreate : function(){
2546         var cfg = {
2547             cls: 'divider',
2548             tag : 'li'
2549         };
2550         
2551         return cfg;
2552     }
2553    
2554 });
2555
2556  
2557
2558  
2559 /*
2560 * Licence: LGPL
2561 */
2562
2563 /**
2564  * @class Roo.bootstrap.Modal
2565  * @extends Roo.bootstrap.Component
2566  * Bootstrap Modal class
2567  * @cfg {String} title Title of dialog
2568  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2569  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2570  * @cfg {Boolean} specificTitle default false
2571  * @cfg {Array} buttons Array of buttons or standard button set..
2572  * @cfg {String} buttonPosition (left|right|center) default right
2573  * @cfg {Boolean} animate default true
2574  * @cfg {Boolean} allow_close default true
2575  * @cfg {Boolean} fitwindow default false
2576  * @cfg {String} size (sm|lg) default empty
2577  *
2578  *
2579  * @constructor
2580  * Create a new Modal Dialog
2581  * @param {Object} config The config object
2582  */
2583
2584 Roo.bootstrap.Modal = function(config){
2585     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2586     this.addEvents({
2587         // raw events
2588         /**
2589          * @event btnclick
2590          * The raw btnclick event for the button
2591          * @param {Roo.EventObject} e
2592          */
2593         "btnclick" : true,
2594         /**
2595          * @event resize
2596          * Fire when dialog resize
2597          * @param {Roo.bootstrap.Modal} this
2598          * @param {Roo.EventObject} e
2599          */
2600         "resize" : true
2601     });
2602     this.buttons = this.buttons || [];
2603
2604     if (this.tmpl) {
2605         this.tmpl = Roo.factory(this.tmpl);
2606     }
2607
2608 };
2609
2610 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2611
2612     title : 'test dialog',
2613
2614     buttons : false,
2615
2616     // set on load...
2617
2618     html: false,
2619
2620     tmp: false,
2621
2622     specificTitle: false,
2623
2624     buttonPosition: 'right',
2625
2626     allow_close : true,
2627
2628     animate : true,
2629
2630     fitwindow: false,
2631
2632
2633      // private
2634     dialogEl: false,
2635     bodyEl:  false,
2636     footerEl:  false,
2637     titleEl:  false,
2638     closeEl:  false,
2639
2640     size: '',
2641
2642
2643     onRender : function(ct, position)
2644     {
2645         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2646
2647         if(!this.el){
2648             var cfg = Roo.apply({},  this.getAutoCreate());
2649             cfg.id = Roo.id();
2650             //if(!cfg.name){
2651             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2652             //}
2653             //if (!cfg.name.length) {
2654             //    delete cfg.name;
2655            // }
2656             if (this.cls) {
2657                 cfg.cls += ' ' + this.cls;
2658             }
2659             if (this.style) {
2660                 cfg.style = this.style;
2661             }
2662             this.el = Roo.get(document.body).createChild(cfg, position);
2663         }
2664         //var type = this.el.dom.type;
2665
2666
2667         if(this.tabIndex !== undefined){
2668             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2669         }
2670
2671         this.dialogEl = this.el.select('.modal-dialog',true).first();
2672         this.bodyEl = this.el.select('.modal-body',true).first();
2673         this.closeEl = this.el.select('.modal-header .close', true).first();
2674         this.headerEl = this.el.select('.modal-header',true).first();
2675         this.titleEl = this.el.select('.modal-title',true).first();
2676         this.footerEl = this.el.select('.modal-footer',true).first();
2677
2678         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2679         
2680         //this.el.addClass("x-dlg-modal");
2681
2682         if (this.buttons.length) {
2683             Roo.each(this.buttons, function(bb) {
2684                 var b = Roo.apply({}, bb);
2685                 b.xns = b.xns || Roo.bootstrap;
2686                 b.xtype = b.xtype || 'Button';
2687                 if (typeof(b.listeners) == 'undefined') {
2688                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2689                 }
2690
2691                 var btn = Roo.factory(b);
2692
2693                 btn.render(this.el.select('.modal-footer div').first());
2694
2695             },this);
2696         }
2697         // render the children.
2698         var nitems = [];
2699
2700         if(typeof(this.items) != 'undefined'){
2701             var items = this.items;
2702             delete this.items;
2703
2704             for(var i =0;i < items.length;i++) {
2705                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2706             }
2707         }
2708
2709         this.items = nitems;
2710
2711         // where are these used - they used to be body/close/footer
2712
2713
2714         this.initEvents();
2715         //this.el.addClass([this.fieldClass, this.cls]);
2716
2717     },
2718
2719     getAutoCreate : function(){
2720
2721
2722         var bdy = {
2723                 cls : 'modal-body',
2724                 html : this.html || ''
2725         };
2726
2727         var title = {
2728             tag: 'h4',
2729             cls : 'modal-title',
2730             html : this.title
2731         };
2732
2733         if(this.specificTitle){
2734             title = this.title;
2735
2736         };
2737
2738         var header = [];
2739         if (this.allow_close) {
2740             header.push({
2741                 tag: 'button',
2742                 cls : 'close',
2743                 html : '&times'
2744             });
2745         }
2746
2747         header.push(title);
2748
2749         var size = '';
2750
2751         if(this.size.length){
2752             size = 'modal-' + this.size;
2753         }
2754
2755         var modal = {
2756             cls: "modal",
2757              cn : [
2758                 {
2759                     cls: "modal-dialog " + size,
2760                     cn : [
2761                         {
2762                             cls : "modal-content",
2763                             cn : [
2764                                 {
2765                                     cls : 'modal-header',
2766                                     cn : header
2767                                 },
2768                                 bdy,
2769                                 {
2770                                     cls : 'modal-footer',
2771                                     cn : [
2772                                         {
2773                                             tag: 'div',
2774                                             cls: 'btn-' + this.buttonPosition
2775                                         }
2776                                     ]
2777
2778                                 }
2779
2780
2781                             ]
2782
2783                         }
2784                     ]
2785
2786                 }
2787             ]
2788         };
2789
2790         if(this.animate){
2791             modal.cls += ' fade';
2792         }
2793
2794         return modal;
2795
2796     },
2797     getChildContainer : function() {
2798
2799          return this.bodyEl;
2800
2801     },
2802     getButtonContainer : function() {
2803          return this.el.select('.modal-footer div',true).first();
2804
2805     },
2806     initEvents : function()
2807     {
2808         if (this.allow_close) {
2809             this.closeEl.on('click', this.hide, this);
2810         }
2811         Roo.EventManager.onWindowResize(this.resize, this, true);
2812
2813
2814     },
2815
2816     resize : function()
2817     {
2818         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2819         if (this.fitwindow) {
2820             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2821             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2822             this.setSize(w,h);
2823         }
2824     },
2825
2826     setSize : function(w,h)
2827     {
2828         if (!w && !h) {
2829             return;
2830         }
2831         this.resizeTo(w,h);
2832     },
2833
2834     show : function() {
2835
2836         if (!this.rendered) {
2837             this.render();
2838         }
2839
2840         //this.el.setStyle('display', 'block');
2841         this.el.removeClass('hideing');        
2842         this.el.addClass('show');
2843  
2844         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2845             var _this = this;
2846             (function(){
2847                 this.el.addClass('in');
2848             }).defer(50, this);
2849         }else{
2850             this.el.addClass('in');
2851
2852         }
2853
2854         // not sure how we can show data in here..
2855         //if (this.tmpl) {
2856         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2857         //}
2858
2859         Roo.get(document.body).addClass("x-body-masked");
2860         
2861         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2862         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2863         this.maskEl.addClass('show');
2864         
2865         this.resize();
2866         
2867         this.fireEvent('show', this);
2868
2869         // set zindex here - otherwise it appears to be ignored...
2870         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2871
2872         (function () {
2873             this.items.forEach( function(e) {
2874                 e.layout ? e.layout() : false;
2875
2876             });
2877         }).defer(100,this);
2878
2879     },
2880     hide : function()
2881     {
2882         if(this.fireEvent("beforehide", this) !== false){
2883             this.maskEl.removeClass('show');
2884             Roo.get(document.body).removeClass("x-body-masked");
2885             this.el.removeClass('in');
2886             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2887
2888             if(this.animate){ // why
2889                 this.el.addClass('hideing');
2890                 (function(){
2891                     if (!this.el.hasClass('hideing')) {
2892                         return; // it's been shown again...
2893                     }
2894                     this.el.removeClass('show');
2895                     this.el.removeClass('hideing');
2896                 }).defer(150,this);
2897                 
2898             }else{
2899                  this.el.removeClass('show');
2900             }
2901             this.fireEvent('hide', this);
2902         }
2903     },
2904     isVisible : function()
2905     {
2906         
2907         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2908         
2909     },
2910
2911     addButton : function(str, cb)
2912     {
2913
2914
2915         var b = Roo.apply({}, { html : str } );
2916         b.xns = b.xns || Roo.bootstrap;
2917         b.xtype = b.xtype || 'Button';
2918         if (typeof(b.listeners) == 'undefined') {
2919             b.listeners = { click : cb.createDelegate(this)  };
2920         }
2921
2922         var btn = Roo.factory(b);
2923
2924         btn.render(this.el.select('.modal-footer div').first());
2925
2926         return btn;
2927
2928     },
2929
2930     setDefaultButton : function(btn)
2931     {
2932         //this.el.select('.modal-footer').()
2933     },
2934     diff : false,
2935
2936     resizeTo: function(w,h)
2937     {
2938         // skip.. ?? why??
2939
2940         this.dialogEl.setWidth(w);
2941         if (this.diff === false) {
2942             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2943         }
2944
2945         this.bodyEl.setHeight(h-this.diff);
2946
2947         this.fireEvent('resize', this);
2948
2949     },
2950     setContentSize  : function(w, h)
2951     {
2952
2953     },
2954     onButtonClick: function(btn,e)
2955     {
2956         //Roo.log([a,b,c]);
2957         this.fireEvent('btnclick', btn.name, e);
2958     },
2959      /**
2960      * Set the title of the Dialog
2961      * @param {String} str new Title
2962      */
2963     setTitle: function(str) {
2964         this.titleEl.dom.innerHTML = str;
2965     },
2966     /**
2967      * Set the body of the Dialog
2968      * @param {String} str new Title
2969      */
2970     setBody: function(str) {
2971         this.bodyEl.dom.innerHTML = str;
2972     },
2973     /**
2974      * Set the body of the Dialog using the template
2975      * @param {Obj} data - apply this data to the template and replace the body contents.
2976      */
2977     applyBody: function(obj)
2978     {
2979         if (!this.tmpl) {
2980             Roo.log("Error - using apply Body without a template");
2981             //code
2982         }
2983         this.tmpl.overwrite(this.bodyEl, obj);
2984     }
2985
2986 });
2987
2988
2989 Roo.apply(Roo.bootstrap.Modal,  {
2990     /**
2991          * Button config that displays a single OK button
2992          * @type Object
2993          */
2994         OK :  [{
2995             name : 'ok',
2996             weight : 'primary',
2997             html : 'OK'
2998         }],
2999         /**
3000          * Button config that displays Yes and No buttons
3001          * @type Object
3002          */
3003         YESNO : [
3004             {
3005                 name  : 'no',
3006                 html : 'No'
3007             },
3008             {
3009                 name  :'yes',
3010                 weight : 'primary',
3011                 html : 'Yes'
3012             }
3013         ],
3014
3015         /**
3016          * Button config that displays OK and Cancel buttons
3017          * @type Object
3018          */
3019         OKCANCEL : [
3020             {
3021                name : 'cancel',
3022                 html : 'Cancel'
3023             },
3024             {
3025                 name : 'ok',
3026                 weight : 'primary',
3027                 html : 'OK'
3028             }
3029         ],
3030         /**
3031          * Button config that displays Yes, No and Cancel buttons
3032          * @type Object
3033          */
3034         YESNOCANCEL : [
3035             {
3036                 name : 'yes',
3037                 weight : 'primary',
3038                 html : 'Yes'
3039             },
3040             {
3041                 name : 'no',
3042                 html : 'No'
3043             },
3044             {
3045                 name : 'cancel',
3046                 html : 'Cancel'
3047             }
3048         ],
3049         
3050         zIndex : 10001
3051 });
3052 /*
3053  * - LGPL
3054  *
3055  * messagebox - can be used as a replace
3056  * 
3057  */
3058 /**
3059  * @class Roo.MessageBox
3060  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3061  * Example usage:
3062  *<pre><code>
3063 // Basic alert:
3064 Roo.Msg.alert('Status', 'Changes saved successfully.');
3065
3066 // Prompt for user data:
3067 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3068     if (btn == 'ok'){
3069         // process text value...
3070     }
3071 });
3072
3073 // Show a dialog using config options:
3074 Roo.Msg.show({
3075    title:'Save Changes?',
3076    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3077    buttons: Roo.Msg.YESNOCANCEL,
3078    fn: processResult,
3079    animEl: 'elId'
3080 });
3081 </code></pre>
3082  * @singleton
3083  */
3084 Roo.bootstrap.MessageBox = function(){
3085     var dlg, opt, mask, waitTimer;
3086     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3087     var buttons, activeTextEl, bwidth;
3088
3089     
3090     // private
3091     var handleButton = function(button){
3092         dlg.hide();
3093         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3094     };
3095
3096     // private
3097     var handleHide = function(){
3098         if(opt && opt.cls){
3099             dlg.el.removeClass(opt.cls);
3100         }
3101         //if(waitTimer){
3102         //    Roo.TaskMgr.stop(waitTimer);
3103         //    waitTimer = null;
3104         //}
3105     };
3106
3107     // private
3108     var updateButtons = function(b){
3109         var width = 0;
3110         if(!b){
3111             buttons["ok"].hide();
3112             buttons["cancel"].hide();
3113             buttons["yes"].hide();
3114             buttons["no"].hide();
3115             //dlg.footer.dom.style.display = 'none';
3116             return width;
3117         }
3118         dlg.footerEl.dom.style.display = '';
3119         for(var k in buttons){
3120             if(typeof buttons[k] != "function"){
3121                 if(b[k]){
3122                     buttons[k].show();
3123                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3124                     width += buttons[k].el.getWidth()+15;
3125                 }else{
3126                     buttons[k].hide();
3127                 }
3128             }
3129         }
3130         return width;
3131     };
3132
3133     // private
3134     var handleEsc = function(d, k, e){
3135         if(opt && opt.closable !== false){
3136             dlg.hide();
3137         }
3138         if(e){
3139             e.stopEvent();
3140         }
3141     };
3142
3143     return {
3144         /**
3145          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3146          * @return {Roo.BasicDialog} The BasicDialog element
3147          */
3148         getDialog : function(){
3149            if(!dlg){
3150                 dlg = new Roo.bootstrap.Modal( {
3151                     //draggable: true,
3152                     //resizable:false,
3153                     //constraintoviewport:false,
3154                     //fixedcenter:true,
3155                     //collapsible : false,
3156                     //shim:true,
3157                     //modal: true,
3158                 //    width: 'auto',
3159                   //  height:100,
3160                     //buttonAlign:"center",
3161                     closeClick : function(){
3162                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3163                             handleButton("no");
3164                         }else{
3165                             handleButton("cancel");
3166                         }
3167                     }
3168                 });
3169                 dlg.render();
3170                 dlg.on("hide", handleHide);
3171                 mask = dlg.mask;
3172                 //dlg.addKeyListener(27, handleEsc);
3173                 buttons = {};
3174                 this.buttons = buttons;
3175                 var bt = this.buttonText;
3176                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3177                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3178                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3179                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3180                 //Roo.log(buttons);
3181                 bodyEl = dlg.bodyEl.createChild({
3182
3183                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3184                         '<textarea class="roo-mb-textarea"></textarea>' +
3185                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3186                 });
3187                 msgEl = bodyEl.dom.firstChild;
3188                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3189                 textboxEl.enableDisplayMode();
3190                 textboxEl.addKeyListener([10,13], function(){
3191                     if(dlg.isVisible() && opt && opt.buttons){
3192                         if(opt.buttons.ok){
3193                             handleButton("ok");
3194                         }else if(opt.buttons.yes){
3195                             handleButton("yes");
3196                         }
3197                     }
3198                 });
3199                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3200                 textareaEl.enableDisplayMode();
3201                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3202                 progressEl.enableDisplayMode();
3203                 
3204                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3205                 var pf = progressEl.dom.firstChild;
3206                 if (pf) {
3207                     pp = Roo.get(pf.firstChild);
3208                     pp.setHeight(pf.offsetHeight);
3209                 }
3210                 
3211             }
3212             return dlg;
3213         },
3214
3215         /**
3216          * Updates the message box body text
3217          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3218          * the XHTML-compliant non-breaking space character '&amp;#160;')
3219          * @return {Roo.MessageBox} This message box
3220          */
3221         updateText : function(text)
3222         {
3223             if(!dlg.isVisible() && !opt.width){
3224                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3225                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3226             }
3227             msgEl.innerHTML = text || '&#160;';
3228       
3229             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3230             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3231             var w = Math.max(
3232                     Math.min(opt.width || cw , this.maxWidth), 
3233                     Math.max(opt.minWidth || this.minWidth, bwidth)
3234             );
3235             if(opt.prompt){
3236                 activeTextEl.setWidth(w);
3237             }
3238             if(dlg.isVisible()){
3239                 dlg.fixedcenter = false;
3240             }
3241             // to big, make it scroll. = But as usual stupid IE does not support
3242             // !important..
3243             
3244             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3245                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3246                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3247             } else {
3248                 bodyEl.dom.style.height = '';
3249                 bodyEl.dom.style.overflowY = '';
3250             }
3251             if (cw > w) {
3252                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3253             } else {
3254                 bodyEl.dom.style.overflowX = '';
3255             }
3256             
3257             dlg.setContentSize(w, bodyEl.getHeight());
3258             if(dlg.isVisible()){
3259                 dlg.fixedcenter = true;
3260             }
3261             return this;
3262         },
3263
3264         /**
3265          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3266          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3267          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3268          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3269          * @return {Roo.MessageBox} This message box
3270          */
3271         updateProgress : function(value, text){
3272             if(text){
3273                 this.updateText(text);
3274             }
3275             
3276             if (pp) { // weird bug on my firefox - for some reason this is not defined
3277                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3278                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3279             }
3280             return this;
3281         },        
3282
3283         /**
3284          * Returns true if the message box is currently displayed
3285          * @return {Boolean} True if the message box is visible, else false
3286          */
3287         isVisible : function(){
3288             return dlg && dlg.isVisible();  
3289         },
3290
3291         /**
3292          * Hides the message box if it is displayed
3293          */
3294         hide : function(){
3295             if(this.isVisible()){
3296                 dlg.hide();
3297             }  
3298         },
3299
3300         /**
3301          * Displays a new message box, or reinitializes an existing message box, based on the config options
3302          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3303          * The following config object properties are supported:
3304          * <pre>
3305 Property    Type             Description
3306 ----------  ---------------  ------------------------------------------------------------------------------------
3307 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3308                                    closes (defaults to undefined)
3309 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3310                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3311 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3312                                    progress and wait dialogs will ignore this property and always hide the
3313                                    close button as they can only be closed programmatically.
3314 cls               String           A custom CSS class to apply to the message box element
3315 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3316                                    displayed (defaults to 75)
3317 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3318                                    function will be btn (the name of the button that was clicked, if applicable,
3319                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3320                                    Progress and wait dialogs will ignore this option since they do not respond to
3321                                    user actions and can only be closed programmatically, so any required function
3322                                    should be called by the same code after it closes the dialog.
3323 icon              String           A CSS class that provides a background image to be used as an icon for
3324                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3325 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3326 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3327 modal             Boolean          False to allow user interaction with the page while the message box is
3328                                    displayed (defaults to true)
3329 msg               String           A string that will replace the existing message box body text (defaults
3330                                    to the XHTML-compliant non-breaking space character '&#160;')
3331 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3332 progress          Boolean          True to display a progress bar (defaults to false)
3333 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3334 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3335 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3336 title             String           The title text
3337 value             String           The string value to set into the active textbox element if displayed
3338 wait              Boolean          True to display a progress bar (defaults to false)
3339 width             Number           The width of the dialog in pixels
3340 </pre>
3341          *
3342          * Example usage:
3343          * <pre><code>
3344 Roo.Msg.show({
3345    title: 'Address',
3346    msg: 'Please enter your address:',
3347    width: 300,
3348    buttons: Roo.MessageBox.OKCANCEL,
3349    multiline: true,
3350    fn: saveAddress,
3351    animEl: 'addAddressBtn'
3352 });
3353 </code></pre>
3354          * @param {Object} config Configuration options
3355          * @return {Roo.MessageBox} This message box
3356          */
3357         show : function(options)
3358         {
3359             
3360             // this causes nightmares if you show one dialog after another
3361             // especially on callbacks..
3362              
3363             if(this.isVisible()){
3364                 
3365                 this.hide();
3366                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3367                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3368                 Roo.log("New Dialog Message:" +  options.msg )
3369                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3370                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3371                 
3372             }
3373             var d = this.getDialog();
3374             opt = options;
3375             d.setTitle(opt.title || "&#160;");
3376             d.closeEl.setDisplayed(opt.closable !== false);
3377             activeTextEl = textboxEl;
3378             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3379             if(opt.prompt){
3380                 if(opt.multiline){
3381                     textboxEl.hide();
3382                     textareaEl.show();
3383                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3384                         opt.multiline : this.defaultTextHeight);
3385                     activeTextEl = textareaEl;
3386                 }else{
3387                     textboxEl.show();
3388                     textareaEl.hide();
3389                 }
3390             }else{
3391                 textboxEl.hide();
3392                 textareaEl.hide();
3393             }
3394             progressEl.setDisplayed(opt.progress === true);
3395             this.updateProgress(0);
3396             activeTextEl.dom.value = opt.value || "";
3397             if(opt.prompt){
3398                 dlg.setDefaultButton(activeTextEl);
3399             }else{
3400                 var bs = opt.buttons;
3401                 var db = null;
3402                 if(bs && bs.ok){
3403                     db = buttons["ok"];
3404                 }else if(bs && bs.yes){
3405                     db = buttons["yes"];
3406                 }
3407                 dlg.setDefaultButton(db);
3408             }
3409             bwidth = updateButtons(opt.buttons);
3410             this.updateText(opt.msg);
3411             if(opt.cls){
3412                 d.el.addClass(opt.cls);
3413             }
3414             d.proxyDrag = opt.proxyDrag === true;
3415             d.modal = opt.modal !== false;
3416             d.mask = opt.modal !== false ? mask : false;
3417             if(!d.isVisible()){
3418                 // force it to the end of the z-index stack so it gets a cursor in FF
3419                 document.body.appendChild(dlg.el.dom);
3420                 d.animateTarget = null;
3421                 d.show(options.animEl);
3422             }
3423             return this;
3424         },
3425
3426         /**
3427          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3428          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3429          * and closing the message box when the process is complete.
3430          * @param {String} title The title bar text
3431          * @param {String} msg The message box body text
3432          * @return {Roo.MessageBox} This message box
3433          */
3434         progress : function(title, msg){
3435             this.show({
3436                 title : title,
3437                 msg : msg,
3438                 buttons: false,
3439                 progress:true,
3440                 closable:false,
3441                 minWidth: this.minProgressWidth,
3442                 modal : true
3443             });
3444             return this;
3445         },
3446
3447         /**
3448          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3449          * If a callback function is passed it will be called after the user clicks the button, and the
3450          * id of the button that was clicked will be passed as the only parameter to the callback
3451          * (could also be the top-right close button).
3452          * @param {String} title The title bar text
3453          * @param {String} msg The message box body text
3454          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3455          * @param {Object} scope (optional) The scope of the callback function
3456          * @return {Roo.MessageBox} This message box
3457          */
3458         alert : function(title, msg, fn, scope)
3459         {
3460             this.show({
3461                 title : title,
3462                 msg : msg,
3463                 buttons: this.OK,
3464                 fn: fn,
3465                 closable : false,
3466                 scope : scope,
3467                 modal : true
3468             });
3469             return this;
3470         },
3471
3472         /**
3473          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3474          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3475          * You are responsible for closing the message box when the process is complete.
3476          * @param {String} msg The message box body text
3477          * @param {String} title (optional) The title bar text
3478          * @return {Roo.MessageBox} This message box
3479          */
3480         wait : function(msg, title){
3481             this.show({
3482                 title : title,
3483                 msg : msg,
3484                 buttons: false,
3485                 closable:false,
3486                 progress:true,
3487                 modal:true,
3488                 width:300,
3489                 wait:true
3490             });
3491             waitTimer = Roo.TaskMgr.start({
3492                 run: function(i){
3493                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3494                 },
3495                 interval: 1000
3496             });
3497             return this;
3498         },
3499
3500         /**
3501          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3502          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3503          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3504          * @param {String} title The title bar text
3505          * @param {String} msg The message box body text
3506          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3507          * @param {Object} scope (optional) The scope of the callback function
3508          * @return {Roo.MessageBox} This message box
3509          */
3510         confirm : function(title, msg, fn, scope){
3511             this.show({
3512                 title : title,
3513                 msg : msg,
3514                 buttons: this.YESNO,
3515                 fn: fn,
3516                 scope : scope,
3517                 modal : true
3518             });
3519             return this;
3520         },
3521
3522         /**
3523          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3524          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3525          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3526          * (could also be the top-right close button) and the text that was entered will be passed as the two
3527          * parameters to the callback.
3528          * @param {String} title The title bar text
3529          * @param {String} msg The message box body text
3530          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3531          * @param {Object} scope (optional) The scope of the callback function
3532          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3533          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3534          * @return {Roo.MessageBox} This message box
3535          */
3536         prompt : function(title, msg, fn, scope, multiline){
3537             this.show({
3538                 title : title,
3539                 msg : msg,
3540                 buttons: this.OKCANCEL,
3541                 fn: fn,
3542                 minWidth:250,
3543                 scope : scope,
3544                 prompt:true,
3545                 multiline: multiline,
3546                 modal : true
3547             });
3548             return this;
3549         },
3550
3551         /**
3552          * Button config that displays a single OK button
3553          * @type Object
3554          */
3555         OK : {ok:true},
3556         /**
3557          * Button config that displays Yes and No buttons
3558          * @type Object
3559          */
3560         YESNO : {yes:true, no:true},
3561         /**
3562          * Button config that displays OK and Cancel buttons
3563          * @type Object
3564          */
3565         OKCANCEL : {ok:true, cancel:true},
3566         /**
3567          * Button config that displays Yes, No and Cancel buttons
3568          * @type Object
3569          */
3570         YESNOCANCEL : {yes:true, no:true, cancel:true},
3571
3572         /**
3573          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3574          * @type Number
3575          */
3576         defaultTextHeight : 75,
3577         /**
3578          * The maximum width in pixels of the message box (defaults to 600)
3579          * @type Number
3580          */
3581         maxWidth : 600,
3582         /**
3583          * The minimum width in pixels of the message box (defaults to 100)
3584          * @type Number
3585          */
3586         minWidth : 100,
3587         /**
3588          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3589          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3590          * @type Number
3591          */
3592         minProgressWidth : 250,
3593         /**
3594          * An object containing the default button text strings that can be overriden for localized language support.
3595          * Supported properties are: ok, cancel, yes and no.
3596          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3597          * @type Object
3598          */
3599         buttonText : {
3600             ok : "OK",
3601             cancel : "Cancel",
3602             yes : "Yes",
3603             no : "No"
3604         }
3605     };
3606 }();
3607
3608 /**
3609  * Shorthand for {@link Roo.MessageBox}
3610  */
3611 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3612 Roo.Msg = Roo.Msg || Roo.MessageBox;
3613 /*
3614  * - LGPL
3615  *
3616  * navbar
3617  * 
3618  */
3619
3620 /**
3621  * @class Roo.bootstrap.Navbar
3622  * @extends Roo.bootstrap.Component
3623  * Bootstrap Navbar class
3624
3625  * @constructor
3626  * Create a new Navbar
3627  * @param {Object} config The config object
3628  */
3629
3630
3631 Roo.bootstrap.Navbar = function(config){
3632     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3633     this.addEvents({
3634         // raw events
3635         /**
3636          * @event beforetoggle
3637          * Fire before toggle the menu
3638          * @param {Roo.EventObject} e
3639          */
3640         "beforetoggle" : true
3641     });
3642 };
3643
3644 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3645     
3646     
3647    
3648     // private
3649     navItems : false,
3650     loadMask : false,
3651     
3652     
3653     getAutoCreate : function(){
3654         
3655         
3656         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3657         
3658     },
3659     
3660     initEvents :function ()
3661     {
3662         //Roo.log(this.el.select('.navbar-toggle',true));
3663         this.el.select('.navbar-toggle',true).on('click', function() {
3664             if(this.fireEvent('beforetoggle', this) !== false){
3665                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3666             }
3667             
3668         }, this);
3669         
3670         var mark = {
3671             tag: "div",
3672             cls:"x-dlg-mask"
3673         };
3674         
3675         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3676         
3677         var size = this.el.getSize();
3678         this.maskEl.setSize(size.width, size.height);
3679         this.maskEl.enableDisplayMode("block");
3680         this.maskEl.hide();
3681         
3682         if(this.loadMask){
3683             this.maskEl.show();
3684         }
3685     },
3686     
3687     
3688     getChildContainer : function()
3689     {
3690         if (this.el.select('.collapse').getCount()) {
3691             return this.el.select('.collapse',true).first();
3692         }
3693         
3694         return this.el;
3695     },
3696     
3697     mask : function()
3698     {
3699         this.maskEl.show();
3700     },
3701     
3702     unmask : function()
3703     {
3704         this.maskEl.hide();
3705     } 
3706     
3707     
3708     
3709     
3710 });
3711
3712
3713
3714  
3715
3716  /*
3717  * - LGPL
3718  *
3719  * navbar
3720  * 
3721  */
3722
3723 /**
3724  * @class Roo.bootstrap.NavSimplebar
3725  * @extends Roo.bootstrap.Navbar
3726  * Bootstrap Sidebar class
3727  *
3728  * @cfg {Boolean} inverse is inverted color
3729  * 
3730  * @cfg {String} type (nav | pills | tabs)
3731  * @cfg {Boolean} arrangement stacked | justified
3732  * @cfg {String} align (left | right) alignment
3733  * 
3734  * @cfg {Boolean} main (true|false) main nav bar? default false
3735  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3736  * 
3737  * @cfg {String} tag (header|footer|nav|div) default is nav 
3738
3739  * 
3740  * 
3741  * 
3742  * @constructor
3743  * Create a new Sidebar
3744  * @param {Object} config The config object
3745  */
3746
3747
3748 Roo.bootstrap.NavSimplebar = function(config){
3749     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3750 };
3751
3752 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3753     
3754     inverse: false,
3755     
3756     type: false,
3757     arrangement: '',
3758     align : false,
3759     
3760     
3761     
3762     main : false,
3763     
3764     
3765     tag : false,
3766     
3767     
3768     getAutoCreate : function(){
3769         
3770         
3771         var cfg = {
3772             tag : this.tag || 'div',
3773             cls : 'navbar'
3774         };
3775           
3776         
3777         cfg.cn = [
3778             {
3779                 cls: 'nav',
3780                 tag : 'ul'
3781             }
3782         ];
3783         
3784          
3785         this.type = this.type || 'nav';
3786         if (['tabs','pills'].indexOf(this.type)!==-1) {
3787             cfg.cn[0].cls += ' nav-' + this.type
3788         
3789         
3790         } else {
3791             if (this.type!=='nav') {
3792                 Roo.log('nav type must be nav/tabs/pills')
3793             }
3794             cfg.cn[0].cls += ' navbar-nav'
3795         }
3796         
3797         
3798         
3799         
3800         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3801             cfg.cn[0].cls += ' nav-' + this.arrangement;
3802         }
3803         
3804         
3805         if (this.align === 'right') {
3806             cfg.cn[0].cls += ' navbar-right';
3807         }
3808         
3809         if (this.inverse) {
3810             cfg.cls += ' navbar-inverse';
3811             
3812         }
3813         
3814         
3815         return cfg;
3816     
3817         
3818     }
3819     
3820     
3821     
3822 });
3823
3824
3825
3826  
3827
3828  
3829        /*
3830  * - LGPL
3831  *
3832  * navbar
3833  * 
3834  */
3835
3836 /**
3837  * @class Roo.bootstrap.NavHeaderbar
3838  * @extends Roo.bootstrap.NavSimplebar
3839  * Bootstrap Sidebar class
3840  *
3841  * @cfg {String} brand what is brand
3842  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3843  * @cfg {String} brand_href href of the brand
3844  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3845  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3846  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3847  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3848  * 
3849  * @constructor
3850  * Create a new Sidebar
3851  * @param {Object} config The config object
3852  */
3853
3854
3855 Roo.bootstrap.NavHeaderbar = function(config){
3856     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3857       
3858 };
3859
3860 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3861     
3862     position: '',
3863     brand: '',
3864     brand_href: false,
3865     srButton : true,
3866     autohide : false,
3867     desktopCenter : false,
3868    
3869     
3870     getAutoCreate : function(){
3871         
3872         var   cfg = {
3873             tag: this.nav || 'nav',
3874             cls: 'navbar',
3875             role: 'navigation',
3876             cn: []
3877         };
3878         
3879         var cn = cfg.cn;
3880         if (this.desktopCenter) {
3881             cn.push({cls : 'container', cn : []});
3882             cn = cn[0].cn;
3883         }
3884         
3885         if(this.srButton){
3886             cn.push({
3887                 tag: 'div',
3888                 cls: 'navbar-header',
3889                 cn: [
3890                     {
3891                         tag: 'button',
3892                         type: 'button',
3893                         cls: 'navbar-toggle',
3894                         'data-toggle': 'collapse',
3895                         cn: [
3896                             {
3897                                 tag: 'span',
3898                                 cls: 'sr-only',
3899                                 html: 'Toggle navigation'
3900                             },
3901                             {
3902                                 tag: 'span',
3903                                 cls: 'icon-bar'
3904                             },
3905                             {
3906                                 tag: 'span',
3907                                 cls: 'icon-bar'
3908                             },
3909                             {
3910                                 tag: 'span',
3911                                 cls: 'icon-bar'
3912                             }
3913                         ]
3914                     }
3915                 ]
3916             });
3917         }
3918         
3919         cn.push({
3920             tag: 'div',
3921             cls: 'collapse navbar-collapse',
3922             cn : []
3923         });
3924         
3925         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3926         
3927         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3928             cfg.cls += ' navbar-' + this.position;
3929             
3930             // tag can override this..
3931             
3932             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3933         }
3934         
3935         if (this.brand !== '') {
3936             cn[0].cn.push({
3937                 tag: 'a',
3938                 href: this.brand_href ? this.brand_href : '#',
3939                 cls: 'navbar-brand',
3940                 cn: [
3941                 this.brand
3942                 ]
3943             });
3944         }
3945         
3946         if(this.main){
3947             cfg.cls += ' main-nav';
3948         }
3949         
3950         
3951         return cfg;
3952
3953         
3954     },
3955     getHeaderChildContainer : function()
3956     {
3957         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3958             return this.el.select('.navbar-header',true).first();
3959         }
3960         
3961         return this.getChildContainer();
3962     },
3963     
3964     
3965     initEvents : function()
3966     {
3967         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3968         
3969         if (this.autohide) {
3970             
3971             var prevScroll = 0;
3972             var ft = this.el;
3973             
3974             Roo.get(document).on('scroll',function(e) {
3975                 var ns = Roo.get(document).getScroll().top;
3976                 var os = prevScroll;
3977                 prevScroll = ns;
3978                 
3979                 if(ns > os){
3980                     ft.removeClass('slideDown');
3981                     ft.addClass('slideUp');
3982                     return;
3983                 }
3984                 ft.removeClass('slideUp');
3985                 ft.addClass('slideDown');
3986                  
3987               
3988           },this);
3989         }
3990     }    
3991     
3992 });
3993
3994
3995
3996  
3997
3998  /*
3999  * - LGPL
4000  *
4001  * navbar
4002  * 
4003  */
4004
4005 /**
4006  * @class Roo.bootstrap.NavSidebar
4007  * @extends Roo.bootstrap.Navbar
4008  * Bootstrap Sidebar class
4009  * 
4010  * @constructor
4011  * Create a new Sidebar
4012  * @param {Object} config The config object
4013  */
4014
4015
4016 Roo.bootstrap.NavSidebar = function(config){
4017     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4018 };
4019
4020 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4021     
4022     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4023     
4024     getAutoCreate : function(){
4025         
4026         
4027         return  {
4028             tag: 'div',
4029             cls: 'sidebar sidebar-nav'
4030         };
4031     
4032         
4033     }
4034     
4035     
4036     
4037 });
4038
4039
4040
4041  
4042
4043  /*
4044  * - LGPL
4045  *
4046  * nav group
4047  * 
4048  */
4049
4050 /**
4051  * @class Roo.bootstrap.NavGroup
4052  * @extends Roo.bootstrap.Component
4053  * Bootstrap NavGroup class
4054  * @cfg {String} align (left|right)
4055  * @cfg {Boolean} inverse
4056  * @cfg {String} type (nav|pills|tab) default nav
4057  * @cfg {String} navId - reference Id for navbar.
4058
4059  * 
4060  * @constructor
4061  * Create a new nav group
4062  * @param {Object} config The config object
4063  */
4064
4065 Roo.bootstrap.NavGroup = function(config){
4066     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4067     this.navItems = [];
4068    
4069     Roo.bootstrap.NavGroup.register(this);
4070      this.addEvents({
4071         /**
4072              * @event changed
4073              * Fires when the active item changes
4074              * @param {Roo.bootstrap.NavGroup} this
4075              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4076              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4077          */
4078         'changed': true
4079      });
4080     
4081 };
4082
4083 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4084     
4085     align: '',
4086     inverse: false,
4087     form: false,
4088     type: 'nav',
4089     navId : '',
4090     // private
4091     
4092     navItems : false, 
4093     
4094     getAutoCreate : function()
4095     {
4096         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4097         
4098         cfg = {
4099             tag : 'ul',
4100             cls: 'nav' 
4101         };
4102         
4103         if (['tabs','pills'].indexOf(this.type)!==-1) {
4104             cfg.cls += ' nav-' + this.type
4105         } else {
4106             if (this.type!=='nav') {
4107                 Roo.log('nav type must be nav/tabs/pills')
4108             }
4109             cfg.cls += ' navbar-nav'
4110         }
4111         
4112         if (this.parent() && this.parent().sidebar) {
4113             cfg = {
4114                 tag: 'ul',
4115                 cls: 'dashboard-menu sidebar-menu'
4116             };
4117             
4118             return cfg;
4119         }
4120         
4121         if (this.form === true) {
4122             cfg = {
4123                 tag: 'form',
4124                 cls: 'navbar-form'
4125             };
4126             
4127             if (this.align === 'right') {
4128                 cfg.cls += ' navbar-right';
4129             } else {
4130                 cfg.cls += ' navbar-left';
4131             }
4132         }
4133         
4134         if (this.align === 'right') {
4135             cfg.cls += ' navbar-right';
4136         }
4137         
4138         if (this.inverse) {
4139             cfg.cls += ' navbar-inverse';
4140             
4141         }
4142         
4143         
4144         return cfg;
4145     },
4146     /**
4147     * sets the active Navigation item
4148     * @param {Roo.bootstrap.NavItem} the new current navitem
4149     */
4150     setActiveItem : function(item)
4151     {
4152         var prev = false;
4153         Roo.each(this.navItems, function(v){
4154             if (v == item) {
4155                 return ;
4156             }
4157             if (v.isActive()) {
4158                 v.setActive(false, true);
4159                 prev = v;
4160                 
4161             }
4162             
4163         });
4164
4165         item.setActive(true, true);
4166         this.fireEvent('changed', this, item, prev);
4167         
4168         
4169     },
4170     /**
4171     * gets the active Navigation item
4172     * @return {Roo.bootstrap.NavItem} the current navitem
4173     */
4174     getActive : function()
4175     {
4176         
4177         var prev = false;
4178         Roo.each(this.navItems, function(v){
4179             
4180             if (v.isActive()) {
4181                 prev = v;
4182                 
4183             }
4184             
4185         });
4186         return prev;
4187     },
4188     
4189     indexOfNav : function()
4190     {
4191         
4192         var prev = false;
4193         Roo.each(this.navItems, function(v,i){
4194             
4195             if (v.isActive()) {
4196                 prev = i;
4197                 
4198             }
4199             
4200         });
4201         return prev;
4202     },
4203     /**
4204     * adds a Navigation item
4205     * @param {Roo.bootstrap.NavItem} the navitem to add
4206     */
4207     addItem : function(cfg)
4208     {
4209         var cn = new Roo.bootstrap.NavItem(cfg);
4210         this.register(cn);
4211         cn.parentId = this.id;
4212         cn.onRender(this.el, null);
4213         return cn;
4214     },
4215     /**
4216     * register a Navigation item
4217     * @param {Roo.bootstrap.NavItem} the navitem to add
4218     */
4219     register : function(item)
4220     {
4221         this.navItems.push( item);
4222         item.navId = this.navId;
4223     
4224     },
4225     
4226     /**
4227     * clear all the Navigation item
4228     */
4229    
4230     clearAll : function()
4231     {
4232         this.navItems = [];
4233         this.el.dom.innerHTML = '';
4234     },
4235     
4236     getNavItem: function(tabId)
4237     {
4238         var ret = false;
4239         Roo.each(this.navItems, function(e) {
4240             if (e.tabId == tabId) {
4241                ret =  e;
4242                return false;
4243             }
4244             return true;
4245             
4246         });
4247         return ret;
4248     },
4249     
4250     setActiveNext : function()
4251     {
4252         var i = this.indexOfNav(this.getActive());
4253         if (i > this.navItems.length) {
4254             return;
4255         }
4256         this.setActiveItem(this.navItems[i+1]);
4257     },
4258     setActivePrev : function()
4259     {
4260         var i = this.indexOfNav(this.getActive());
4261         if (i  < 1) {
4262             return;
4263         }
4264         this.setActiveItem(this.navItems[i-1]);
4265     },
4266     clearWasActive : function(except) {
4267         Roo.each(this.navItems, function(e) {
4268             if (e.tabId != except.tabId && e.was_active) {
4269                e.was_active = false;
4270                return false;
4271             }
4272             return true;
4273             
4274         });
4275     },
4276     getWasActive : function ()
4277     {
4278         var r = false;
4279         Roo.each(this.navItems, function(e) {
4280             if (e.was_active) {
4281                r = e;
4282                return false;
4283             }
4284             return true;
4285             
4286         });
4287         return r;
4288     }
4289     
4290     
4291 });
4292
4293  
4294 Roo.apply(Roo.bootstrap.NavGroup, {
4295     
4296     groups: {},
4297      /**
4298     * register a Navigation Group
4299     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4300     */
4301     register : function(navgrp)
4302     {
4303         this.groups[navgrp.navId] = navgrp;
4304         
4305     },
4306     /**
4307     * fetch a Navigation Group based on the navigation ID
4308     * @param {string} the navgroup to add
4309     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4310     */
4311     get: function(navId) {
4312         if (typeof(this.groups[navId]) == 'undefined') {
4313             return false;
4314             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4315         }
4316         return this.groups[navId] ;
4317     }
4318     
4319     
4320     
4321 });
4322
4323  /*
4324  * - LGPL
4325  *
4326  * row
4327  * 
4328  */
4329
4330 /**
4331  * @class Roo.bootstrap.NavItem
4332  * @extends Roo.bootstrap.Component
4333  * Bootstrap Navbar.NavItem class
4334  * @cfg {String} href  link to
4335  * @cfg {String} html content of button
4336  * @cfg {String} badge text inside badge
4337  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4338  * @cfg {String} glyphicon name of glyphicon
4339  * @cfg {String} icon name of font awesome icon
4340  * @cfg {Boolean} active Is item active
4341  * @cfg {Boolean} disabled Is item disabled
4342  
4343  * @cfg {Boolean} preventDefault (true | false) default false
4344  * @cfg {String} tabId the tab that this item activates.
4345  * @cfg {String} tagtype (a|span) render as a href or span?
4346  * @cfg {Boolean} animateRef (true|false) link to element default false  
4347   
4348  * @constructor
4349  * Create a new Navbar Item
4350  * @param {Object} config The config object
4351  */
4352 Roo.bootstrap.NavItem = function(config){
4353     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4354     this.addEvents({
4355         // raw events
4356         /**
4357          * @event click
4358          * The raw click event for the entire grid.
4359          * @param {Roo.EventObject} e
4360          */
4361         "click" : true,
4362          /**
4363             * @event changed
4364             * Fires when the active item active state changes
4365             * @param {Roo.bootstrap.NavItem} this
4366             * @param {boolean} state the new state
4367              
4368          */
4369         'changed': true,
4370         /**
4371             * @event scrollto
4372             * Fires when scroll to element
4373             * @param {Roo.bootstrap.NavItem} this
4374             * @param {Object} options
4375             * @param {Roo.EventObject} e
4376              
4377          */
4378         'scrollto': true
4379     });
4380    
4381 };
4382
4383 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4384     
4385     href: false,
4386     html: '',
4387     badge: '',
4388     icon: false,
4389     glyphicon: false,
4390     active: false,
4391     preventDefault : false,
4392     tabId : false,
4393     tagtype : 'a',
4394     disabled : false,
4395     animateRef : false,
4396     was_active : false,
4397     
4398     getAutoCreate : function(){
4399          
4400         var cfg = {
4401             tag: 'li',
4402             cls: 'nav-item'
4403             
4404         };
4405         
4406         if (this.active) {
4407             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4408         }
4409         if (this.disabled) {
4410             cfg.cls += ' disabled';
4411         }
4412         
4413         if (this.href || this.html || this.glyphicon || this.icon) {
4414             cfg.cn = [
4415                 {
4416                     tag: this.tagtype,
4417                     href : this.href || "#",
4418                     html: this.html || ''
4419                 }
4420             ];
4421             
4422             if (this.icon) {
4423                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4424             }
4425
4426             if(this.glyphicon) {
4427                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4428             }
4429             
4430             if (this.menu) {
4431                 
4432                 cfg.cn[0].html += " <span class='caret'></span>";
4433              
4434             }
4435             
4436             if (this.badge !== '') {
4437                  
4438                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4439             }
4440         }
4441         
4442         
4443         
4444         return cfg;
4445     },
4446     initEvents: function() 
4447     {
4448         if (typeof (this.menu) != 'undefined') {
4449             this.menu.parentType = this.xtype;
4450             this.menu.triggerEl = this.el;
4451             this.menu = this.addxtype(Roo.apply({}, this.menu));
4452         }
4453         
4454         this.el.select('a',true).on('click', this.onClick, this);
4455         
4456         if(this.tagtype == 'span'){
4457             this.el.select('span',true).on('click', this.onClick, this);
4458         }
4459        
4460         // at this point parent should be available..
4461         this.parent().register(this);
4462     },
4463     
4464     onClick : function(e)
4465     {
4466         if (e.getTarget('.dropdown-menu-item')) {
4467             // did you click on a menu itemm.... - then don't trigger onclick..
4468             return;
4469         }
4470         
4471         if(
4472                 this.preventDefault || 
4473                 this.href == '#' 
4474         ){
4475             Roo.log("NavItem - prevent Default?");
4476             e.preventDefault();
4477         }
4478         
4479         if (this.disabled) {
4480             return;
4481         }
4482         
4483         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4484         if (tg && tg.transition) {
4485             Roo.log("waiting for the transitionend");
4486             return;
4487         }
4488         
4489         
4490         
4491         //Roo.log("fire event clicked");
4492         if(this.fireEvent('click', this, e) === false){
4493             return;
4494         };
4495         
4496         if(this.tagtype == 'span'){
4497             return;
4498         }
4499         
4500         //Roo.log(this.href);
4501         var ael = this.el.select('a',true).first();
4502         //Roo.log(ael);
4503         
4504         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4505             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4506             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4507                 return; // ignore... - it's a 'hash' to another page.
4508             }
4509             Roo.log("NavItem - prevent Default?");
4510             e.preventDefault();
4511             this.scrollToElement(e);
4512         }
4513         
4514         
4515         var p =  this.parent();
4516    
4517         if (['tabs','pills'].indexOf(p.type)!==-1) {
4518             if (typeof(p.setActiveItem) !== 'undefined') {
4519                 p.setActiveItem(this);
4520             }
4521         }
4522         
4523         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4524         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4525             // remove the collapsed menu expand...
4526             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4527         }
4528     },
4529     
4530     isActive: function () {
4531         return this.active
4532     },
4533     setActive : function(state, fire, is_was_active)
4534     {
4535         if (this.active && !state && this.navId) {
4536             this.was_active = true;
4537             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4538             if (nv) {
4539                 nv.clearWasActive(this);
4540             }
4541             
4542         }
4543         this.active = state;
4544         
4545         if (!state ) {
4546             this.el.removeClass('active');
4547         } else if (!this.el.hasClass('active')) {
4548             this.el.addClass('active');
4549         }
4550         if (fire) {
4551             this.fireEvent('changed', this, state);
4552         }
4553         
4554         // show a panel if it's registered and related..
4555         
4556         if (!this.navId || !this.tabId || !state || is_was_active) {
4557             return;
4558         }
4559         
4560         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4561         if (!tg) {
4562             return;
4563         }
4564         var pan = tg.getPanelByName(this.tabId);
4565         if (!pan) {
4566             return;
4567         }
4568         // if we can not flip to new panel - go back to old nav highlight..
4569         if (false == tg.showPanel(pan)) {
4570             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4571             if (nv) {
4572                 var onav = nv.getWasActive();
4573                 if (onav) {
4574                     onav.setActive(true, false, true);
4575                 }
4576             }
4577             
4578         }
4579         
4580         
4581         
4582     },
4583      // this should not be here...
4584     setDisabled : function(state)
4585     {
4586         this.disabled = state;
4587         if (!state ) {
4588             this.el.removeClass('disabled');
4589         } else if (!this.el.hasClass('disabled')) {
4590             this.el.addClass('disabled');
4591         }
4592         
4593     },
4594     
4595     /**
4596      * Fetch the element to display the tooltip on.
4597      * @return {Roo.Element} defaults to this.el
4598      */
4599     tooltipEl : function()
4600     {
4601         return this.el.select('' + this.tagtype + '', true).first();
4602     },
4603     
4604     scrollToElement : function(e)
4605     {
4606         var c = document.body;
4607         
4608         /*
4609          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4610          */
4611         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4612             c = document.documentElement;
4613         }
4614         
4615         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4616         
4617         if(!target){
4618             return;
4619         }
4620
4621         var o = target.calcOffsetsTo(c);
4622         
4623         var options = {
4624             target : target,
4625             value : o[1]
4626         };
4627         
4628         this.fireEvent('scrollto', this, options, e);
4629         
4630         Roo.get(c).scrollTo('top', options.value, true);
4631         
4632         return;
4633     }
4634 });
4635  
4636
4637  /*
4638  * - LGPL
4639  *
4640  * sidebar item
4641  *
4642  *  li
4643  *    <span> icon </span>
4644  *    <span> text </span>
4645  *    <span>badge </span>
4646  */
4647
4648 /**
4649  * @class Roo.bootstrap.NavSidebarItem
4650  * @extends Roo.bootstrap.NavItem
4651  * Bootstrap Navbar.NavSidebarItem class
4652  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4653  * {Boolean} open is the menu open
4654  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4655  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4656  * {String} buttonSize (sm|md|lg)the extra classes for the button
4657  * {Boolean} showArrow show arrow next to the text (default true)
4658  * @constructor
4659  * Create a new Navbar Button
4660  * @param {Object} config The config object
4661  */
4662 Roo.bootstrap.NavSidebarItem = function(config){
4663     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4664     this.addEvents({
4665         // raw events
4666         /**
4667          * @event click
4668          * The raw click event for the entire grid.
4669          * @param {Roo.EventObject} e
4670          */
4671         "click" : true,
4672          /**
4673             * @event changed
4674             * Fires when the active item active state changes
4675             * @param {Roo.bootstrap.NavSidebarItem} this
4676             * @param {boolean} state the new state
4677              
4678          */
4679         'changed': true
4680     });
4681    
4682 };
4683
4684 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4685     
4686     badgeWeight : 'default',
4687     
4688     open: false,
4689     
4690     buttonView : false,
4691     
4692     buttonWeight : 'default',
4693     
4694     buttonSize : 'md',
4695     
4696     showArrow : true,
4697     
4698     getAutoCreate : function(){
4699         
4700         
4701         var a = {
4702                 tag: 'a',
4703                 href : this.href || '#',
4704                 cls: '',
4705                 html : '',
4706                 cn : []
4707         };
4708         
4709         if(this.buttonView){
4710             a = {
4711                 tag: 'button',
4712                 href : this.href || '#',
4713                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4714                 html : this.html,
4715                 cn : []
4716             };
4717         }
4718         
4719         var cfg = {
4720             tag: 'li',
4721             cls: '',
4722             cn: [ a ]
4723         };
4724         
4725         if (this.active) {
4726             cfg.cls += ' active';
4727         }
4728         
4729         if (this.disabled) {
4730             cfg.cls += ' disabled';
4731         }
4732         if (this.open) {
4733             cfg.cls += ' open x-open';
4734         }
4735         // left icon..
4736         if (this.glyphicon || this.icon) {
4737             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4738             a.cn.push({ tag : 'i', cls : c }) ;
4739         }
4740         
4741         if(!this.buttonView){
4742             var span = {
4743                 tag: 'span',
4744                 html : this.html || ''
4745             };
4746
4747             a.cn.push(span);
4748             
4749         }
4750         
4751         if (this.badge !== '') {
4752             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4753         }
4754         
4755         if (this.menu) {
4756             
4757             if(this.showArrow){
4758                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4759             }
4760             
4761             a.cls += ' dropdown-toggle treeview' ;
4762         }
4763         
4764         return cfg;
4765     },
4766     
4767     initEvents : function()
4768     { 
4769         if (typeof (this.menu) != 'undefined') {
4770             this.menu.parentType = this.xtype;
4771             this.menu.triggerEl = this.el;
4772             this.menu = this.addxtype(Roo.apply({}, this.menu));
4773         }
4774         
4775         this.el.on('click', this.onClick, this);
4776         
4777         if(this.badge !== ''){
4778             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4779         }
4780         
4781     },
4782     
4783     onClick : function(e)
4784     {
4785         if(this.disabled){
4786             e.preventDefault();
4787             return;
4788         }
4789         
4790         if(this.preventDefault){
4791             e.preventDefault();
4792         }
4793         
4794         this.fireEvent('click', this);
4795     },
4796     
4797     disable : function()
4798     {
4799         this.setDisabled(true);
4800     },
4801     
4802     enable : function()
4803     {
4804         this.setDisabled(false);
4805     },
4806     
4807     setDisabled : function(state)
4808     {
4809         if(this.disabled == state){
4810             return;
4811         }
4812         
4813         this.disabled = state;
4814         
4815         if (state) {
4816             this.el.addClass('disabled');
4817             return;
4818         }
4819         
4820         this.el.removeClass('disabled');
4821         
4822         return;
4823     },
4824     
4825     setActive : function(state)
4826     {
4827         if(this.active == state){
4828             return;
4829         }
4830         
4831         this.active = state;
4832         
4833         if (state) {
4834             this.el.addClass('active');
4835             return;
4836         }
4837         
4838         this.el.removeClass('active');
4839         
4840         return;
4841     },
4842     
4843     isActive: function () 
4844     {
4845         return this.active;
4846     },
4847     
4848     setBadge : function(str)
4849     {
4850         if(!this.badgeEl){
4851             return;
4852         }
4853         
4854         this.badgeEl.dom.innerHTML = str;
4855     }
4856     
4857    
4858      
4859  
4860 });
4861  
4862
4863  /*
4864  * - LGPL
4865  *
4866  * row
4867  * 
4868  */
4869
4870 /**
4871  * @class Roo.bootstrap.Row
4872  * @extends Roo.bootstrap.Component
4873  * Bootstrap Row class (contains columns...)
4874  * 
4875  * @constructor
4876  * Create a new Row
4877  * @param {Object} config The config object
4878  */
4879
4880 Roo.bootstrap.Row = function(config){
4881     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4882 };
4883
4884 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4885     
4886     getAutoCreate : function(){
4887        return {
4888             cls: 'row clearfix'
4889        };
4890     }
4891     
4892     
4893 });
4894
4895  
4896
4897  /*
4898  * - LGPL
4899  *
4900  * element
4901  * 
4902  */
4903
4904 /**
4905  * @class Roo.bootstrap.Element
4906  * @extends Roo.bootstrap.Component
4907  * Bootstrap Element class
4908  * @cfg {String} html contents of the element
4909  * @cfg {String} tag tag of the element
4910  * @cfg {String} cls class of the element
4911  * @cfg {Boolean} preventDefault (true|false) default false
4912  * @cfg {Boolean} clickable (true|false) default false
4913  * 
4914  * @constructor
4915  * Create a new Element
4916  * @param {Object} config The config object
4917  */
4918
4919 Roo.bootstrap.Element = function(config){
4920     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4921     
4922     this.addEvents({
4923         // raw events
4924         /**
4925          * @event click
4926          * When a element is chick
4927          * @param {Roo.bootstrap.Element} this
4928          * @param {Roo.EventObject} e
4929          */
4930         "click" : true
4931     });
4932 };
4933
4934 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4935     
4936     tag: 'div',
4937     cls: '',
4938     html: '',
4939     preventDefault: false, 
4940     clickable: false,
4941     
4942     getAutoCreate : function(){
4943         
4944         var cfg = {
4945             tag: this.tag,
4946             // cls: this.cls, double assign in parent class Component.js :: onRender
4947             html: this.html
4948         };
4949         
4950         return cfg;
4951     },
4952     
4953     initEvents: function() 
4954     {
4955         Roo.bootstrap.Element.superclass.initEvents.call(this);
4956         
4957         if(this.clickable){
4958             this.el.on('click', this.onClick, this);
4959         }
4960         
4961     },
4962     
4963     onClick : function(e)
4964     {
4965         if(this.preventDefault){
4966             e.preventDefault();
4967         }
4968         
4969         this.fireEvent('click', this, e);
4970     },
4971     
4972     getValue : function()
4973     {
4974         return this.el.dom.innerHTML;
4975     },
4976     
4977     setValue : function(value)
4978     {
4979         this.el.dom.innerHTML = value;
4980     }
4981    
4982 });
4983
4984  
4985
4986  /*
4987  * - LGPL
4988  *
4989  * pagination
4990  * 
4991  */
4992
4993 /**
4994  * @class Roo.bootstrap.Pagination
4995  * @extends Roo.bootstrap.Component
4996  * Bootstrap Pagination class
4997  * @cfg {String} size xs | sm | md | lg
4998  * @cfg {Boolean} inverse false | true
4999  * 
5000  * @constructor
5001  * Create a new Pagination
5002  * @param {Object} config The config object
5003  */
5004
5005 Roo.bootstrap.Pagination = function(config){
5006     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5007 };
5008
5009 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5010     
5011     cls: false,
5012     size: false,
5013     inverse: false,
5014     
5015     getAutoCreate : function(){
5016         var cfg = {
5017             tag: 'ul',
5018                 cls: 'pagination'
5019         };
5020         if (this.inverse) {
5021             cfg.cls += ' inverse';
5022         }
5023         if (this.html) {
5024             cfg.html=this.html;
5025         }
5026         if (this.cls) {
5027             cfg.cls += " " + this.cls;
5028         }
5029         return cfg;
5030     }
5031    
5032 });
5033
5034  
5035
5036  /*
5037  * - LGPL
5038  *
5039  * Pagination item
5040  * 
5041  */
5042
5043
5044 /**
5045  * @class Roo.bootstrap.PaginationItem
5046  * @extends Roo.bootstrap.Component
5047  * Bootstrap PaginationItem class
5048  * @cfg {String} html text
5049  * @cfg {String} href the link
5050  * @cfg {Boolean} preventDefault (true | false) default true
5051  * @cfg {Boolean} active (true | false) default false
5052  * @cfg {Boolean} disabled default false
5053  * 
5054  * 
5055  * @constructor
5056  * Create a new PaginationItem
5057  * @param {Object} config The config object
5058  */
5059
5060
5061 Roo.bootstrap.PaginationItem = function(config){
5062     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5063     this.addEvents({
5064         // raw events
5065         /**
5066          * @event click
5067          * The raw click event for the entire grid.
5068          * @param {Roo.EventObject} e
5069          */
5070         "click" : true
5071     });
5072 };
5073
5074 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5075     
5076     href : false,
5077     html : false,
5078     preventDefault: true,
5079     active : false,
5080     cls : false,
5081     disabled: false,
5082     
5083     getAutoCreate : function(){
5084         var cfg= {
5085             tag: 'li',
5086             cn: [
5087                 {
5088                     tag : 'a',
5089                     href : this.href ? this.href : '#',
5090                     html : this.html ? this.html : ''
5091                 }
5092             ]
5093         };
5094         
5095         if(this.cls){
5096             cfg.cls = this.cls;
5097         }
5098         
5099         if(this.disabled){
5100             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5101         }
5102         
5103         if(this.active){
5104             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5105         }
5106         
5107         return cfg;
5108     },
5109     
5110     initEvents: function() {
5111         
5112         this.el.on('click', this.onClick, this);
5113         
5114     },
5115     onClick : function(e)
5116     {
5117         Roo.log('PaginationItem on click ');
5118         if(this.preventDefault){
5119             e.preventDefault();
5120         }
5121         
5122         if(this.disabled){
5123             return;
5124         }
5125         
5126         this.fireEvent('click', this, e);
5127     }
5128    
5129 });
5130
5131  
5132
5133  /*
5134  * - LGPL
5135  *
5136  * slider
5137  * 
5138  */
5139
5140
5141 /**
5142  * @class Roo.bootstrap.Slider
5143  * @extends Roo.bootstrap.Component
5144  * Bootstrap Slider class
5145  *    
5146  * @constructor
5147  * Create a new Slider
5148  * @param {Object} config The config object
5149  */
5150
5151 Roo.bootstrap.Slider = function(config){
5152     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5153 };
5154
5155 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5156     
5157     getAutoCreate : function(){
5158         
5159         var cfg = {
5160             tag: 'div',
5161             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5162             cn: [
5163                 {
5164                     tag: 'a',
5165                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5166                 }
5167             ]
5168         };
5169         
5170         return cfg;
5171     }
5172    
5173 });
5174
5175  /*
5176  * Based on:
5177  * Ext JS Library 1.1.1
5178  * Copyright(c) 2006-2007, Ext JS, LLC.
5179  *
5180  * Originally Released Under LGPL - original licence link has changed is not relivant.
5181  *
5182  * Fork - LGPL
5183  * <script type="text/javascript">
5184  */
5185  
5186
5187 /**
5188  * @class Roo.grid.ColumnModel
5189  * @extends Roo.util.Observable
5190  * This is the default implementation of a ColumnModel used by the Grid. It defines
5191  * the columns in the grid.
5192  * <br>Usage:<br>
5193  <pre><code>
5194  var colModel = new Roo.grid.ColumnModel([
5195         {header: "Ticker", width: 60, sortable: true, locked: true},
5196         {header: "Company Name", width: 150, sortable: true},
5197         {header: "Market Cap.", width: 100, sortable: true},
5198         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5199         {header: "Employees", width: 100, sortable: true, resizable: false}
5200  ]);
5201  </code></pre>
5202  * <p>
5203  
5204  * The config options listed for this class are options which may appear in each
5205  * individual column definition.
5206  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5207  * @constructor
5208  * @param {Object} config An Array of column config objects. See this class's
5209  * config objects for details.
5210 */
5211 Roo.grid.ColumnModel = function(config){
5212         /**
5213      * The config passed into the constructor
5214      */
5215     this.config = config;
5216     this.lookup = {};
5217
5218     // if no id, create one
5219     // if the column does not have a dataIndex mapping,
5220     // map it to the order it is in the config
5221     for(var i = 0, len = config.length; i < len; i++){
5222         var c = config[i];
5223         if(typeof c.dataIndex == "undefined"){
5224             c.dataIndex = i;
5225         }
5226         if(typeof c.renderer == "string"){
5227             c.renderer = Roo.util.Format[c.renderer];
5228         }
5229         if(typeof c.id == "undefined"){
5230             c.id = Roo.id();
5231         }
5232         if(c.editor && c.editor.xtype){
5233             c.editor  = Roo.factory(c.editor, Roo.grid);
5234         }
5235         if(c.editor && c.editor.isFormField){
5236             c.editor = new Roo.grid.GridEditor(c.editor);
5237         }
5238         this.lookup[c.id] = c;
5239     }
5240
5241     /**
5242      * The width of columns which have no width specified (defaults to 100)
5243      * @type Number
5244      */
5245     this.defaultWidth = 100;
5246
5247     /**
5248      * Default sortable of columns which have no sortable specified (defaults to false)
5249      * @type Boolean
5250      */
5251     this.defaultSortable = false;
5252
5253     this.addEvents({
5254         /**
5255              * @event widthchange
5256              * Fires when the width of a column changes.
5257              * @param {ColumnModel} this
5258              * @param {Number} columnIndex The column index
5259              * @param {Number} newWidth The new width
5260              */
5261             "widthchange": true,
5262         /**
5263              * @event headerchange
5264              * Fires when the text of a header changes.
5265              * @param {ColumnModel} this
5266              * @param {Number} columnIndex The column index
5267              * @param {Number} newText The new header text
5268              */
5269             "headerchange": true,
5270         /**
5271              * @event hiddenchange
5272              * Fires when a column is hidden or "unhidden".
5273              * @param {ColumnModel} this
5274              * @param {Number} columnIndex The column index
5275              * @param {Boolean} hidden true if hidden, false otherwise
5276              */
5277             "hiddenchange": true,
5278             /**
5279          * @event columnmoved
5280          * Fires when a column is moved.
5281          * @param {ColumnModel} this
5282          * @param {Number} oldIndex
5283          * @param {Number} newIndex
5284          */
5285         "columnmoved" : true,
5286         /**
5287          * @event columlockchange
5288          * Fires when a column's locked state is changed
5289          * @param {ColumnModel} this
5290          * @param {Number} colIndex
5291          * @param {Boolean} locked true if locked
5292          */
5293         "columnlockchange" : true
5294     });
5295     Roo.grid.ColumnModel.superclass.constructor.call(this);
5296 };
5297 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5298     /**
5299      * @cfg {String} header The header text to display in the Grid view.
5300      */
5301     /**
5302      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5303      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5304      * specified, the column's index is used as an index into the Record's data Array.
5305      */
5306     /**
5307      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5308      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5309      */
5310     /**
5311      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5312      * Defaults to the value of the {@link #defaultSortable} property.
5313      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5314      */
5315     /**
5316      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5317      */
5318     /**
5319      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5320      */
5321     /**
5322      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5323      */
5324     /**
5325      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5326      */
5327     /**
5328      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5329      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5330      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5331      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5332      */
5333        /**
5334      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5335      */
5336     /**
5337      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5338      */
5339     /**
5340      * @cfg {String} cursor (Optional)
5341      */
5342     /**
5343      * @cfg {String} tooltip (Optional)
5344      */
5345     /**
5346      * @cfg {Number} xs (Optional)
5347      */
5348     /**
5349      * @cfg {Number} sm (Optional)
5350      */
5351     /**
5352      * @cfg {Number} md (Optional)
5353      */
5354     /**
5355      * @cfg {Number} lg (Optional)
5356      */
5357     /**
5358      * Returns the id of the column at the specified index.
5359      * @param {Number} index The column index
5360      * @return {String} the id
5361      */
5362     getColumnId : function(index){
5363         return this.config[index].id;
5364     },
5365
5366     /**
5367      * Returns the column for a specified id.
5368      * @param {String} id The column id
5369      * @return {Object} the column
5370      */
5371     getColumnById : function(id){
5372         return this.lookup[id];
5373     },
5374
5375     
5376     /**
5377      * Returns the column for a specified dataIndex.
5378      * @param {String} dataIndex The column dataIndex
5379      * @return {Object|Boolean} the column or false if not found
5380      */
5381     getColumnByDataIndex: function(dataIndex){
5382         var index = this.findColumnIndex(dataIndex);
5383         return index > -1 ? this.config[index] : false;
5384     },
5385     
5386     /**
5387      * Returns the index for a specified column id.
5388      * @param {String} id The column id
5389      * @return {Number} the index, or -1 if not found
5390      */
5391     getIndexById : function(id){
5392         for(var i = 0, len = this.config.length; i < len; i++){
5393             if(this.config[i].id == id){
5394                 return i;
5395             }
5396         }
5397         return -1;
5398     },
5399     
5400     /**
5401      * Returns the index for a specified column dataIndex.
5402      * @param {String} dataIndex The column dataIndex
5403      * @return {Number} the index, or -1 if not found
5404      */
5405     
5406     findColumnIndex : function(dataIndex){
5407         for(var i = 0, len = this.config.length; i < len; i++){
5408             if(this.config[i].dataIndex == dataIndex){
5409                 return i;
5410             }
5411         }
5412         return -1;
5413     },
5414     
5415     
5416     moveColumn : function(oldIndex, newIndex){
5417         var c = this.config[oldIndex];
5418         this.config.splice(oldIndex, 1);
5419         this.config.splice(newIndex, 0, c);
5420         this.dataMap = null;
5421         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5422     },
5423
5424     isLocked : function(colIndex){
5425         return this.config[colIndex].locked === true;
5426     },
5427
5428     setLocked : function(colIndex, value, suppressEvent){
5429         if(this.isLocked(colIndex) == value){
5430             return;
5431         }
5432         this.config[colIndex].locked = value;
5433         if(!suppressEvent){
5434             this.fireEvent("columnlockchange", this, colIndex, value);
5435         }
5436     },
5437
5438     getTotalLockedWidth : function(){
5439         var totalWidth = 0;
5440         for(var i = 0; i < this.config.length; i++){
5441             if(this.isLocked(i) && !this.isHidden(i)){
5442                 this.totalWidth += this.getColumnWidth(i);
5443             }
5444         }
5445         return totalWidth;
5446     },
5447
5448     getLockedCount : function(){
5449         for(var i = 0, len = this.config.length; i < len; i++){
5450             if(!this.isLocked(i)){
5451                 return i;
5452             }
5453         }
5454         
5455         return this.config.length;
5456     },
5457
5458     /**
5459      * Returns the number of columns.
5460      * @return {Number}
5461      */
5462     getColumnCount : function(visibleOnly){
5463         if(visibleOnly === true){
5464             var c = 0;
5465             for(var i = 0, len = this.config.length; i < len; i++){
5466                 if(!this.isHidden(i)){
5467                     c++;
5468                 }
5469             }
5470             return c;
5471         }
5472         return this.config.length;
5473     },
5474
5475     /**
5476      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5477      * @param {Function} fn
5478      * @param {Object} scope (optional)
5479      * @return {Array} result
5480      */
5481     getColumnsBy : function(fn, scope){
5482         var r = [];
5483         for(var i = 0, len = this.config.length; i < len; i++){
5484             var c = this.config[i];
5485             if(fn.call(scope||this, c, i) === true){
5486                 r[r.length] = c;
5487             }
5488         }
5489         return r;
5490     },
5491
5492     /**
5493      * Returns true if the specified column is sortable.
5494      * @param {Number} col The column index
5495      * @return {Boolean}
5496      */
5497     isSortable : function(col){
5498         if(typeof this.config[col].sortable == "undefined"){
5499             return this.defaultSortable;
5500         }
5501         return this.config[col].sortable;
5502     },
5503
5504     /**
5505      * Returns the rendering (formatting) function defined for the column.
5506      * @param {Number} col The column index.
5507      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5508      */
5509     getRenderer : function(col){
5510         if(!this.config[col].renderer){
5511             return Roo.grid.ColumnModel.defaultRenderer;
5512         }
5513         return this.config[col].renderer;
5514     },
5515
5516     /**
5517      * Sets the rendering (formatting) function for a column.
5518      * @param {Number} col The column index
5519      * @param {Function} fn The function to use to process the cell's raw data
5520      * to return HTML markup for the grid view. The render function is called with
5521      * the following parameters:<ul>
5522      * <li>Data value.</li>
5523      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5524      * <li>css A CSS style string to apply to the table cell.</li>
5525      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5526      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5527      * <li>Row index</li>
5528      * <li>Column index</li>
5529      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5530      */
5531     setRenderer : function(col, fn){
5532         this.config[col].renderer = fn;
5533     },
5534
5535     /**
5536      * Returns the width for the specified column.
5537      * @param {Number} col The column index
5538      * @return {Number}
5539      */
5540     getColumnWidth : function(col){
5541         return this.config[col].width * 1 || this.defaultWidth;
5542     },
5543
5544     /**
5545      * Sets the width for a column.
5546      * @param {Number} col The column index
5547      * @param {Number} width The new width
5548      */
5549     setColumnWidth : function(col, width, suppressEvent){
5550         this.config[col].width = width;
5551         this.totalWidth = null;
5552         if(!suppressEvent){
5553              this.fireEvent("widthchange", this, col, width);
5554         }
5555     },
5556
5557     /**
5558      * Returns the total width of all columns.
5559      * @param {Boolean} includeHidden True to include hidden column widths
5560      * @return {Number}
5561      */
5562     getTotalWidth : function(includeHidden){
5563         if(!this.totalWidth){
5564             this.totalWidth = 0;
5565             for(var i = 0, len = this.config.length; i < len; i++){
5566                 if(includeHidden || !this.isHidden(i)){
5567                     this.totalWidth += this.getColumnWidth(i);
5568                 }
5569             }
5570         }
5571         return this.totalWidth;
5572     },
5573
5574     /**
5575      * Returns the header for the specified column.
5576      * @param {Number} col The column index
5577      * @return {String}
5578      */
5579     getColumnHeader : function(col){
5580         return this.config[col].header;
5581     },
5582
5583     /**
5584      * Sets the header for a column.
5585      * @param {Number} col The column index
5586      * @param {String} header The new header
5587      */
5588     setColumnHeader : function(col, header){
5589         this.config[col].header = header;
5590         this.fireEvent("headerchange", this, col, header);
5591     },
5592
5593     /**
5594      * Returns the tooltip for the specified column.
5595      * @param {Number} col The column index
5596      * @return {String}
5597      */
5598     getColumnTooltip : function(col){
5599             return this.config[col].tooltip;
5600     },
5601     /**
5602      * Sets the tooltip for a column.
5603      * @param {Number} col The column index
5604      * @param {String} tooltip The new tooltip
5605      */
5606     setColumnTooltip : function(col, tooltip){
5607             this.config[col].tooltip = tooltip;
5608     },
5609
5610     /**
5611      * Returns the dataIndex for the specified column.
5612      * @param {Number} col The column index
5613      * @return {Number}
5614      */
5615     getDataIndex : function(col){
5616         return this.config[col].dataIndex;
5617     },
5618
5619     /**
5620      * Sets the dataIndex for a column.
5621      * @param {Number} col The column index
5622      * @param {Number} dataIndex The new dataIndex
5623      */
5624     setDataIndex : function(col, dataIndex){
5625         this.config[col].dataIndex = dataIndex;
5626     },
5627
5628     
5629     
5630     /**
5631      * Returns true if the cell is editable.
5632      * @param {Number} colIndex The column index
5633      * @param {Number} rowIndex The row index - this is nto actually used..?
5634      * @return {Boolean}
5635      */
5636     isCellEditable : function(colIndex, rowIndex){
5637         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5638     },
5639
5640     /**
5641      * Returns the editor defined for the cell/column.
5642      * return false or null to disable editing.
5643      * @param {Number} colIndex The column index
5644      * @param {Number} rowIndex The row index
5645      * @return {Object}
5646      */
5647     getCellEditor : function(colIndex, rowIndex){
5648         return this.config[colIndex].editor;
5649     },
5650
5651     /**
5652      * Sets if a column is editable.
5653      * @param {Number} col The column index
5654      * @param {Boolean} editable True if the column is editable
5655      */
5656     setEditable : function(col, editable){
5657         this.config[col].editable = editable;
5658     },
5659
5660
5661     /**
5662      * Returns true if the column is hidden.
5663      * @param {Number} colIndex The column index
5664      * @return {Boolean}
5665      */
5666     isHidden : function(colIndex){
5667         return this.config[colIndex].hidden;
5668     },
5669
5670
5671     /**
5672      * Returns true if the column width cannot be changed
5673      */
5674     isFixed : function(colIndex){
5675         return this.config[colIndex].fixed;
5676     },
5677
5678     /**
5679      * Returns true if the column can be resized
5680      * @return {Boolean}
5681      */
5682     isResizable : function(colIndex){
5683         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5684     },
5685     /**
5686      * Sets if a column is hidden.
5687      * @param {Number} colIndex The column index
5688      * @param {Boolean} hidden True if the column is hidden
5689      */
5690     setHidden : function(colIndex, hidden){
5691         this.config[colIndex].hidden = hidden;
5692         this.totalWidth = null;
5693         this.fireEvent("hiddenchange", this, colIndex, hidden);
5694     },
5695
5696     /**
5697      * Sets the editor for a column.
5698      * @param {Number} col The column index
5699      * @param {Object} editor The editor object
5700      */
5701     setEditor : function(col, editor){
5702         this.config[col].editor = editor;
5703     }
5704 });
5705
5706 Roo.grid.ColumnModel.defaultRenderer = function(value)
5707 {
5708     if(typeof value == "object") {
5709         return value;
5710     }
5711         if(typeof value == "string" && value.length < 1){
5712             return "&#160;";
5713         }
5714     
5715         return String.format("{0}", value);
5716 };
5717
5718 // Alias for backwards compatibility
5719 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5720 /*
5721  * Based on:
5722  * Ext JS Library 1.1.1
5723  * Copyright(c) 2006-2007, Ext JS, LLC.
5724  *
5725  * Originally Released Under LGPL - original licence link has changed is not relivant.
5726  *
5727  * Fork - LGPL
5728  * <script type="text/javascript">
5729  */
5730  
5731 /**
5732  * @class Roo.LoadMask
5733  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5734  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5735  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5736  * element's UpdateManager load indicator and will be destroyed after the initial load.
5737  * @constructor
5738  * Create a new LoadMask
5739  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5740  * @param {Object} config The config object
5741  */
5742 Roo.LoadMask = function(el, config){
5743     this.el = Roo.get(el);
5744     Roo.apply(this, config);
5745     if(this.store){
5746         this.store.on('beforeload', this.onBeforeLoad, this);
5747         this.store.on('load', this.onLoad, this);
5748         this.store.on('loadexception', this.onLoadException, this);
5749         this.removeMask = false;
5750     }else{
5751         var um = this.el.getUpdateManager();
5752         um.showLoadIndicator = false; // disable the default indicator
5753         um.on('beforeupdate', this.onBeforeLoad, this);
5754         um.on('update', this.onLoad, this);
5755         um.on('failure', this.onLoad, this);
5756         this.removeMask = true;
5757     }
5758 };
5759
5760 Roo.LoadMask.prototype = {
5761     /**
5762      * @cfg {Boolean} removeMask
5763      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5764      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5765      */
5766     /**
5767      * @cfg {String} msg
5768      * The text to display in a centered loading message box (defaults to 'Loading...')
5769      */
5770     msg : 'Loading...',
5771     /**
5772      * @cfg {String} msgCls
5773      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5774      */
5775     msgCls : 'x-mask-loading',
5776
5777     /**
5778      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5779      * @type Boolean
5780      */
5781     disabled: false,
5782
5783     /**
5784      * Disables the mask to prevent it from being displayed
5785      */
5786     disable : function(){
5787        this.disabled = true;
5788     },
5789
5790     /**
5791      * Enables the mask so that it can be displayed
5792      */
5793     enable : function(){
5794         this.disabled = false;
5795     },
5796     
5797     onLoadException : function()
5798     {
5799         Roo.log(arguments);
5800         
5801         if (typeof(arguments[3]) != 'undefined') {
5802             Roo.MessageBox.alert("Error loading",arguments[3]);
5803         } 
5804         /*
5805         try {
5806             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5807                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5808             }   
5809         } catch(e) {
5810             
5811         }
5812         */
5813     
5814         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5815     },
5816     // private
5817     onLoad : function()
5818     {
5819         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5820     },
5821
5822     // private
5823     onBeforeLoad : function(){
5824         if(!this.disabled){
5825             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5826         }
5827     },
5828
5829     // private
5830     destroy : function(){
5831         if(this.store){
5832             this.store.un('beforeload', this.onBeforeLoad, this);
5833             this.store.un('load', this.onLoad, this);
5834             this.store.un('loadexception', this.onLoadException, this);
5835         }else{
5836             var um = this.el.getUpdateManager();
5837             um.un('beforeupdate', this.onBeforeLoad, this);
5838             um.un('update', this.onLoad, this);
5839             um.un('failure', this.onLoad, this);
5840         }
5841     }
5842 };/*
5843  * - LGPL
5844  *
5845  * table
5846  * 
5847  */
5848
5849 /**
5850  * @class Roo.bootstrap.Table
5851  * @extends Roo.bootstrap.Component
5852  * Bootstrap Table class
5853  * @cfg {String} cls table class
5854  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5855  * @cfg {String} bgcolor Specifies the background color for a table
5856  * @cfg {Number} border Specifies whether the table cells should have borders or not
5857  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5858  * @cfg {Number} cellspacing Specifies the space between cells
5859  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5860  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5861  * @cfg {String} sortable Specifies that the table should be sortable
5862  * @cfg {String} summary Specifies a summary of the content of a table
5863  * @cfg {Number} width Specifies the width of a table
5864  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5865  * 
5866  * @cfg {boolean} striped Should the rows be alternative striped
5867  * @cfg {boolean} bordered Add borders to the table
5868  * @cfg {boolean} hover Add hover highlighting
5869  * @cfg {boolean} condensed Format condensed
5870  * @cfg {boolean} responsive Format condensed
5871  * @cfg {Boolean} loadMask (true|false) default false
5872  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5873  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5874  * @cfg {Boolean} rowSelection (true|false) default false
5875  * @cfg {Boolean} cellSelection (true|false) default false
5876  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5877  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5878  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5879  
5880  * 
5881  * @constructor
5882  * Create a new Table
5883  * @param {Object} config The config object
5884  */
5885
5886 Roo.bootstrap.Table = function(config){
5887     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5888     
5889   
5890     
5891     // BC...
5892     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5893     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5894     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5895     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5896     
5897     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5898     if (this.sm) {
5899         this.sm.grid = this;
5900         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5901         this.sm = this.selModel;
5902         this.sm.xmodule = this.xmodule || false;
5903     }
5904     
5905     if (this.cm && typeof(this.cm.config) == 'undefined') {
5906         this.colModel = new Roo.grid.ColumnModel(this.cm);
5907         this.cm = this.colModel;
5908         this.cm.xmodule = this.xmodule || false;
5909     }
5910     if (this.store) {
5911         this.store= Roo.factory(this.store, Roo.data);
5912         this.ds = this.store;
5913         this.ds.xmodule = this.xmodule || false;
5914          
5915     }
5916     if (this.footer && this.store) {
5917         this.footer.dataSource = this.ds;
5918         this.footer = Roo.factory(this.footer);
5919     }
5920     
5921     /** @private */
5922     this.addEvents({
5923         /**
5924          * @event cellclick
5925          * Fires when a cell is clicked
5926          * @param {Roo.bootstrap.Table} this
5927          * @param {Roo.Element} el
5928          * @param {Number} rowIndex
5929          * @param {Number} columnIndex
5930          * @param {Roo.EventObject} e
5931          */
5932         "cellclick" : true,
5933         /**
5934          * @event celldblclick
5935          * Fires when a cell is double clicked
5936          * @param {Roo.bootstrap.Table} this
5937          * @param {Roo.Element} el
5938          * @param {Number} rowIndex
5939          * @param {Number} columnIndex
5940          * @param {Roo.EventObject} e
5941          */
5942         "celldblclick" : true,
5943         /**
5944          * @event rowclick
5945          * Fires when a row is clicked
5946          * @param {Roo.bootstrap.Table} this
5947          * @param {Roo.Element} el
5948          * @param {Number} rowIndex
5949          * @param {Roo.EventObject} e
5950          */
5951         "rowclick" : true,
5952         /**
5953          * @event rowdblclick
5954          * Fires when a row is double clicked
5955          * @param {Roo.bootstrap.Table} this
5956          * @param {Roo.Element} el
5957          * @param {Number} rowIndex
5958          * @param {Roo.EventObject} e
5959          */
5960         "rowdblclick" : true,
5961         /**
5962          * @event mouseover
5963          * Fires when a mouseover occur
5964          * @param {Roo.bootstrap.Table} this
5965          * @param {Roo.Element} el
5966          * @param {Number} rowIndex
5967          * @param {Number} columnIndex
5968          * @param {Roo.EventObject} e
5969          */
5970         "mouseover" : true,
5971         /**
5972          * @event mouseout
5973          * Fires when a mouseout occur
5974          * @param {Roo.bootstrap.Table} this
5975          * @param {Roo.Element} el
5976          * @param {Number} rowIndex
5977          * @param {Number} columnIndex
5978          * @param {Roo.EventObject} e
5979          */
5980         "mouseout" : true,
5981         /**
5982          * @event rowclass
5983          * Fires when a row is rendered, so you can change add a style to it.
5984          * @param {Roo.bootstrap.Table} this
5985          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5986          */
5987         'rowclass' : true,
5988           /**
5989          * @event rowsrendered
5990          * Fires when all the  rows have been rendered
5991          * @param {Roo.bootstrap.Table} this
5992          */
5993         'rowsrendered' : true,
5994         /**
5995          * @event contextmenu
5996          * The raw contextmenu event for the entire grid.
5997          * @param {Roo.EventObject} e
5998          */
5999         "contextmenu" : true,
6000         /**
6001          * @event rowcontextmenu
6002          * Fires when a row is right clicked
6003          * @param {Roo.bootstrap.Table} this
6004          * @param {Number} rowIndex
6005          * @param {Roo.EventObject} e
6006          */
6007         "rowcontextmenu" : true,
6008         /**
6009          * @event cellcontextmenu
6010          * Fires when a cell is right clicked
6011          * @param {Roo.bootstrap.Table} this
6012          * @param {Number} rowIndex
6013          * @param {Number} cellIndex
6014          * @param {Roo.EventObject} e
6015          */
6016          "cellcontextmenu" : true,
6017          /**
6018          * @event headercontextmenu
6019          * Fires when a header is right clicked
6020          * @param {Roo.bootstrap.Table} this
6021          * @param {Number} columnIndex
6022          * @param {Roo.EventObject} e
6023          */
6024         "headercontextmenu" : true
6025     });
6026 };
6027
6028 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6029     
6030     cls: false,
6031     align: false,
6032     bgcolor: false,
6033     border: false,
6034     cellpadding: false,
6035     cellspacing: false,
6036     frame: false,
6037     rules: false,
6038     sortable: false,
6039     summary: false,
6040     width: false,
6041     striped : false,
6042     scrollBody : false,
6043     bordered: false,
6044     hover:  false,
6045     condensed : false,
6046     responsive : false,
6047     sm : false,
6048     cm : false,
6049     store : false,
6050     loadMask : false,
6051     footerShow : true,
6052     headerShow : true,
6053   
6054     rowSelection : false,
6055     cellSelection : false,
6056     layout : false,
6057     
6058     // Roo.Element - the tbody
6059     mainBody: false,
6060     // Roo.Element - thead element
6061     mainHead: false,
6062     
6063     container: false, // used by gridpanel...
6064     
6065     lazyLoad : false,
6066     
6067     CSS : Roo.util.CSS,
6068     
6069     getAutoCreate : function()
6070     {
6071         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6072         
6073         cfg = {
6074             tag: 'table',
6075             cls : 'table',
6076             cn : []
6077         };
6078         if (this.scrollBody) {
6079             cfg.cls += ' table-body-fixed';
6080         }    
6081         if (this.striped) {
6082             cfg.cls += ' table-striped';
6083         }
6084         
6085         if (this.hover) {
6086             cfg.cls += ' table-hover';
6087         }
6088         if (this.bordered) {
6089             cfg.cls += ' table-bordered';
6090         }
6091         if (this.condensed) {
6092             cfg.cls += ' table-condensed';
6093         }
6094         if (this.responsive) {
6095             cfg.cls += ' table-responsive';
6096         }
6097         
6098         if (this.cls) {
6099             cfg.cls+=  ' ' +this.cls;
6100         }
6101         
6102         // this lot should be simplifed...
6103         
6104         if (this.align) {
6105             cfg.align=this.align;
6106         }
6107         if (this.bgcolor) {
6108             cfg.bgcolor=this.bgcolor;
6109         }
6110         if (this.border) {
6111             cfg.border=this.border;
6112         }
6113         if (this.cellpadding) {
6114             cfg.cellpadding=this.cellpadding;
6115         }
6116         if (this.cellspacing) {
6117             cfg.cellspacing=this.cellspacing;
6118         }
6119         if (this.frame) {
6120             cfg.frame=this.frame;
6121         }
6122         if (this.rules) {
6123             cfg.rules=this.rules;
6124         }
6125         if (this.sortable) {
6126             cfg.sortable=this.sortable;
6127         }
6128         if (this.summary) {
6129             cfg.summary=this.summary;
6130         }
6131         if (this.width) {
6132             cfg.width=this.width;
6133         }
6134         if (this.layout) {
6135             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6136         }
6137         
6138         if(this.store || this.cm){
6139             if(this.headerShow){
6140                 cfg.cn.push(this.renderHeader());
6141             }
6142             
6143             cfg.cn.push(this.renderBody());
6144             
6145             if(this.footerShow){
6146                 cfg.cn.push(this.renderFooter());
6147             }
6148             // where does this come from?
6149             //cfg.cls+=  ' TableGrid';
6150         }
6151         
6152         return { cn : [ cfg ] };
6153     },
6154     
6155     initEvents : function()
6156     {   
6157         if(!this.store || !this.cm){
6158             return;
6159         }
6160         if (this.selModel) {
6161             this.selModel.initEvents();
6162         }
6163         
6164         
6165         //Roo.log('initEvents with ds!!!!');
6166         
6167         this.mainBody = this.el.select('tbody', true).first();
6168         this.mainHead = this.el.select('thead', true).first();
6169         
6170         
6171         
6172         
6173         var _this = this;
6174         
6175         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6176             e.on('click', _this.sort, _this);
6177         });
6178         
6179         this.mainBody.on("click", this.onClick, this);
6180         this.mainBody.on("dblclick", this.onDblClick, this);
6181         
6182         // why is this done????? = it breaks dialogs??
6183         //this.parent().el.setStyle('position', 'relative');
6184         
6185         
6186         if (this.footer) {
6187             this.footer.parentId = this.id;
6188             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6189             
6190             if(this.lazyLoad){
6191                 this.el.select('tfoot tr td').first().addClass('hide');
6192             }
6193         } 
6194         
6195         if(this.loadMask) {
6196             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6197         }
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         
6614         this.autoSize();
6615     },
6616     
6617     
6618     onUpdate : function(ds,record)
6619     {
6620         this.refreshRow(record);
6621         this.autoSize();
6622     },
6623     
6624     onRemove : function(ds, record, index, isUpdate){
6625         if(isUpdate !== true){
6626             this.fireEvent("beforerowremoved", this, index, record);
6627         }
6628         var bt = this.mainBody.dom;
6629         
6630         var rows = this.el.select('tbody > tr', true).elements;
6631         
6632         if(typeof(rows[index]) != 'undefined'){
6633             bt.removeChild(rows[index].dom);
6634         }
6635         
6636 //        if(bt.rows[index]){
6637 //            bt.removeChild(bt.rows[index]);
6638 //        }
6639         
6640         if(isUpdate !== true){
6641             //this.stripeRows(index);
6642             //this.syncRowHeights(index, index);
6643             //this.layout();
6644             this.fireEvent("rowremoved", this, index, record);
6645         }
6646     },
6647     
6648     onAdd : function(ds, records, rowIndex)
6649     {
6650         //Roo.log('on Add called');
6651         // - note this does not handle multiple adding very well..
6652         var bt = this.mainBody.dom;
6653         for (var i =0 ; i < records.length;i++) {
6654             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6655             //Roo.log(records[i]);
6656             //Roo.log(this.store.getAt(rowIndex+i));
6657             this.insertRow(this.store, rowIndex + i, false);
6658             return;
6659         }
6660         
6661     },
6662     
6663     
6664     refreshRow : function(record){
6665         var ds = this.store, index;
6666         if(typeof record == 'number'){
6667             index = record;
6668             record = ds.getAt(index);
6669         }else{
6670             index = ds.indexOf(record);
6671         }
6672         this.insertRow(ds, index, true);
6673         this.autoSize();
6674         this.onRemove(ds, record, index+1, true);
6675         this.autoSize();
6676         //this.syncRowHeights(index, index);
6677         //this.layout();
6678         this.fireEvent("rowupdated", this, index, record);
6679     },
6680     
6681     insertRow : function(dm, rowIndex, isUpdate){
6682         
6683         if(!isUpdate){
6684             this.fireEvent("beforerowsinserted", this, rowIndex);
6685         }
6686             //var s = this.getScrollState();
6687         var row = this.renderRow(this.cm, this.store, rowIndex);
6688         // insert before rowIndex..
6689         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6690         
6691         var _this = this;
6692                 
6693         if(row.cellObjects.length){
6694             Roo.each(row.cellObjects, function(r){
6695                 _this.renderCellObject(r);
6696             })
6697         }
6698             
6699         if(!isUpdate){
6700             this.fireEvent("rowsinserted", this, rowIndex);
6701             //this.syncRowHeights(firstRow, lastRow);
6702             //this.stripeRows(firstRow);
6703             //this.layout();
6704         }
6705         
6706     },
6707     
6708     
6709     getRowDom : function(rowIndex)
6710     {
6711         var rows = this.el.select('tbody > tr', true).elements;
6712         
6713         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6714         
6715     },
6716     // returns the object tree for a tr..
6717   
6718     
6719     renderRow : function(cm, ds, rowIndex) 
6720     {
6721         var d = ds.getAt(rowIndex);
6722         
6723         var row = {
6724             tag : 'tr',
6725             cls : 'x-row-' + rowIndex,
6726             cn : []
6727         };
6728             
6729         var cellObjects = [];
6730         
6731         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6732             var config = cm.config[i];
6733             
6734             var renderer = cm.getRenderer(i);
6735             var value = '';
6736             var id = false;
6737             
6738             if(typeof(renderer) !== 'undefined'){
6739                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6740             }
6741             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6742             // and are rendered into the cells after the row is rendered - using the id for the element.
6743             
6744             if(typeof(value) === 'object'){
6745                 id = Roo.id();
6746                 cellObjects.push({
6747                     container : id,
6748                     cfg : value 
6749                 })
6750             }
6751             
6752             var rowcfg = {
6753                 record: d,
6754                 rowIndex : rowIndex,
6755                 colIndex : i,
6756                 rowClass : ''
6757             };
6758
6759             this.fireEvent('rowclass', this, rowcfg);
6760             
6761             var td = {
6762                 tag: 'td',
6763                 cls : rowcfg.rowClass + ' x-col-' + i,
6764                 style: '',
6765                 html: (typeof(value) === 'object') ? '' : value
6766             };
6767             
6768             if (id) {
6769                 td.id = id;
6770             }
6771             
6772             if(typeof(config.colspan) != 'undefined'){
6773                 td.colspan = config.colspan;
6774             }
6775             
6776             if(typeof(config.hidden) != 'undefined' && config.hidden){
6777                 td.style += ' display:none;';
6778             }
6779             
6780             if(typeof(config.align) != 'undefined' && config.align.length){
6781                 td.style += ' text-align:' + config.align + ';';
6782             }
6783             
6784             if(typeof(config.width) != 'undefined'){
6785                 td.style += ' width:' +  config.width + 'px;';
6786             }
6787             
6788             if(typeof(config.cursor) != 'undefined'){
6789                 td.style += ' cursor:' +  config.cursor + ';';
6790             }
6791             
6792             if(typeof(config.cls) != 'undefined'){
6793                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6794             }
6795             
6796             ['xs','sm','md','lg'].map(function(size){
6797                 
6798                 if(typeof(config[size]) == 'undefined'){
6799                     return;
6800                 }
6801                 
6802                 if (!config[size]) { // 0 = hidden
6803                     td.cls += ' hidden-' + size;
6804                     return;
6805                 }
6806                 
6807                 td.cls += ' col-' + size + '-' + config[size];
6808
6809             });
6810             
6811             row.cn.push(td);
6812            
6813         }
6814         
6815         row.cellObjects = cellObjects;
6816         
6817         return row;
6818           
6819     },
6820     
6821     
6822     
6823     onBeforeLoad : function()
6824     {
6825         
6826     },
6827      /**
6828      * Remove all rows
6829      */
6830     clear : function()
6831     {
6832         this.el.select('tbody', true).first().dom.innerHTML = '';
6833     },
6834     /**
6835      * Show or hide a row.
6836      * @param {Number} rowIndex to show or hide
6837      * @param {Boolean} state hide
6838      */
6839     setRowVisibility : function(rowIndex, state)
6840     {
6841         var bt = this.mainBody.dom;
6842         
6843         var rows = this.el.select('tbody > tr', true).elements;
6844         
6845         if(typeof(rows[rowIndex]) == 'undefined'){
6846             return;
6847         }
6848         rows[rowIndex].dom.style.display = state ? '' : 'none';
6849     },
6850     
6851     
6852     getSelectionModel : function(){
6853         if(!this.selModel){
6854             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6855         }
6856         return this.selModel;
6857     },
6858     /*
6859      * Render the Roo.bootstrap object from renderder
6860      */
6861     renderCellObject : function(r)
6862     {
6863         var _this = this;
6864         
6865         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6866         
6867         var t = r.cfg.render(r.container);
6868         
6869         if(r.cfg.cn){
6870             Roo.each(r.cfg.cn, function(c){
6871                 var child = {
6872                     container: t.getChildContainer(),
6873                     cfg: c
6874                 };
6875                 _this.renderCellObject(child);
6876             })
6877         }
6878     },
6879     
6880     getRowIndex : function(row)
6881     {
6882         var rowIndex = -1;
6883         
6884         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6885             if(el != row){
6886                 return;
6887             }
6888             
6889             rowIndex = index;
6890         });
6891         
6892         return rowIndex;
6893     },
6894      /**
6895      * Returns the grid's underlying element = used by panel.Grid
6896      * @return {Element} The element
6897      */
6898     getGridEl : function(){
6899         return this.el;
6900     },
6901      /**
6902      * Forces a resize - used by panel.Grid
6903      * @return {Element} The element
6904      */
6905     autoSize : function()
6906     {
6907         //var ctr = Roo.get(this.container.dom.parentElement);
6908         var ctr = Roo.get(this.el.dom);
6909         
6910         var thd = this.getGridEl().select('thead',true).first();
6911         var tbd = this.getGridEl().select('tbody', true).first();
6912         var tfd = this.getGridEl().select('tfoot', true).first();
6913         
6914         var cw = ctr.getWidth();
6915         
6916         if (tbd) {
6917             
6918             tbd.setSize(ctr.getWidth(),
6919                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6920             );
6921             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6922             cw -= barsize;
6923         }
6924         cw = Math.max(cw, this.totalWidth);
6925         this.getGridEl().select('tr',true).setWidth(cw);
6926         // resize 'expandable coloumn?
6927         
6928         return; // we doe not have a view in this design..
6929         
6930     },
6931     onBodyScroll: function()
6932     {
6933         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6934         if(this.mainHead){
6935             this.mainHead.setStyle({
6936                 'position' : 'relative',
6937                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6938             });
6939         }
6940         
6941         if(this.lazyLoad){
6942             
6943             var scrollHeight = this.mainBody.dom.scrollHeight;
6944             
6945             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6946             
6947             var height = this.mainBody.getHeight();
6948             
6949             if(scrollHeight - height == scrollTop) {
6950                 
6951                 var total = this.ds.getTotalCount();
6952                 
6953                 if(this.footer.cursor + this.footer.pageSize < total){
6954                     
6955                     this.footer.ds.load({
6956                         params : {
6957                             start : this.footer.cursor + this.footer.pageSize,
6958                             limit : this.footer.pageSize
6959                         },
6960                         add : true
6961                     });
6962                 }
6963             }
6964             
6965         }
6966     },
6967     
6968     onHeaderChange : function()
6969     {
6970         var header = this.renderHeader();
6971         var table = this.el.select('table', true).first();
6972         
6973         this.mainHead.remove();
6974         this.mainHead = table.createChild(header, this.mainBody, false);
6975     },
6976     
6977     onHiddenChange : function(colModel, colIndex, hidden)
6978     {
6979         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6980         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6981         
6982         this.CSS.updateRule(thSelector, "display", "");
6983         this.CSS.updateRule(tdSelector, "display", "");
6984         
6985         if(hidden){
6986             this.CSS.updateRule(thSelector, "display", "none");
6987             this.CSS.updateRule(tdSelector, "display", "none");
6988         }
6989         
6990         this.onHeaderChange();
6991         this.onLoad();
6992         
6993     }
6994     
6995 });
6996
6997  
6998
6999  /*
7000  * - LGPL
7001  *
7002  * table cell
7003  * 
7004  */
7005
7006 /**
7007  * @class Roo.bootstrap.TableCell
7008  * @extends Roo.bootstrap.Component
7009  * Bootstrap TableCell class
7010  * @cfg {String} html cell contain text
7011  * @cfg {String} cls cell class
7012  * @cfg {String} tag cell tag (td|th) default td
7013  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7014  * @cfg {String} align Aligns the content in a cell
7015  * @cfg {String} axis Categorizes cells
7016  * @cfg {String} bgcolor Specifies the background color of a cell
7017  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7018  * @cfg {Number} colspan Specifies the number of columns a cell should span
7019  * @cfg {String} headers Specifies one or more header cells a cell is related to
7020  * @cfg {Number} height Sets the height of a cell
7021  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7022  * @cfg {Number} rowspan Sets the number of rows a cell should span
7023  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7024  * @cfg {String} valign Vertical aligns the content in a cell
7025  * @cfg {Number} width Specifies the width of a cell
7026  * 
7027  * @constructor
7028  * Create a new TableCell
7029  * @param {Object} config The config object
7030  */
7031
7032 Roo.bootstrap.TableCell = function(config){
7033     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7034 };
7035
7036 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7037     
7038     html: false,
7039     cls: false,
7040     tag: false,
7041     abbr: false,
7042     align: false,
7043     axis: false,
7044     bgcolor: false,
7045     charoff: false,
7046     colspan: false,
7047     headers: false,
7048     height: false,
7049     nowrap: false,
7050     rowspan: false,
7051     scope: false,
7052     valign: false,
7053     width: false,
7054     
7055     
7056     getAutoCreate : function(){
7057         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7058         
7059         cfg = {
7060             tag: 'td'
7061         };
7062         
7063         if(this.tag){
7064             cfg.tag = this.tag;
7065         }
7066         
7067         if (this.html) {
7068             cfg.html=this.html
7069         }
7070         if (this.cls) {
7071             cfg.cls=this.cls
7072         }
7073         if (this.abbr) {
7074             cfg.abbr=this.abbr
7075         }
7076         if (this.align) {
7077             cfg.align=this.align
7078         }
7079         if (this.axis) {
7080             cfg.axis=this.axis
7081         }
7082         if (this.bgcolor) {
7083             cfg.bgcolor=this.bgcolor
7084         }
7085         if (this.charoff) {
7086             cfg.charoff=this.charoff
7087         }
7088         if (this.colspan) {
7089             cfg.colspan=this.colspan
7090         }
7091         if (this.headers) {
7092             cfg.headers=this.headers
7093         }
7094         if (this.height) {
7095             cfg.height=this.height
7096         }
7097         if (this.nowrap) {
7098             cfg.nowrap=this.nowrap
7099         }
7100         if (this.rowspan) {
7101             cfg.rowspan=this.rowspan
7102         }
7103         if (this.scope) {
7104             cfg.scope=this.scope
7105         }
7106         if (this.valign) {
7107             cfg.valign=this.valign
7108         }
7109         if (this.width) {
7110             cfg.width=this.width
7111         }
7112         
7113         
7114         return cfg;
7115     }
7116    
7117 });
7118
7119  
7120
7121  /*
7122  * - LGPL
7123  *
7124  * table row
7125  * 
7126  */
7127
7128 /**
7129  * @class Roo.bootstrap.TableRow
7130  * @extends Roo.bootstrap.Component
7131  * Bootstrap TableRow class
7132  * @cfg {String} cls row class
7133  * @cfg {String} align Aligns the content in a table row
7134  * @cfg {String} bgcolor Specifies a background color for a table row
7135  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7136  * @cfg {String} valign Vertical aligns the content in a table row
7137  * 
7138  * @constructor
7139  * Create a new TableRow
7140  * @param {Object} config The config object
7141  */
7142
7143 Roo.bootstrap.TableRow = function(config){
7144     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7145 };
7146
7147 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7148     
7149     cls: false,
7150     align: false,
7151     bgcolor: false,
7152     charoff: false,
7153     valign: false,
7154     
7155     getAutoCreate : function(){
7156         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7157         
7158         cfg = {
7159             tag: 'tr'
7160         };
7161             
7162         if(this.cls){
7163             cfg.cls = this.cls;
7164         }
7165         if(this.align){
7166             cfg.align = this.align;
7167         }
7168         if(this.bgcolor){
7169             cfg.bgcolor = this.bgcolor;
7170         }
7171         if(this.charoff){
7172             cfg.charoff = this.charoff;
7173         }
7174         if(this.valign){
7175             cfg.valign = this.valign;
7176         }
7177         
7178         return cfg;
7179     }
7180    
7181 });
7182
7183  
7184
7185  /*
7186  * - LGPL
7187  *
7188  * table body
7189  * 
7190  */
7191
7192 /**
7193  * @class Roo.bootstrap.TableBody
7194  * @extends Roo.bootstrap.Component
7195  * Bootstrap TableBody class
7196  * @cfg {String} cls element class
7197  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7198  * @cfg {String} align Aligns the content inside the element
7199  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7200  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7201  * 
7202  * @constructor
7203  * Create a new TableBody
7204  * @param {Object} config The config object
7205  */
7206
7207 Roo.bootstrap.TableBody = function(config){
7208     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7209 };
7210
7211 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7212     
7213     cls: false,
7214     tag: false,
7215     align: false,
7216     charoff: false,
7217     valign: false,
7218     
7219     getAutoCreate : function(){
7220         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7221         
7222         cfg = {
7223             tag: 'tbody'
7224         };
7225             
7226         if (this.cls) {
7227             cfg.cls=this.cls
7228         }
7229         if(this.tag){
7230             cfg.tag = this.tag;
7231         }
7232         
7233         if(this.align){
7234             cfg.align = this.align;
7235         }
7236         if(this.charoff){
7237             cfg.charoff = this.charoff;
7238         }
7239         if(this.valign){
7240             cfg.valign = this.valign;
7241         }
7242         
7243         return cfg;
7244     }
7245     
7246     
7247 //    initEvents : function()
7248 //    {
7249 //        
7250 //        if(!this.store){
7251 //            return;
7252 //        }
7253 //        
7254 //        this.store = Roo.factory(this.store, Roo.data);
7255 //        this.store.on('load', this.onLoad, this);
7256 //        
7257 //        this.store.load();
7258 //        
7259 //    },
7260 //    
7261 //    onLoad: function () 
7262 //    {   
7263 //        this.fireEvent('load', this);
7264 //    }
7265 //    
7266 //   
7267 });
7268
7269  
7270
7271  /*
7272  * Based on:
7273  * Ext JS Library 1.1.1
7274  * Copyright(c) 2006-2007, Ext JS, LLC.
7275  *
7276  * Originally Released Under LGPL - original licence link has changed is not relivant.
7277  *
7278  * Fork - LGPL
7279  * <script type="text/javascript">
7280  */
7281
7282 // as we use this in bootstrap.
7283 Roo.namespace('Roo.form');
7284  /**
7285  * @class Roo.form.Action
7286  * Internal Class used to handle form actions
7287  * @constructor
7288  * @param {Roo.form.BasicForm} el The form element or its id
7289  * @param {Object} config Configuration options
7290  */
7291
7292  
7293  
7294 // define the action interface
7295 Roo.form.Action = function(form, options){
7296     this.form = form;
7297     this.options = options || {};
7298 };
7299 /**
7300  * Client Validation Failed
7301  * @const 
7302  */
7303 Roo.form.Action.CLIENT_INVALID = 'client';
7304 /**
7305  * Server Validation Failed
7306  * @const 
7307  */
7308 Roo.form.Action.SERVER_INVALID = 'server';
7309  /**
7310  * Connect to Server Failed
7311  * @const 
7312  */
7313 Roo.form.Action.CONNECT_FAILURE = 'connect';
7314 /**
7315  * Reading Data from Server Failed
7316  * @const 
7317  */
7318 Roo.form.Action.LOAD_FAILURE = 'load';
7319
7320 Roo.form.Action.prototype = {
7321     type : 'default',
7322     failureType : undefined,
7323     response : undefined,
7324     result : undefined,
7325
7326     // interface method
7327     run : function(options){
7328
7329     },
7330
7331     // interface method
7332     success : function(response){
7333
7334     },
7335
7336     // interface method
7337     handleResponse : function(response){
7338
7339     },
7340
7341     // default connection failure
7342     failure : function(response){
7343         
7344         this.response = response;
7345         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7346         this.form.afterAction(this, false);
7347     },
7348
7349     processResponse : function(response){
7350         this.response = response;
7351         if(!response.responseText){
7352             return true;
7353         }
7354         this.result = this.handleResponse(response);
7355         return this.result;
7356     },
7357
7358     // utility functions used internally
7359     getUrl : function(appendParams){
7360         var url = this.options.url || this.form.url || this.form.el.dom.action;
7361         if(appendParams){
7362             var p = this.getParams();
7363             if(p){
7364                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7365             }
7366         }
7367         return url;
7368     },
7369
7370     getMethod : function(){
7371         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7372     },
7373
7374     getParams : function(){
7375         var bp = this.form.baseParams;
7376         var p = this.options.params;
7377         if(p){
7378             if(typeof p == "object"){
7379                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7380             }else if(typeof p == 'string' && bp){
7381                 p += '&' + Roo.urlEncode(bp);
7382             }
7383         }else if(bp){
7384             p = Roo.urlEncode(bp);
7385         }
7386         return p;
7387     },
7388
7389     createCallback : function(){
7390         return {
7391             success: this.success,
7392             failure: this.failure,
7393             scope: this,
7394             timeout: (this.form.timeout*1000),
7395             upload: this.form.fileUpload ? this.success : undefined
7396         };
7397     }
7398 };
7399
7400 Roo.form.Action.Submit = function(form, options){
7401     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7402 };
7403
7404 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7405     type : 'submit',
7406
7407     haveProgress : false,
7408     uploadComplete : false,
7409     
7410     // uploadProgress indicator.
7411     uploadProgress : function()
7412     {
7413         if (!this.form.progressUrl) {
7414             return;
7415         }
7416         
7417         if (!this.haveProgress) {
7418             Roo.MessageBox.progress("Uploading", "Uploading");
7419         }
7420         if (this.uploadComplete) {
7421            Roo.MessageBox.hide();
7422            return;
7423         }
7424         
7425         this.haveProgress = true;
7426    
7427         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7428         
7429         var c = new Roo.data.Connection();
7430         c.request({
7431             url : this.form.progressUrl,
7432             params: {
7433                 id : uid
7434             },
7435             method: 'GET',
7436             success : function(req){
7437                //console.log(data);
7438                 var rdata = false;
7439                 var edata;
7440                 try  {
7441                    rdata = Roo.decode(req.responseText)
7442                 } catch (e) {
7443                     Roo.log("Invalid data from server..");
7444                     Roo.log(edata);
7445                     return;
7446                 }
7447                 if (!rdata || !rdata.success) {
7448                     Roo.log(rdata);
7449                     Roo.MessageBox.alert(Roo.encode(rdata));
7450                     return;
7451                 }
7452                 var data = rdata.data;
7453                 
7454                 if (this.uploadComplete) {
7455                    Roo.MessageBox.hide();
7456                    return;
7457                 }
7458                    
7459                 if (data){
7460                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7461                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7462                     );
7463                 }
7464                 this.uploadProgress.defer(2000,this);
7465             },
7466        
7467             failure: function(data) {
7468                 Roo.log('progress url failed ');
7469                 Roo.log(data);
7470             },
7471             scope : this
7472         });
7473            
7474     },
7475     
7476     
7477     run : function()
7478     {
7479         // run get Values on the form, so it syncs any secondary forms.
7480         this.form.getValues();
7481         
7482         var o = this.options;
7483         var method = this.getMethod();
7484         var isPost = method == 'POST';
7485         if(o.clientValidation === false || this.form.isValid()){
7486             
7487             if (this.form.progressUrl) {
7488                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7489                     (new Date() * 1) + '' + Math.random());
7490                     
7491             } 
7492             
7493             
7494             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7495                 form:this.form.el.dom,
7496                 url:this.getUrl(!isPost),
7497                 method: method,
7498                 params:isPost ? this.getParams() : null,
7499                 isUpload: this.form.fileUpload
7500             }));
7501             
7502             this.uploadProgress();
7503
7504         }else if (o.clientValidation !== false){ // client validation failed
7505             this.failureType = Roo.form.Action.CLIENT_INVALID;
7506             this.form.afterAction(this, false);
7507         }
7508     },
7509
7510     success : function(response)
7511     {
7512         this.uploadComplete= true;
7513         if (this.haveProgress) {
7514             Roo.MessageBox.hide();
7515         }
7516         
7517         
7518         var result = this.processResponse(response);
7519         if(result === true || result.success){
7520             this.form.afterAction(this, true);
7521             return;
7522         }
7523         if(result.errors){
7524             this.form.markInvalid(result.errors);
7525             this.failureType = Roo.form.Action.SERVER_INVALID;
7526         }
7527         this.form.afterAction(this, false);
7528     },
7529     failure : function(response)
7530     {
7531         this.uploadComplete= true;
7532         if (this.haveProgress) {
7533             Roo.MessageBox.hide();
7534         }
7535         
7536         this.response = response;
7537         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7538         this.form.afterAction(this, false);
7539     },
7540     
7541     handleResponse : function(response){
7542         if(this.form.errorReader){
7543             var rs = this.form.errorReader.read(response);
7544             var errors = [];
7545             if(rs.records){
7546                 for(var i = 0, len = rs.records.length; i < len; i++) {
7547                     var r = rs.records[i];
7548                     errors[i] = r.data;
7549                 }
7550             }
7551             if(errors.length < 1){
7552                 errors = null;
7553             }
7554             return {
7555                 success : rs.success,
7556                 errors : errors
7557             };
7558         }
7559         var ret = false;
7560         try {
7561             ret = Roo.decode(response.responseText);
7562         } catch (e) {
7563             ret = {
7564                 success: false,
7565                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7566                 errors : []
7567             };
7568         }
7569         return ret;
7570         
7571     }
7572 });
7573
7574
7575 Roo.form.Action.Load = function(form, options){
7576     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7577     this.reader = this.form.reader;
7578 };
7579
7580 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7581     type : 'load',
7582
7583     run : function(){
7584         
7585         Roo.Ajax.request(Roo.apply(
7586                 this.createCallback(), {
7587                     method:this.getMethod(),
7588                     url:this.getUrl(false),
7589                     params:this.getParams()
7590         }));
7591     },
7592
7593     success : function(response){
7594         
7595         var result = this.processResponse(response);
7596         if(result === true || !result.success || !result.data){
7597             this.failureType = Roo.form.Action.LOAD_FAILURE;
7598             this.form.afterAction(this, false);
7599             return;
7600         }
7601         this.form.clearInvalid();
7602         this.form.setValues(result.data);
7603         this.form.afterAction(this, true);
7604     },
7605
7606     handleResponse : function(response){
7607         if(this.form.reader){
7608             var rs = this.form.reader.read(response);
7609             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7610             return {
7611                 success : rs.success,
7612                 data : data
7613             };
7614         }
7615         return Roo.decode(response.responseText);
7616     }
7617 });
7618
7619 Roo.form.Action.ACTION_TYPES = {
7620     'load' : Roo.form.Action.Load,
7621     'submit' : Roo.form.Action.Submit
7622 };/*
7623  * - LGPL
7624  *
7625  * form
7626  *
7627  */
7628
7629 /**
7630  * @class Roo.bootstrap.Form
7631  * @extends Roo.bootstrap.Component
7632  * Bootstrap Form class
7633  * @cfg {String} method  GET | POST (default POST)
7634  * @cfg {String} labelAlign top | left (default top)
7635  * @cfg {String} align left  | right - for navbars
7636  * @cfg {Boolean} loadMask load mask when submit (default true)
7637
7638  *
7639  * @constructor
7640  * Create a new Form
7641  * @param {Object} config The config object
7642  */
7643
7644
7645 Roo.bootstrap.Form = function(config){
7646     
7647     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7648     
7649     Roo.bootstrap.Form.popover.apply();
7650     
7651     this.addEvents({
7652         /**
7653          * @event clientvalidation
7654          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7655          * @param {Form} this
7656          * @param {Boolean} valid true if the form has passed client-side validation
7657          */
7658         clientvalidation: true,
7659         /**
7660          * @event beforeaction
7661          * Fires before any action is performed. Return false to cancel the action.
7662          * @param {Form} this
7663          * @param {Action} action The action to be performed
7664          */
7665         beforeaction: true,
7666         /**
7667          * @event actionfailed
7668          * Fires when an action fails.
7669          * @param {Form} this
7670          * @param {Action} action The action that failed
7671          */
7672         actionfailed : true,
7673         /**
7674          * @event actioncomplete
7675          * Fires when an action is completed.
7676          * @param {Form} this
7677          * @param {Action} action The action that completed
7678          */
7679         actioncomplete : true
7680     });
7681 };
7682
7683 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7684
7685      /**
7686      * @cfg {String} method
7687      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7688      */
7689     method : 'POST',
7690     /**
7691      * @cfg {String} url
7692      * The URL to use for form actions if one isn't supplied in the action options.
7693      */
7694     /**
7695      * @cfg {Boolean} fileUpload
7696      * Set to true if this form is a file upload.
7697      */
7698
7699     /**
7700      * @cfg {Object} baseParams
7701      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7702      */
7703
7704     /**
7705      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7706      */
7707     timeout: 30,
7708     /**
7709      * @cfg {Sting} align (left|right) for navbar forms
7710      */
7711     align : 'left',
7712
7713     // private
7714     activeAction : null,
7715
7716     /**
7717      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7718      * element by passing it or its id or mask the form itself by passing in true.
7719      * @type Mixed
7720      */
7721     waitMsgTarget : false,
7722
7723     loadMask : true,
7724     
7725     /**
7726      * @cfg {Boolean} errorMask (true|false) default false
7727      */
7728     errorMask : false,
7729     
7730     /**
7731      * @cfg {Number} maskOffset Default 100
7732      */
7733     maskOffset : 100,
7734     
7735     /**
7736      * @cfg {Boolean} maskBody
7737      */
7738     maskBody : false,
7739
7740     getAutoCreate : function(){
7741
7742         var cfg = {
7743             tag: 'form',
7744             method : this.method || 'POST',
7745             id : this.id || Roo.id(),
7746             cls : ''
7747         };
7748         if (this.parent().xtype.match(/^Nav/)) {
7749             cfg.cls = 'navbar-form navbar-' + this.align;
7750
7751         }
7752
7753         if (this.labelAlign == 'left' ) {
7754             cfg.cls += ' form-horizontal';
7755         }
7756
7757
7758         return cfg;
7759     },
7760     initEvents : function()
7761     {
7762         this.el.on('submit', this.onSubmit, this);
7763         // this was added as random key presses on the form where triggering form submit.
7764         this.el.on('keypress', function(e) {
7765             if (e.getCharCode() != 13) {
7766                 return true;
7767             }
7768             // we might need to allow it for textareas.. and some other items.
7769             // check e.getTarget().
7770
7771             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7772                 return true;
7773             }
7774
7775             Roo.log("keypress blocked");
7776
7777             e.preventDefault();
7778             return false;
7779         });
7780         
7781     },
7782     // private
7783     onSubmit : function(e){
7784         e.stopEvent();
7785     },
7786
7787      /**
7788      * Returns true if client-side validation on the form is successful.
7789      * @return Boolean
7790      */
7791     isValid : function(){
7792         var items = this.getItems();
7793         var valid = true;
7794         var target = false;
7795         
7796         items.each(function(f){
7797             
7798             if(f.validate()){
7799                 return;
7800             }
7801             valid = false;
7802
7803             if(!target && f.el.isVisible(true)){
7804                 target = f;
7805             }
7806            
7807         });
7808         
7809         if(this.errorMask && !valid){
7810             Roo.bootstrap.Form.popover.mask(this, target);
7811         }
7812         
7813         return valid;
7814     },
7815     
7816     /**
7817      * Returns true if any fields in this form have changed since their original load.
7818      * @return Boolean
7819      */
7820     isDirty : function(){
7821         var dirty = false;
7822         var items = this.getItems();
7823         items.each(function(f){
7824            if(f.isDirty()){
7825                dirty = true;
7826                return false;
7827            }
7828            return true;
7829         });
7830         return dirty;
7831     },
7832      /**
7833      * Performs a predefined action (submit or load) or custom actions you define on this form.
7834      * @param {String} actionName The name of the action type
7835      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7836      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7837      * accept other config options):
7838      * <pre>
7839 Property          Type             Description
7840 ----------------  ---------------  ----------------------------------------------------------------------------------
7841 url               String           The url for the action (defaults to the form's url)
7842 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7843 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7844 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7845                                    validate the form on the client (defaults to false)
7846      * </pre>
7847      * @return {BasicForm} this
7848      */
7849     doAction : function(action, options){
7850         if(typeof action == 'string'){
7851             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7852         }
7853         if(this.fireEvent('beforeaction', this, action) !== false){
7854             this.beforeAction(action);
7855             action.run.defer(100, action);
7856         }
7857         return this;
7858     },
7859
7860     // private
7861     beforeAction : function(action){
7862         var o = action.options;
7863         
7864         if(this.loadMask){
7865             
7866             if(this.maskBody){
7867                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7868             } else {
7869                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7870             }
7871         }
7872         // not really supported yet.. ??
7873
7874         //if(this.waitMsgTarget === true){
7875         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7876         //}else if(this.waitMsgTarget){
7877         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7878         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7879         //}else {
7880         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7881        // }
7882
7883     },
7884
7885     // private
7886     afterAction : function(action, success){
7887         this.activeAction = null;
7888         var o = action.options;
7889
7890         if(this.loadMask){
7891             
7892             if(this.maskBody){
7893                 Roo.get(document.body).unmask();
7894             } else {
7895                 this.el.unmask();
7896             }
7897         }
7898         
7899         //if(this.waitMsgTarget === true){
7900 //            this.el.unmask();
7901         //}else if(this.waitMsgTarget){
7902         //    this.waitMsgTarget.unmask();
7903         //}else{
7904         //    Roo.MessageBox.updateProgress(1);
7905         //    Roo.MessageBox.hide();
7906        // }
7907         //
7908         if(success){
7909             if(o.reset){
7910                 this.reset();
7911             }
7912             Roo.callback(o.success, o.scope, [this, action]);
7913             this.fireEvent('actioncomplete', this, action);
7914
7915         }else{
7916
7917             // failure condition..
7918             // we have a scenario where updates need confirming.
7919             // eg. if a locking scenario exists..
7920             // we look for { errors : { needs_confirm : true }} in the response.
7921             if (
7922                 (typeof(action.result) != 'undefined')  &&
7923                 (typeof(action.result.errors) != 'undefined')  &&
7924                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7925            ){
7926                 var _t = this;
7927                 Roo.log("not supported yet");
7928                  /*
7929
7930                 Roo.MessageBox.confirm(
7931                     "Change requires confirmation",
7932                     action.result.errorMsg,
7933                     function(r) {
7934                         if (r != 'yes') {
7935                             return;
7936                         }
7937                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7938                     }
7939
7940                 );
7941                 */
7942
7943
7944                 return;
7945             }
7946
7947             Roo.callback(o.failure, o.scope, [this, action]);
7948             // show an error message if no failed handler is set..
7949             if (!this.hasListener('actionfailed')) {
7950                 Roo.log("need to add dialog support");
7951                 /*
7952                 Roo.MessageBox.alert("Error",
7953                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7954                         action.result.errorMsg :
7955                         "Saving Failed, please check your entries or try again"
7956                 );
7957                 */
7958             }
7959
7960             this.fireEvent('actionfailed', this, action);
7961         }
7962
7963     },
7964     /**
7965      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7966      * @param {String} id The value to search for
7967      * @return Field
7968      */
7969     findField : function(id){
7970         var items = this.getItems();
7971         var field = items.get(id);
7972         if(!field){
7973              items.each(function(f){
7974                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7975                     field = f;
7976                     return false;
7977                 }
7978                 return true;
7979             });
7980         }
7981         return field || null;
7982     },
7983      /**
7984      * Mark fields in this form invalid in bulk.
7985      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7986      * @return {BasicForm} this
7987      */
7988     markInvalid : function(errors){
7989         if(errors instanceof Array){
7990             for(var i = 0, len = errors.length; i < len; i++){
7991                 var fieldError = errors[i];
7992                 var f = this.findField(fieldError.id);
7993                 if(f){
7994                     f.markInvalid(fieldError.msg);
7995                 }
7996             }
7997         }else{
7998             var field, id;
7999             for(id in errors){
8000                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8001                     field.markInvalid(errors[id]);
8002                 }
8003             }
8004         }
8005         //Roo.each(this.childForms || [], function (f) {
8006         //    f.markInvalid(errors);
8007         //});
8008
8009         return this;
8010     },
8011
8012     /**
8013      * Set values for fields in this form in bulk.
8014      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8015      * @return {BasicForm} this
8016      */
8017     setValues : function(values){
8018         if(values instanceof Array){ // array of objects
8019             for(var i = 0, len = values.length; i < len; i++){
8020                 var v = values[i];
8021                 var f = this.findField(v.id);
8022                 if(f){
8023                     f.setValue(v.value);
8024                     if(this.trackResetOnLoad){
8025                         f.originalValue = f.getValue();
8026                     }
8027                 }
8028             }
8029         }else{ // object hash
8030             var field, id;
8031             for(id in values){
8032                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8033
8034                     if (field.setFromData &&
8035                         field.valueField &&
8036                         field.displayField &&
8037                         // combos' with local stores can
8038                         // be queried via setValue()
8039                         // to set their value..
8040                         (field.store && !field.store.isLocal)
8041                         ) {
8042                         // it's a combo
8043                         var sd = { };
8044                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8045                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8046                         field.setFromData(sd);
8047
8048                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8049                         
8050                         field.setFromData(values);
8051                         
8052                     } else {
8053                         field.setValue(values[id]);
8054                     }
8055
8056
8057                     if(this.trackResetOnLoad){
8058                         field.originalValue = field.getValue();
8059                     }
8060                 }
8061             }
8062         }
8063
8064         //Roo.each(this.childForms || [], function (f) {
8065         //    f.setValues(values);
8066         //});
8067
8068         return this;
8069     },
8070
8071     /**
8072      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8073      * they are returned as an array.
8074      * @param {Boolean} asString
8075      * @return {Object}
8076      */
8077     getValues : function(asString){
8078         //if (this.childForms) {
8079             // copy values from the child forms
8080         //    Roo.each(this.childForms, function (f) {
8081         //        this.setValues(f.getValues());
8082         //    }, this);
8083         //}
8084
8085
8086
8087         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8088         if(asString === true){
8089             return fs;
8090         }
8091         return Roo.urlDecode(fs);
8092     },
8093
8094     /**
8095      * Returns the fields in this form as an object with key/value pairs.
8096      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8097      * @return {Object}
8098      */
8099     getFieldValues : function(with_hidden)
8100     {
8101         var items = this.getItems();
8102         var ret = {};
8103         items.each(function(f){
8104             
8105             if (!f.getName()) {
8106                 return;
8107             }
8108             
8109             var v = f.getValue();
8110             
8111             if (f.inputType =='radio') {
8112                 if (typeof(ret[f.getName()]) == 'undefined') {
8113                     ret[f.getName()] = ''; // empty..
8114                 }
8115
8116                 if (!f.el.dom.checked) {
8117                     return;
8118
8119                 }
8120                 v = f.el.dom.value;
8121
8122             }
8123             
8124             if(f.xtype == 'MoneyField'){
8125                 ret[f.currencyName] = f.getCurrency();
8126             }
8127
8128             // not sure if this supported any more..
8129             if ((typeof(v) == 'object') && f.getRawValue) {
8130                 v = f.getRawValue() ; // dates..
8131             }
8132             // combo boxes where name != hiddenName...
8133             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8134                 ret[f.name] = f.getRawValue();
8135             }
8136             ret[f.getName()] = v;
8137         });
8138
8139         return ret;
8140     },
8141
8142     /**
8143      * Clears all invalid messages in this form.
8144      * @return {BasicForm} this
8145      */
8146     clearInvalid : function(){
8147         var items = this.getItems();
8148
8149         items.each(function(f){
8150            f.clearInvalid();
8151         });
8152
8153         return this;
8154     },
8155
8156     /**
8157      * Resets this form.
8158      * @return {BasicForm} this
8159      */
8160     reset : function(){
8161         var items = this.getItems();
8162         items.each(function(f){
8163             f.reset();
8164         });
8165
8166         Roo.each(this.childForms || [], function (f) {
8167             f.reset();
8168         });
8169
8170
8171         return this;
8172     },
8173     
8174     getItems : function()
8175     {
8176         var r=new Roo.util.MixedCollection(false, function(o){
8177             return o.id || (o.id = Roo.id());
8178         });
8179         var iter = function(el) {
8180             if (el.inputEl) {
8181                 r.add(el);
8182             }
8183             if (!el.items) {
8184                 return;
8185             }
8186             Roo.each(el.items,function(e) {
8187                 iter(e);
8188             });
8189         };
8190
8191         iter(this);
8192         return r;
8193     },
8194     
8195     hideFields : function(items)
8196     {
8197         Roo.each(items, function(i){
8198             
8199             var f = this.findField(i);
8200             
8201             if(!f){
8202                 return;
8203             }
8204             
8205             if(f.xtype == 'DateField'){
8206                 f.setVisible(false);
8207                 return;
8208             }
8209             
8210             f.hide();
8211             
8212         }, this);
8213     },
8214     
8215     showFields : function(items)
8216     {
8217         Roo.each(items, function(i){
8218             
8219             var f = this.findField(i);
8220             
8221             if(!f){
8222                 return;
8223             }
8224             
8225             if(f.xtype == 'DateField'){
8226                 f.setVisible(true);
8227                 return;
8228             }
8229             
8230             f.show();
8231             
8232         }, this);
8233     }
8234
8235 });
8236
8237 Roo.apply(Roo.bootstrap.Form, {
8238     
8239     popover : {
8240         
8241         padding : 5,
8242         
8243         isApplied : false,
8244         
8245         isMasked : false,
8246         
8247         form : false,
8248         
8249         target : false,
8250         
8251         toolTip : false,
8252         
8253         intervalID : false,
8254         
8255         maskEl : false,
8256         
8257         apply : function()
8258         {
8259             if(this.isApplied){
8260                 return;
8261             }
8262             
8263             this.maskEl = {
8264                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8265                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8266                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8267                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8268             };
8269             
8270             this.maskEl.top.enableDisplayMode("block");
8271             this.maskEl.left.enableDisplayMode("block");
8272             this.maskEl.bottom.enableDisplayMode("block");
8273             this.maskEl.right.enableDisplayMode("block");
8274             
8275             this.toolTip = new Roo.bootstrap.Tooltip({
8276                 cls : 'roo-form-error-popover',
8277                 alignment : {
8278                     'left' : ['r-l', [-2,0], 'right'],
8279                     'right' : ['l-r', [2,0], 'left'],
8280                     'bottom' : ['tl-bl', [0,2], 'top'],
8281                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8282                 }
8283             });
8284             
8285             this.toolTip.render(Roo.get(document.body));
8286
8287             this.toolTip.el.enableDisplayMode("block");
8288             
8289             Roo.get(document.body).on('click', function(){
8290                 this.unmask();
8291             }, this);
8292             
8293             Roo.get(document.body).on('touchstart', function(){
8294                 this.unmask();
8295             }, this);
8296             
8297             this.isApplied = true
8298         },
8299         
8300         mask : function(form, target)
8301         {
8302             this.form = form;
8303             
8304             this.target = target;
8305             
8306             if(!this.form.errorMask || !target.el){
8307                 return;
8308             }
8309             
8310             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8311             
8312             Roo.log(scrollable);
8313             
8314             var ot = this.target.el.calcOffsetsTo(scrollable);
8315             
8316             var scrollTo = ot[1] - this.form.maskOffset;
8317             
8318             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8319             
8320             scrollable.scrollTo('top', scrollTo);
8321             
8322             var box = this.target.el.getBox();
8323             Roo.log(box);
8324             var zIndex = Roo.bootstrap.Modal.zIndex++;
8325
8326             
8327             this.maskEl.top.setStyle('position', 'absolute');
8328             this.maskEl.top.setStyle('z-index', zIndex);
8329             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8330             this.maskEl.top.setLeft(0);
8331             this.maskEl.top.setTop(0);
8332             this.maskEl.top.show();
8333             
8334             this.maskEl.left.setStyle('position', 'absolute');
8335             this.maskEl.left.setStyle('z-index', zIndex);
8336             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8337             this.maskEl.left.setLeft(0);
8338             this.maskEl.left.setTop(box.y - this.padding);
8339             this.maskEl.left.show();
8340
8341             this.maskEl.bottom.setStyle('position', 'absolute');
8342             this.maskEl.bottom.setStyle('z-index', zIndex);
8343             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8344             this.maskEl.bottom.setLeft(0);
8345             this.maskEl.bottom.setTop(box.bottom + this.padding);
8346             this.maskEl.bottom.show();
8347
8348             this.maskEl.right.setStyle('position', 'absolute');
8349             this.maskEl.right.setStyle('z-index', zIndex);
8350             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8351             this.maskEl.right.setLeft(box.right + this.padding);
8352             this.maskEl.right.setTop(box.y - this.padding);
8353             this.maskEl.right.show();
8354
8355             this.toolTip.bindEl = this.target.el;
8356
8357             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8358
8359             var tip = this.target.blankText;
8360
8361             if(this.target.getValue() !== '' ) {
8362                 
8363                 if (this.target.invalidText.length) {
8364                     tip = this.target.invalidText;
8365                 } else if (this.target.regexText.length){
8366                     tip = this.target.regexText;
8367                 }
8368             }
8369
8370             this.toolTip.show(tip);
8371
8372             this.intervalID = window.setInterval(function() {
8373                 Roo.bootstrap.Form.popover.unmask();
8374             }, 10000);
8375
8376             window.onwheel = function(){ return false;};
8377             
8378             (function(){ this.isMasked = true; }).defer(500, this);
8379             
8380         },
8381         
8382         unmask : function()
8383         {
8384             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8385                 return;
8386             }
8387             
8388             this.maskEl.top.setStyle('position', 'absolute');
8389             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8390             this.maskEl.top.hide();
8391
8392             this.maskEl.left.setStyle('position', 'absolute');
8393             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8394             this.maskEl.left.hide();
8395
8396             this.maskEl.bottom.setStyle('position', 'absolute');
8397             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8398             this.maskEl.bottom.hide();
8399
8400             this.maskEl.right.setStyle('position', 'absolute');
8401             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8402             this.maskEl.right.hide();
8403             
8404             this.toolTip.hide();
8405             
8406             this.toolTip.el.hide();
8407             
8408             window.onwheel = function(){ return true;};
8409             
8410             if(this.intervalID){
8411                 window.clearInterval(this.intervalID);
8412                 this.intervalID = false;
8413             }
8414             
8415             this.isMasked = false;
8416             
8417         }
8418         
8419     }
8420     
8421 });
8422
8423 /*
8424  * Based on:
8425  * Ext JS Library 1.1.1
8426  * Copyright(c) 2006-2007, Ext JS, LLC.
8427  *
8428  * Originally Released Under LGPL - original licence link has changed is not relivant.
8429  *
8430  * Fork - LGPL
8431  * <script type="text/javascript">
8432  */
8433 /**
8434  * @class Roo.form.VTypes
8435  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8436  * @singleton
8437  */
8438 Roo.form.VTypes = function(){
8439     // closure these in so they are only created once.
8440     var alpha = /^[a-zA-Z_]+$/;
8441     var alphanum = /^[a-zA-Z0-9_]+$/;
8442     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8443     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8444
8445     // All these messages and functions are configurable
8446     return {
8447         /**
8448          * The function used to validate email addresses
8449          * @param {String} value The email address
8450          */
8451         'email' : function(v){
8452             return email.test(v);
8453         },
8454         /**
8455          * The error text to display when the email validation function returns false
8456          * @type String
8457          */
8458         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8459         /**
8460          * The keystroke filter mask to be applied on email input
8461          * @type RegExp
8462          */
8463         'emailMask' : /[a-z0-9_\.\-@]/i,
8464
8465         /**
8466          * The function used to validate URLs
8467          * @param {String} value The URL
8468          */
8469         'url' : function(v){
8470             return url.test(v);
8471         },
8472         /**
8473          * The error text to display when the url validation function returns false
8474          * @type String
8475          */
8476         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8477         
8478         /**
8479          * The function used to validate alpha values
8480          * @param {String} value The value
8481          */
8482         'alpha' : function(v){
8483             return alpha.test(v);
8484         },
8485         /**
8486          * The error text to display when the alpha validation function returns false
8487          * @type String
8488          */
8489         'alphaText' : 'This field should only contain letters and _',
8490         /**
8491          * The keystroke filter mask to be applied on alpha input
8492          * @type RegExp
8493          */
8494         'alphaMask' : /[a-z_]/i,
8495
8496         /**
8497          * The function used to validate alphanumeric values
8498          * @param {String} value The value
8499          */
8500         'alphanum' : function(v){
8501             return alphanum.test(v);
8502         },
8503         /**
8504          * The error text to display when the alphanumeric validation function returns false
8505          * @type String
8506          */
8507         'alphanumText' : 'This field should only contain letters, numbers and _',
8508         /**
8509          * The keystroke filter mask to be applied on alphanumeric input
8510          * @type RegExp
8511          */
8512         'alphanumMask' : /[a-z0-9_]/i
8513     };
8514 }();/*
8515  * - LGPL
8516  *
8517  * Input
8518  * 
8519  */
8520
8521 /**
8522  * @class Roo.bootstrap.Input
8523  * @extends Roo.bootstrap.Component
8524  * Bootstrap Input class
8525  * @cfg {Boolean} disabled is it disabled
8526  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8527  * @cfg {String} name name of the input
8528  * @cfg {string} fieldLabel - the label associated
8529  * @cfg {string} placeholder - placeholder to put in text.
8530  * @cfg {string}  before - input group add on before
8531  * @cfg {string} after - input group add on after
8532  * @cfg {string} size - (lg|sm) or leave empty..
8533  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8534  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8535  * @cfg {Number} md colspan out of 12 for computer-sized screens
8536  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8537  * @cfg {string} value default value of the input
8538  * @cfg {Number} labelWidth set the width of label 
8539  * @cfg {Number} labellg set the width of label (1-12)
8540  * @cfg {Number} labelmd set the width of label (1-12)
8541  * @cfg {Number} labelsm set the width of label (1-12)
8542  * @cfg {Number} labelxs set the width of label (1-12)
8543  * @cfg {String} labelAlign (top|left)
8544  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8545  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8546  * @cfg {String} indicatorpos (left|right) default left
8547  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8548  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8549
8550  * @cfg {String} align (left|center|right) Default left
8551  * @cfg {Boolean} forceFeedback (true|false) Default false
8552  * 
8553  * @constructor
8554  * Create a new Input
8555  * @param {Object} config The config object
8556  */
8557
8558 Roo.bootstrap.Input = function(config){
8559     
8560     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8561     
8562     this.addEvents({
8563         /**
8564          * @event focus
8565          * Fires when this field receives input focus.
8566          * @param {Roo.form.Field} this
8567          */
8568         focus : true,
8569         /**
8570          * @event blur
8571          * Fires when this field loses input focus.
8572          * @param {Roo.form.Field} this
8573          */
8574         blur : true,
8575         /**
8576          * @event specialkey
8577          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8578          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8579          * @param {Roo.form.Field} this
8580          * @param {Roo.EventObject} e The event object
8581          */
8582         specialkey : true,
8583         /**
8584          * @event change
8585          * Fires just before the field blurs if the field value has changed.
8586          * @param {Roo.form.Field} this
8587          * @param {Mixed} newValue The new value
8588          * @param {Mixed} oldValue The original value
8589          */
8590         change : true,
8591         /**
8592          * @event invalid
8593          * Fires after the field has been marked as invalid.
8594          * @param {Roo.form.Field} this
8595          * @param {String} msg The validation message
8596          */
8597         invalid : true,
8598         /**
8599          * @event valid
8600          * Fires after the field has been validated with no errors.
8601          * @param {Roo.form.Field} this
8602          */
8603         valid : true,
8604          /**
8605          * @event keyup
8606          * Fires after the key up
8607          * @param {Roo.form.Field} this
8608          * @param {Roo.EventObject}  e The event Object
8609          */
8610         keyup : true
8611     });
8612 };
8613
8614 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8615      /**
8616      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8617       automatic validation (defaults to "keyup").
8618      */
8619     validationEvent : "keyup",
8620      /**
8621      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8622      */
8623     validateOnBlur : true,
8624     /**
8625      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8626      */
8627     validationDelay : 250,
8628      /**
8629      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8630      */
8631     focusClass : "x-form-focus",  // not needed???
8632     
8633        
8634     /**
8635      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8636      */
8637     invalidClass : "has-warning",
8638     
8639     /**
8640      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8641      */
8642     validClass : "has-success",
8643     
8644     /**
8645      * @cfg {Boolean} hasFeedback (true|false) default true
8646      */
8647     hasFeedback : true,
8648     
8649     /**
8650      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8651      */
8652     invalidFeedbackClass : "glyphicon-warning-sign",
8653     
8654     /**
8655      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8656      */
8657     validFeedbackClass : "glyphicon-ok",
8658     
8659     /**
8660      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8661      */
8662     selectOnFocus : false,
8663     
8664      /**
8665      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8666      */
8667     maskRe : null,
8668        /**
8669      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8670      */
8671     vtype : null,
8672     
8673       /**
8674      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8675      */
8676     disableKeyFilter : false,
8677     
8678        /**
8679      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8680      */
8681     disabled : false,
8682      /**
8683      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8684      */
8685     allowBlank : true,
8686     /**
8687      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8688      */
8689     blankText : "Please complete this mandatory field",
8690     
8691      /**
8692      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8693      */
8694     minLength : 0,
8695     /**
8696      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8697      */
8698     maxLength : Number.MAX_VALUE,
8699     /**
8700      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8701      */
8702     minLengthText : "The minimum length for this field is {0}",
8703     /**
8704      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8705      */
8706     maxLengthText : "The maximum length for this field is {0}",
8707   
8708     
8709     /**
8710      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8711      * If available, this function will be called only after the basic validators all return true, and will be passed the
8712      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8713      */
8714     validator : null,
8715     /**
8716      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8717      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8718      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8719      */
8720     regex : null,
8721     /**
8722      * @cfg {String} regexText -- Depricated - use Invalid Text
8723      */
8724     regexText : "",
8725     
8726     /**
8727      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8728      */
8729     invalidText : "",
8730     
8731     
8732     
8733     autocomplete: false,
8734     
8735     
8736     fieldLabel : '',
8737     inputType : 'text',
8738     
8739     name : false,
8740     placeholder: false,
8741     before : false,
8742     after : false,
8743     size : false,
8744     hasFocus : false,
8745     preventMark: false,
8746     isFormField : true,
8747     value : '',
8748     labelWidth : 2,
8749     labelAlign : false,
8750     readOnly : false,
8751     align : false,
8752     formatedValue : false,
8753     forceFeedback : false,
8754     
8755     indicatorpos : 'left',
8756     
8757     labellg : 0,
8758     labelmd : 0,
8759     labelsm : 0,
8760     labelxs : 0,
8761     
8762     capture : '',
8763     accept : '',
8764     
8765     parentLabelAlign : function()
8766     {
8767         var parent = this;
8768         while (parent.parent()) {
8769             parent = parent.parent();
8770             if (typeof(parent.labelAlign) !='undefined') {
8771                 return parent.labelAlign;
8772             }
8773         }
8774         return 'left';
8775         
8776     },
8777     
8778     getAutoCreate : function()
8779     {
8780         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8781         
8782         var id = Roo.id();
8783         
8784         var cfg = {};
8785         
8786         if(this.inputType != 'hidden'){
8787             cfg.cls = 'form-group' //input-group
8788         }
8789         
8790         var input =  {
8791             tag: 'input',
8792             id : id,
8793             type : this.inputType,
8794             value : this.value,
8795             cls : 'form-control',
8796             placeholder : this.placeholder || '',
8797             autocomplete : this.autocomplete || 'new-password'
8798         };
8799         
8800         if(this.capture.length){
8801             input.capture = this.capture;
8802         }
8803         
8804         if(this.accept.length){
8805             input.accept = this.accept + "/*";
8806         }
8807         
8808         if(this.align){
8809             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8810         }
8811         
8812         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8813             input.maxLength = this.maxLength;
8814         }
8815         
8816         if (this.disabled) {
8817             input.disabled=true;
8818         }
8819         
8820         if (this.readOnly) {
8821             input.readonly=true;
8822         }
8823         
8824         if (this.name) {
8825             input.name = this.name;
8826         }
8827         
8828         if (this.size) {
8829             input.cls += ' input-' + this.size;
8830         }
8831         
8832         var settings=this;
8833         ['xs','sm','md','lg'].map(function(size){
8834             if (settings[size]) {
8835                 cfg.cls += ' col-' + size + '-' + settings[size];
8836             }
8837         });
8838         
8839         var inputblock = input;
8840         
8841         var feedback = {
8842             tag: 'span',
8843             cls: 'glyphicon form-control-feedback'
8844         };
8845             
8846         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8847             
8848             inputblock = {
8849                 cls : 'has-feedback',
8850                 cn :  [
8851                     input,
8852                     feedback
8853                 ] 
8854             };  
8855         }
8856         
8857         if (this.before || this.after) {
8858             
8859             inputblock = {
8860                 cls : 'input-group',
8861                 cn :  [] 
8862             };
8863             
8864             if (this.before && typeof(this.before) == 'string') {
8865                 
8866                 inputblock.cn.push({
8867                     tag :'span',
8868                     cls : 'roo-input-before input-group-addon',
8869                     html : this.before
8870                 });
8871             }
8872             if (this.before && typeof(this.before) == 'object') {
8873                 this.before = Roo.factory(this.before);
8874                 
8875                 inputblock.cn.push({
8876                     tag :'span',
8877                     cls : 'roo-input-before input-group-' +
8878                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8879                 });
8880             }
8881             
8882             inputblock.cn.push(input);
8883             
8884             if (this.after && typeof(this.after) == 'string') {
8885                 inputblock.cn.push({
8886                     tag :'span',
8887                     cls : 'roo-input-after input-group-addon',
8888                     html : this.after
8889                 });
8890             }
8891             if (this.after && typeof(this.after) == 'object') {
8892                 this.after = Roo.factory(this.after);
8893                 
8894                 inputblock.cn.push({
8895                     tag :'span',
8896                     cls : 'roo-input-after input-group-' +
8897                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8898                 });
8899             }
8900             
8901             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8902                 inputblock.cls += ' has-feedback';
8903                 inputblock.cn.push(feedback);
8904             }
8905         };
8906         
8907         if (align ==='left' && this.fieldLabel.length) {
8908             
8909             cfg.cls += ' roo-form-group-label-left';
8910             
8911             cfg.cn = [
8912                 {
8913                     tag : 'i',
8914                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8915                     tooltip : 'This field is required'
8916                 },
8917                 {
8918                     tag: 'label',
8919                     'for' :  id,
8920                     cls : 'control-label',
8921                     html : this.fieldLabel
8922
8923                 },
8924                 {
8925                     cls : "", 
8926                     cn: [
8927                         inputblock
8928                     ]
8929                 }
8930             ];
8931             
8932             var labelCfg = cfg.cn[1];
8933             var contentCfg = cfg.cn[2];
8934             
8935             if(this.indicatorpos == 'right'){
8936                 cfg.cn = [
8937                     {
8938                         tag: 'label',
8939                         'for' :  id,
8940                         cls : 'control-label',
8941                         cn : [
8942                             {
8943                                 tag : 'span',
8944                                 html : this.fieldLabel
8945                             },
8946                             {
8947                                 tag : 'i',
8948                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8949                                 tooltip : 'This field is required'
8950                             }
8951                         ]
8952                     },
8953                     {
8954                         cls : "",
8955                         cn: [
8956                             inputblock
8957                         ]
8958                     }
8959
8960                 ];
8961                 
8962                 labelCfg = cfg.cn[0];
8963                 contentCfg = cfg.cn[1];
8964             
8965             }
8966             
8967             if(this.labelWidth > 12){
8968                 labelCfg.style = "width: " + this.labelWidth + 'px';
8969             }
8970             
8971             if(this.labelWidth < 13 && this.labelmd == 0){
8972                 this.labelmd = this.labelWidth;
8973             }
8974             
8975             if(this.labellg > 0){
8976                 labelCfg.cls += ' col-lg-' + this.labellg;
8977                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8978             }
8979             
8980             if(this.labelmd > 0){
8981                 labelCfg.cls += ' col-md-' + this.labelmd;
8982                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8983             }
8984             
8985             if(this.labelsm > 0){
8986                 labelCfg.cls += ' col-sm-' + this.labelsm;
8987                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8988             }
8989             
8990             if(this.labelxs > 0){
8991                 labelCfg.cls += ' col-xs-' + this.labelxs;
8992                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8993             }
8994             
8995             
8996         } else if ( this.fieldLabel.length) {
8997                 
8998             cfg.cn = [
8999                 {
9000                     tag : 'i',
9001                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9002                     tooltip : 'This field is required'
9003                 },
9004                 {
9005                     tag: 'label',
9006                    //cls : 'input-group-addon',
9007                     html : this.fieldLabel
9008
9009                 },
9010
9011                inputblock
9012
9013            ];
9014            
9015            if(this.indicatorpos == 'right'){
9016                 
9017                 cfg.cn = [
9018                     {
9019                         tag: 'label',
9020                        //cls : 'input-group-addon',
9021                         html : this.fieldLabel
9022
9023                     },
9024                     {
9025                         tag : 'i',
9026                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9027                         tooltip : 'This field is required'
9028                     },
9029
9030                    inputblock
9031
9032                ];
9033
9034             }
9035
9036         } else {
9037             
9038             cfg.cn = [
9039
9040                     inputblock
9041
9042             ];
9043                 
9044                 
9045         };
9046         
9047         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9048            cfg.cls += ' navbar-form';
9049         }
9050         
9051         if (this.parentType === 'NavGroup') {
9052            cfg.cls += ' navbar-form';
9053            cfg.tag = 'li';
9054         }
9055         
9056         return cfg;
9057         
9058     },
9059     /**
9060      * return the real input element.
9061      */
9062     inputEl: function ()
9063     {
9064         return this.el.select('input.form-control',true).first();
9065     },
9066     
9067     tooltipEl : function()
9068     {
9069         return this.inputEl();
9070     },
9071     
9072     indicatorEl : function()
9073     {
9074         var indicator = this.el.select('i.roo-required-indicator',true).first();
9075         
9076         if(!indicator){
9077             return false;
9078         }
9079         
9080         return indicator;
9081         
9082     },
9083     
9084     setDisabled : function(v)
9085     {
9086         var i  = this.inputEl().dom;
9087         if (!v) {
9088             i.removeAttribute('disabled');
9089             return;
9090             
9091         }
9092         i.setAttribute('disabled','true');
9093     },
9094     initEvents : function()
9095     {
9096           
9097         this.inputEl().on("keydown" , this.fireKey,  this);
9098         this.inputEl().on("focus", this.onFocus,  this);
9099         this.inputEl().on("blur", this.onBlur,  this);
9100         
9101         this.inputEl().relayEvent('keyup', this);
9102         
9103         this.indicator = this.indicatorEl();
9104         
9105         if(this.indicator){
9106             this.indicator.addClass('invisible');
9107         }
9108  
9109         // reference to original value for reset
9110         this.originalValue = this.getValue();
9111         //Roo.form.TextField.superclass.initEvents.call(this);
9112         if(this.validationEvent == 'keyup'){
9113             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9114             this.inputEl().on('keyup', this.filterValidation, this);
9115         }
9116         else if(this.validationEvent !== false){
9117             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9118         }
9119         
9120         if(this.selectOnFocus){
9121             this.on("focus", this.preFocus, this);
9122             
9123         }
9124         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9125             this.inputEl().on("keypress", this.filterKeys, this);
9126         } else {
9127             this.inputEl().relayEvent('keypress', this);
9128         }
9129        /* if(this.grow){
9130             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9131             this.el.on("click", this.autoSize,  this);
9132         }
9133         */
9134         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9135             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9136         }
9137         
9138         if (typeof(this.before) == 'object') {
9139             this.before.render(this.el.select('.roo-input-before',true).first());
9140         }
9141         if (typeof(this.after) == 'object') {
9142             this.after.render(this.el.select('.roo-input-after',true).first());
9143         }
9144         
9145         this.inputEl().on('change', this.onChange, this);
9146         
9147     },
9148     filterValidation : function(e){
9149         if(!e.isNavKeyPress()){
9150             this.validationTask.delay(this.validationDelay);
9151         }
9152     },
9153      /**
9154      * Validates the field value
9155      * @return {Boolean} True if the value is valid, else false
9156      */
9157     validate : function(){
9158         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9159         if(this.disabled || this.validateValue(this.getRawValue())){
9160             this.markValid();
9161             return true;
9162         }
9163         
9164         this.markInvalid();
9165         return false;
9166     },
9167     
9168     
9169     /**
9170      * Validates a value according to the field's validation rules and marks the field as invalid
9171      * if the validation fails
9172      * @param {Mixed} value The value to validate
9173      * @return {Boolean} True if the value is valid, else false
9174      */
9175     validateValue : function(value)
9176     {
9177         if(this.getVisibilityEl().hasClass('hidden')){
9178             return true;
9179         }
9180         
9181         if(value.length < 1)  { // if it's blank
9182             if(this.allowBlank){
9183                 return true;
9184             }
9185             return false;
9186         }
9187         
9188         if(value.length < this.minLength){
9189             return false;
9190         }
9191         if(value.length > this.maxLength){
9192             return false;
9193         }
9194         if(this.vtype){
9195             var vt = Roo.form.VTypes;
9196             if(!vt[this.vtype](value, this)){
9197                 return false;
9198             }
9199         }
9200         if(typeof this.validator == "function"){
9201             var msg = this.validator(value);
9202             if(msg !== true){
9203                 return false;
9204             }
9205             if (typeof(msg) == 'string') {
9206                 this.invalidText = msg;
9207             }
9208         }
9209         
9210         if(this.regex && !this.regex.test(value)){
9211             return false;
9212         }
9213         
9214         return true;
9215     },
9216     
9217      // private
9218     fireKey : function(e){
9219         //Roo.log('field ' + e.getKey());
9220         if(e.isNavKeyPress()){
9221             this.fireEvent("specialkey", this, e);
9222         }
9223     },
9224     focus : function (selectText){
9225         if(this.rendered){
9226             this.inputEl().focus();
9227             if(selectText === true){
9228                 this.inputEl().dom.select();
9229             }
9230         }
9231         return this;
9232     } ,
9233     
9234     onFocus : function(){
9235         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9236            // this.el.addClass(this.focusClass);
9237         }
9238         if(!this.hasFocus){
9239             this.hasFocus = true;
9240             this.startValue = this.getValue();
9241             this.fireEvent("focus", this);
9242         }
9243     },
9244     
9245     beforeBlur : Roo.emptyFn,
9246
9247     
9248     // private
9249     onBlur : function(){
9250         this.beforeBlur();
9251         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9252             //this.el.removeClass(this.focusClass);
9253         }
9254         this.hasFocus = false;
9255         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9256             this.validate();
9257         }
9258         var v = this.getValue();
9259         if(String(v) !== String(this.startValue)){
9260             this.fireEvent('change', this, v, this.startValue);
9261         }
9262         this.fireEvent("blur", this);
9263     },
9264     
9265     onChange : function(e)
9266     {
9267         var v = this.getValue();
9268         if(String(v) !== String(this.startValue)){
9269             this.fireEvent('change', this, v, this.startValue);
9270         }
9271         
9272     },
9273     
9274     /**
9275      * Resets the current field value to the originally loaded value and clears any validation messages
9276      */
9277     reset : function(){
9278         this.setValue(this.originalValue);
9279         this.validate();
9280     },
9281      /**
9282      * Returns the name of the field
9283      * @return {Mixed} name The name field
9284      */
9285     getName: function(){
9286         return this.name;
9287     },
9288      /**
9289      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9290      * @return {Mixed} value The field value
9291      */
9292     getValue : function(){
9293         
9294         var v = this.inputEl().getValue();
9295         
9296         return v;
9297     },
9298     /**
9299      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9300      * @return {Mixed} value The field value
9301      */
9302     getRawValue : function(){
9303         var v = this.inputEl().getValue();
9304         
9305         return v;
9306     },
9307     
9308     /**
9309      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9310      * @param {Mixed} value The value to set
9311      */
9312     setRawValue : function(v){
9313         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9314     },
9315     
9316     selectText : function(start, end){
9317         var v = this.getRawValue();
9318         if(v.length > 0){
9319             start = start === undefined ? 0 : start;
9320             end = end === undefined ? v.length : end;
9321             var d = this.inputEl().dom;
9322             if(d.setSelectionRange){
9323                 d.setSelectionRange(start, end);
9324             }else if(d.createTextRange){
9325                 var range = d.createTextRange();
9326                 range.moveStart("character", start);
9327                 range.moveEnd("character", v.length-end);
9328                 range.select();
9329             }
9330         }
9331     },
9332     
9333     /**
9334      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9335      * @param {Mixed} value The value to set
9336      */
9337     setValue : function(v){
9338         this.value = v;
9339         if(this.rendered){
9340             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9341             this.validate();
9342         }
9343     },
9344     
9345     /*
9346     processValue : function(value){
9347         if(this.stripCharsRe){
9348             var newValue = value.replace(this.stripCharsRe, '');
9349             if(newValue !== value){
9350                 this.setRawValue(newValue);
9351                 return newValue;
9352             }
9353         }
9354         return value;
9355     },
9356   */
9357     preFocus : function(){
9358         
9359         if(this.selectOnFocus){
9360             this.inputEl().dom.select();
9361         }
9362     },
9363     filterKeys : function(e){
9364         var k = e.getKey();
9365         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9366             return;
9367         }
9368         var c = e.getCharCode(), cc = String.fromCharCode(c);
9369         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9370             return;
9371         }
9372         if(!this.maskRe.test(cc)){
9373             e.stopEvent();
9374         }
9375     },
9376      /**
9377      * Clear any invalid styles/messages for this field
9378      */
9379     clearInvalid : function(){
9380         
9381         if(!this.el || this.preventMark){ // not rendered
9382             return;
9383         }
9384         
9385      
9386         this.el.removeClass(this.invalidClass);
9387         
9388         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9389             
9390             var feedback = this.el.select('.form-control-feedback', true).first();
9391             
9392             if(feedback){
9393                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9394             }
9395             
9396         }
9397         
9398         this.fireEvent('valid', this);
9399     },
9400     
9401      /**
9402      * Mark this field as valid
9403      */
9404     markValid : function()
9405     {
9406         if(!this.el  || this.preventMark){ // not rendered...
9407             return;
9408         }
9409         
9410         this.el.removeClass([this.invalidClass, this.validClass]);
9411         
9412         var feedback = this.el.select('.form-control-feedback', true).first();
9413             
9414         if(feedback){
9415             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9416         }
9417         
9418         if(this.indicator){
9419             this.indicator.removeClass('visible');
9420             this.indicator.addClass('invisible');
9421         }
9422         
9423         if(this.disabled){
9424             return;
9425         }
9426         
9427         if(this.allowBlank && !this.getRawValue().length){
9428             return;
9429         }
9430         
9431         this.el.addClass(this.validClass);
9432         
9433         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9434             
9435             var feedback = this.el.select('.form-control-feedback', true).first();
9436             
9437             if(feedback){
9438                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9439                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9440             }
9441             
9442         }
9443         
9444         this.fireEvent('valid', this);
9445     },
9446     
9447      /**
9448      * Mark this field as invalid
9449      * @param {String} msg The validation message
9450      */
9451     markInvalid : function(msg)
9452     {
9453         if(!this.el  || this.preventMark){ // not rendered
9454             return;
9455         }
9456         
9457         this.el.removeClass([this.invalidClass, this.validClass]);
9458         
9459         var feedback = this.el.select('.form-control-feedback', true).first();
9460             
9461         if(feedback){
9462             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9463         }
9464
9465         if(this.disabled){
9466             return;
9467         }
9468         
9469         if(this.allowBlank && !this.getRawValue().length){
9470             return;
9471         }
9472         
9473         if(this.indicator){
9474             this.indicator.removeClass('invisible');
9475             this.indicator.addClass('visible');
9476         }
9477         
9478         this.el.addClass(this.invalidClass);
9479         
9480         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9481             
9482             var feedback = this.el.select('.form-control-feedback', true).first();
9483             
9484             if(feedback){
9485                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9486                 
9487                 if(this.getValue().length || this.forceFeedback){
9488                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9489                 }
9490                 
9491             }
9492             
9493         }
9494         
9495         this.fireEvent('invalid', this, msg);
9496     },
9497     // private
9498     SafariOnKeyDown : function(event)
9499     {
9500         // this is a workaround for a password hang bug on chrome/ webkit.
9501         if (this.inputEl().dom.type != 'password') {
9502             return;
9503         }
9504         
9505         var isSelectAll = false;
9506         
9507         if(this.inputEl().dom.selectionEnd > 0){
9508             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9509         }
9510         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9511             event.preventDefault();
9512             this.setValue('');
9513             return;
9514         }
9515         
9516         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9517             
9518             event.preventDefault();
9519             // this is very hacky as keydown always get's upper case.
9520             //
9521             var cc = String.fromCharCode(event.getCharCode());
9522             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9523             
9524         }
9525     },
9526     adjustWidth : function(tag, w){
9527         tag = tag.toLowerCase();
9528         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9529             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9530                 if(tag == 'input'){
9531                     return w + 2;
9532                 }
9533                 if(tag == 'textarea'){
9534                     return w-2;
9535                 }
9536             }else if(Roo.isOpera){
9537                 if(tag == 'input'){
9538                     return w + 2;
9539                 }
9540                 if(tag == 'textarea'){
9541                     return w-2;
9542                 }
9543             }
9544         }
9545         return w;
9546     },
9547     
9548     setFieldLabel : function(v)
9549     {
9550         if(!this.rendered){
9551             return;
9552         }
9553         
9554         if(this.indicator){
9555             var ar = this.el.select('label > span',true);
9556             
9557             if (ar.elements.length) {
9558                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9559                 this.fieldLabel = v;
9560                 return;
9561             }
9562             
9563             var br = this.el.select('label',true);
9564             
9565             if(br.elements.length) {
9566                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9567                 this.fieldLabel = v;
9568                 return;
9569             }
9570             
9571             Roo.log('Cannot Found any of label > span || label in input');
9572             return;
9573         }
9574         
9575         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9576         this.fieldLabel = v;
9577         
9578         
9579     }
9580 });
9581
9582  
9583 /*
9584  * - LGPL
9585  *
9586  * Input
9587  * 
9588  */
9589
9590 /**
9591  * @class Roo.bootstrap.TextArea
9592  * @extends Roo.bootstrap.Input
9593  * Bootstrap TextArea class
9594  * @cfg {Number} cols Specifies the visible width of a text area
9595  * @cfg {Number} rows Specifies the visible number of lines in a text area
9596  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9597  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9598  * @cfg {string} html text
9599  * 
9600  * @constructor
9601  * Create a new TextArea
9602  * @param {Object} config The config object
9603  */
9604
9605 Roo.bootstrap.TextArea = function(config){
9606     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9607    
9608 };
9609
9610 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9611      
9612     cols : false,
9613     rows : 5,
9614     readOnly : false,
9615     warp : 'soft',
9616     resize : false,
9617     value: false,
9618     html: false,
9619     
9620     getAutoCreate : function(){
9621         
9622         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9623         
9624         var id = Roo.id();
9625         
9626         var cfg = {};
9627         
9628         if(this.inputType != 'hidden'){
9629             cfg.cls = 'form-group' //input-group
9630         }
9631         
9632         var input =  {
9633             tag: 'textarea',
9634             id : id,
9635             warp : this.warp,
9636             rows : this.rows,
9637             value : this.value || '',
9638             html: this.html || '',
9639             cls : 'form-control',
9640             placeholder : this.placeholder || '' 
9641             
9642         };
9643         
9644         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9645             input.maxLength = this.maxLength;
9646         }
9647         
9648         if(this.resize){
9649             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9650         }
9651         
9652         if(this.cols){
9653             input.cols = this.cols;
9654         }
9655         
9656         if (this.readOnly) {
9657             input.readonly = true;
9658         }
9659         
9660         if (this.name) {
9661             input.name = this.name;
9662         }
9663         
9664         if (this.size) {
9665             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9666         }
9667         
9668         var settings=this;
9669         ['xs','sm','md','lg'].map(function(size){
9670             if (settings[size]) {
9671                 cfg.cls += ' col-' + size + '-' + settings[size];
9672             }
9673         });
9674         
9675         var inputblock = input;
9676         
9677         if(this.hasFeedback && !this.allowBlank){
9678             
9679             var feedback = {
9680                 tag: 'span',
9681                 cls: 'glyphicon form-control-feedback'
9682             };
9683
9684             inputblock = {
9685                 cls : 'has-feedback',
9686                 cn :  [
9687                     input,
9688                     feedback
9689                 ] 
9690             };  
9691         }
9692         
9693         
9694         if (this.before || this.after) {
9695             
9696             inputblock = {
9697                 cls : 'input-group',
9698                 cn :  [] 
9699             };
9700             if (this.before) {
9701                 inputblock.cn.push({
9702                     tag :'span',
9703                     cls : 'input-group-addon',
9704                     html : this.before
9705                 });
9706             }
9707             
9708             inputblock.cn.push(input);
9709             
9710             if(this.hasFeedback && !this.allowBlank){
9711                 inputblock.cls += ' has-feedback';
9712                 inputblock.cn.push(feedback);
9713             }
9714             
9715             if (this.after) {
9716                 inputblock.cn.push({
9717                     tag :'span',
9718                     cls : 'input-group-addon',
9719                     html : this.after
9720                 });
9721             }
9722             
9723         }
9724         
9725         if (align ==='left' && this.fieldLabel.length) {
9726             cfg.cn = [
9727                 {
9728                     tag: 'label',
9729                     'for' :  id,
9730                     cls : 'control-label',
9731                     html : this.fieldLabel
9732                 },
9733                 {
9734                     cls : "",
9735                     cn: [
9736                         inputblock
9737                     ]
9738                 }
9739
9740             ];
9741             
9742             if(this.labelWidth > 12){
9743                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9744             }
9745
9746             if(this.labelWidth < 13 && this.labelmd == 0){
9747                 this.labelmd = this.labelWidth;
9748             }
9749
9750             if(this.labellg > 0){
9751                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9752                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9753             }
9754
9755             if(this.labelmd > 0){
9756                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9757                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9758             }
9759
9760             if(this.labelsm > 0){
9761                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9762                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9763             }
9764
9765             if(this.labelxs > 0){
9766                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9767                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9768             }
9769             
9770         } else if ( this.fieldLabel.length) {
9771             cfg.cn = [
9772
9773                {
9774                    tag: 'label',
9775                    //cls : 'input-group-addon',
9776                    html : this.fieldLabel
9777
9778                },
9779
9780                inputblock
9781
9782            ];
9783
9784         } else {
9785
9786             cfg.cn = [
9787
9788                 inputblock
9789
9790             ];
9791                 
9792         }
9793         
9794         if (this.disabled) {
9795             input.disabled=true;
9796         }
9797         
9798         return cfg;
9799         
9800     },
9801     /**
9802      * return the real textarea element.
9803      */
9804     inputEl: function ()
9805     {
9806         return this.el.select('textarea.form-control',true).first();
9807     },
9808     
9809     /**
9810      * Clear any invalid styles/messages for this field
9811      */
9812     clearInvalid : function()
9813     {
9814         
9815         if(!this.el || this.preventMark){ // not rendered
9816             return;
9817         }
9818         
9819         var label = this.el.select('label', true).first();
9820         var icon = this.el.select('i.fa-star', true).first();
9821         
9822         if(label && icon){
9823             icon.remove();
9824         }
9825         
9826         this.el.removeClass(this.invalidClass);
9827         
9828         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9829             
9830             var feedback = this.el.select('.form-control-feedback', true).first();
9831             
9832             if(feedback){
9833                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9834             }
9835             
9836         }
9837         
9838         this.fireEvent('valid', this);
9839     },
9840     
9841      /**
9842      * Mark this field as valid
9843      */
9844     markValid : function()
9845     {
9846         if(!this.el  || this.preventMark){ // not rendered
9847             return;
9848         }
9849         
9850         this.el.removeClass([this.invalidClass, this.validClass]);
9851         
9852         var feedback = this.el.select('.form-control-feedback', true).first();
9853             
9854         if(feedback){
9855             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9856         }
9857
9858         if(this.disabled || this.allowBlank){
9859             return;
9860         }
9861         
9862         var label = this.el.select('label', true).first();
9863         var icon = this.el.select('i.fa-star', true).first();
9864         
9865         if(label && icon){
9866             icon.remove();
9867         }
9868         
9869         this.el.addClass(this.validClass);
9870         
9871         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9872             
9873             var feedback = this.el.select('.form-control-feedback', true).first();
9874             
9875             if(feedback){
9876                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9877                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9878             }
9879             
9880         }
9881         
9882         this.fireEvent('valid', this);
9883     },
9884     
9885      /**
9886      * Mark this field as invalid
9887      * @param {String} msg The validation message
9888      */
9889     markInvalid : function(msg)
9890     {
9891         if(!this.el  || this.preventMark){ // not rendered
9892             return;
9893         }
9894         
9895         this.el.removeClass([this.invalidClass, this.validClass]);
9896         
9897         var feedback = this.el.select('.form-control-feedback', true).first();
9898             
9899         if(feedback){
9900             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9901         }
9902
9903         if(this.disabled || this.allowBlank){
9904             return;
9905         }
9906         
9907         var label = this.el.select('label', true).first();
9908         var icon = this.el.select('i.fa-star', true).first();
9909         
9910         if(!this.getValue().length && label && !icon){
9911             this.el.createChild({
9912                 tag : 'i',
9913                 cls : 'text-danger fa fa-lg fa-star',
9914                 tooltip : 'This field is required',
9915                 style : 'margin-right:5px;'
9916             }, label, true);
9917         }
9918
9919         this.el.addClass(this.invalidClass);
9920         
9921         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9922             
9923             var feedback = this.el.select('.form-control-feedback', true).first();
9924             
9925             if(feedback){
9926                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9927                 
9928                 if(this.getValue().length || this.forceFeedback){
9929                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9930                 }
9931                 
9932             }
9933             
9934         }
9935         
9936         this.fireEvent('invalid', this, msg);
9937     }
9938 });
9939
9940  
9941 /*
9942  * - LGPL
9943  *
9944  * trigger field - base class for combo..
9945  * 
9946  */
9947  
9948 /**
9949  * @class Roo.bootstrap.TriggerField
9950  * @extends Roo.bootstrap.Input
9951  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9952  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9953  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9954  * for which you can provide a custom implementation.  For example:
9955  * <pre><code>
9956 var trigger = new Roo.bootstrap.TriggerField();
9957 trigger.onTriggerClick = myTriggerFn;
9958 trigger.applyTo('my-field');
9959 </code></pre>
9960  *
9961  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9962  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9963  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9964  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9965  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9966
9967  * @constructor
9968  * Create a new TriggerField.
9969  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9970  * to the base TextField)
9971  */
9972 Roo.bootstrap.TriggerField = function(config){
9973     this.mimicing = false;
9974     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9975 };
9976
9977 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9978     /**
9979      * @cfg {String} triggerClass A CSS class to apply to the trigger
9980      */
9981      /**
9982      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9983      */
9984     hideTrigger:false,
9985
9986     /**
9987      * @cfg {Boolean} removable (true|false) special filter default false
9988      */
9989     removable : false,
9990     
9991     /** @cfg {Boolean} grow @hide */
9992     /** @cfg {Number} growMin @hide */
9993     /** @cfg {Number} growMax @hide */
9994
9995     /**
9996      * @hide 
9997      * @method
9998      */
9999     autoSize: Roo.emptyFn,
10000     // private
10001     monitorTab : true,
10002     // private
10003     deferHeight : true,
10004
10005     
10006     actionMode : 'wrap',
10007     
10008     caret : false,
10009     
10010     
10011     getAutoCreate : function(){
10012        
10013         var align = this.labelAlign || this.parentLabelAlign();
10014         
10015         var id = Roo.id();
10016         
10017         var cfg = {
10018             cls: 'form-group' //input-group
10019         };
10020         
10021         
10022         var input =  {
10023             tag: 'input',
10024             id : id,
10025             type : this.inputType,
10026             cls : 'form-control',
10027             autocomplete: 'new-password',
10028             placeholder : this.placeholder || '' 
10029             
10030         };
10031         if (this.name) {
10032             input.name = this.name;
10033         }
10034         if (this.size) {
10035             input.cls += ' input-' + this.size;
10036         }
10037         
10038         if (this.disabled) {
10039             input.disabled=true;
10040         }
10041         
10042         var inputblock = input;
10043         
10044         if(this.hasFeedback && !this.allowBlank){
10045             
10046             var feedback = {
10047                 tag: 'span',
10048                 cls: 'glyphicon form-control-feedback'
10049             };
10050             
10051             if(this.removable && !this.editable && !this.tickable){
10052                 inputblock = {
10053                     cls : 'has-feedback',
10054                     cn :  [
10055                         inputblock,
10056                         {
10057                             tag: 'button',
10058                             html : 'x',
10059                             cls : 'roo-combo-removable-btn close'
10060                         },
10061                         feedback
10062                     ] 
10063                 };
10064             } else {
10065                 inputblock = {
10066                     cls : 'has-feedback',
10067                     cn :  [
10068                         inputblock,
10069                         feedback
10070                     ] 
10071                 };
10072             }
10073
10074         } else {
10075             if(this.removable && !this.editable && !this.tickable){
10076                 inputblock = {
10077                     cls : 'roo-removable',
10078                     cn :  [
10079                         inputblock,
10080                         {
10081                             tag: 'button',
10082                             html : 'x',
10083                             cls : 'roo-combo-removable-btn close'
10084                         }
10085                     ] 
10086                 };
10087             }
10088         }
10089         
10090         if (this.before || this.after) {
10091             
10092             inputblock = {
10093                 cls : 'input-group',
10094                 cn :  [] 
10095             };
10096             if (this.before) {
10097                 inputblock.cn.push({
10098                     tag :'span',
10099                     cls : 'input-group-addon',
10100                     html : this.before
10101                 });
10102             }
10103             
10104             inputblock.cn.push(input);
10105             
10106             if(this.hasFeedback && !this.allowBlank){
10107                 inputblock.cls += ' has-feedback';
10108                 inputblock.cn.push(feedback);
10109             }
10110             
10111             if (this.after) {
10112                 inputblock.cn.push({
10113                     tag :'span',
10114                     cls : 'input-group-addon',
10115                     html : this.after
10116                 });
10117             }
10118             
10119         };
10120         
10121         var box = {
10122             tag: 'div',
10123             cn: [
10124                 {
10125                     tag: 'input',
10126                     type : 'hidden',
10127                     cls: 'form-hidden-field'
10128                 },
10129                 inputblock
10130             ]
10131             
10132         };
10133         
10134         if(this.multiple){
10135             box = {
10136                 tag: 'div',
10137                 cn: [
10138                     {
10139                         tag: 'input',
10140                         type : 'hidden',
10141                         cls: 'form-hidden-field'
10142                     },
10143                     {
10144                         tag: 'ul',
10145                         cls: 'roo-select2-choices',
10146                         cn:[
10147                             {
10148                                 tag: 'li',
10149                                 cls: 'roo-select2-search-field',
10150                                 cn: [
10151
10152                                     inputblock
10153                                 ]
10154                             }
10155                         ]
10156                     }
10157                 ]
10158             }
10159         };
10160         
10161         var combobox = {
10162             cls: 'roo-select2-container input-group',
10163             cn: [
10164                 box
10165 //                {
10166 //                    tag: 'ul',
10167 //                    cls: 'typeahead typeahead-long dropdown-menu',
10168 //                    style: 'display:none'
10169 //                }
10170             ]
10171         };
10172         
10173         if(!this.multiple && this.showToggleBtn){
10174             
10175             var caret = {
10176                         tag: 'span',
10177                         cls: 'caret'
10178              };
10179             if (this.caret != false) {
10180                 caret = {
10181                      tag: 'i',
10182                      cls: 'fa fa-' + this.caret
10183                 };
10184                 
10185             }
10186             
10187             combobox.cn.push({
10188                 tag :'span',
10189                 cls : 'input-group-addon btn dropdown-toggle',
10190                 cn : [
10191                     caret,
10192                     {
10193                         tag: 'span',
10194                         cls: 'combobox-clear',
10195                         cn  : [
10196                             {
10197                                 tag : 'i',
10198                                 cls: 'icon-remove'
10199                             }
10200                         ]
10201                     }
10202                 ]
10203
10204             })
10205         }
10206         
10207         if(this.multiple){
10208             combobox.cls += ' roo-select2-container-multi';
10209         }
10210         
10211         if (align ==='left' && this.fieldLabel.length) {
10212             
10213             cfg.cls += ' roo-form-group-label-left';
10214
10215             cfg.cn = [
10216                 {
10217                     tag : 'i',
10218                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10219                     tooltip : 'This field is required'
10220                 },
10221                 {
10222                     tag: 'label',
10223                     'for' :  id,
10224                     cls : 'control-label',
10225                     html : this.fieldLabel
10226
10227                 },
10228                 {
10229                     cls : "", 
10230                     cn: [
10231                         combobox
10232                     ]
10233                 }
10234
10235             ];
10236             
10237             var labelCfg = cfg.cn[1];
10238             var contentCfg = cfg.cn[2];
10239             
10240             if(this.indicatorpos == 'right'){
10241                 cfg.cn = [
10242                     {
10243                         tag: 'label',
10244                         'for' :  id,
10245                         cls : 'control-label',
10246                         cn : [
10247                             {
10248                                 tag : 'span',
10249                                 html : this.fieldLabel
10250                             },
10251                             {
10252                                 tag : 'i',
10253                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10254                                 tooltip : 'This field is required'
10255                             }
10256                         ]
10257                     },
10258                     {
10259                         cls : "", 
10260                         cn: [
10261                             combobox
10262                         ]
10263                     }
10264
10265                 ];
10266                 
10267                 labelCfg = cfg.cn[0];
10268                 contentCfg = cfg.cn[1];
10269             }
10270             
10271             if(this.labelWidth > 12){
10272                 labelCfg.style = "width: " + this.labelWidth + 'px';
10273             }
10274             
10275             if(this.labelWidth < 13 && this.labelmd == 0){
10276                 this.labelmd = this.labelWidth;
10277             }
10278             
10279             if(this.labellg > 0){
10280                 labelCfg.cls += ' col-lg-' + this.labellg;
10281                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10282             }
10283             
10284             if(this.labelmd > 0){
10285                 labelCfg.cls += ' col-md-' + this.labelmd;
10286                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10287             }
10288             
10289             if(this.labelsm > 0){
10290                 labelCfg.cls += ' col-sm-' + this.labelsm;
10291                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10292             }
10293             
10294             if(this.labelxs > 0){
10295                 labelCfg.cls += ' col-xs-' + this.labelxs;
10296                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10297             }
10298             
10299         } else if ( this.fieldLabel.length) {
10300 //                Roo.log(" label");
10301             cfg.cn = [
10302                 {
10303                    tag : 'i',
10304                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10305                    tooltip : 'This field is required'
10306                },
10307                {
10308                    tag: 'label',
10309                    //cls : 'input-group-addon',
10310                    html : this.fieldLabel
10311
10312                },
10313
10314                combobox
10315
10316             ];
10317             
10318             if(this.indicatorpos == 'right'){
10319                 
10320                 cfg.cn = [
10321                     {
10322                        tag: 'label',
10323                        cn : [
10324                            {
10325                                tag : 'span',
10326                                html : this.fieldLabel
10327                            },
10328                            {
10329                               tag : 'i',
10330                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10331                               tooltip : 'This field is required'
10332                            }
10333                        ]
10334
10335                     },
10336                     combobox
10337
10338                 ];
10339
10340             }
10341
10342         } else {
10343             
10344 //                Roo.log(" no label && no align");
10345                 cfg = combobox
10346                      
10347                 
10348         }
10349         
10350         var settings=this;
10351         ['xs','sm','md','lg'].map(function(size){
10352             if (settings[size]) {
10353                 cfg.cls += ' col-' + size + '-' + settings[size];
10354             }
10355         });
10356         
10357         return cfg;
10358         
10359     },
10360     
10361     
10362     
10363     // private
10364     onResize : function(w, h){
10365 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10366 //        if(typeof w == 'number'){
10367 //            var x = w - this.trigger.getWidth();
10368 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10369 //            this.trigger.setStyle('left', x+'px');
10370 //        }
10371     },
10372
10373     // private
10374     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10375
10376     // private
10377     getResizeEl : function(){
10378         return this.inputEl();
10379     },
10380
10381     // private
10382     getPositionEl : function(){
10383         return this.inputEl();
10384     },
10385
10386     // private
10387     alignErrorIcon : function(){
10388         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10389     },
10390
10391     // private
10392     initEvents : function(){
10393         
10394         this.createList();
10395         
10396         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10397         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10398         if(!this.multiple && this.showToggleBtn){
10399             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10400             if(this.hideTrigger){
10401                 this.trigger.setDisplayed(false);
10402             }
10403             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10404         }
10405         
10406         if(this.multiple){
10407             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10408         }
10409         
10410         if(this.removable && !this.editable && !this.tickable){
10411             var close = this.closeTriggerEl();
10412             
10413             if(close){
10414                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10415                 close.on('click', this.removeBtnClick, this, close);
10416             }
10417         }
10418         
10419         //this.trigger.addClassOnOver('x-form-trigger-over');
10420         //this.trigger.addClassOnClick('x-form-trigger-click');
10421         
10422         //if(!this.width){
10423         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10424         //}
10425     },
10426     
10427     closeTriggerEl : function()
10428     {
10429         var close = this.el.select('.roo-combo-removable-btn', true).first();
10430         return close ? close : false;
10431     },
10432     
10433     removeBtnClick : function(e, h, el)
10434     {
10435         e.preventDefault();
10436         
10437         if(this.fireEvent("remove", this) !== false){
10438             this.reset();
10439             this.fireEvent("afterremove", this)
10440         }
10441     },
10442     
10443     createList : function()
10444     {
10445         this.list = Roo.get(document.body).createChild({
10446             tag: 'ul',
10447             cls: 'typeahead typeahead-long dropdown-menu',
10448             style: 'display:none'
10449         });
10450         
10451         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10452         
10453     },
10454
10455     // private
10456     initTrigger : function(){
10457        
10458     },
10459
10460     // private
10461     onDestroy : function(){
10462         if(this.trigger){
10463             this.trigger.removeAllListeners();
10464           //  this.trigger.remove();
10465         }
10466         //if(this.wrap){
10467         //    this.wrap.remove();
10468         //}
10469         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10470     },
10471
10472     // private
10473     onFocus : function(){
10474         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10475         /*
10476         if(!this.mimicing){
10477             this.wrap.addClass('x-trigger-wrap-focus');
10478             this.mimicing = true;
10479             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10480             if(this.monitorTab){
10481                 this.el.on("keydown", this.checkTab, this);
10482             }
10483         }
10484         */
10485     },
10486
10487     // private
10488     checkTab : function(e){
10489         if(e.getKey() == e.TAB){
10490             this.triggerBlur();
10491         }
10492     },
10493
10494     // private
10495     onBlur : function(){
10496         // do nothing
10497     },
10498
10499     // private
10500     mimicBlur : function(e, t){
10501         /*
10502         if(!this.wrap.contains(t) && this.validateBlur()){
10503             this.triggerBlur();
10504         }
10505         */
10506     },
10507
10508     // private
10509     triggerBlur : function(){
10510         this.mimicing = false;
10511         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10512         if(this.monitorTab){
10513             this.el.un("keydown", this.checkTab, this);
10514         }
10515         //this.wrap.removeClass('x-trigger-wrap-focus');
10516         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10517     },
10518
10519     // private
10520     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10521     validateBlur : function(e, t){
10522         return true;
10523     },
10524
10525     // private
10526     onDisable : function(){
10527         this.inputEl().dom.disabled = true;
10528         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10529         //if(this.wrap){
10530         //    this.wrap.addClass('x-item-disabled');
10531         //}
10532     },
10533
10534     // private
10535     onEnable : function(){
10536         this.inputEl().dom.disabled = false;
10537         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10538         //if(this.wrap){
10539         //    this.el.removeClass('x-item-disabled');
10540         //}
10541     },
10542
10543     // private
10544     onShow : function(){
10545         var ae = this.getActionEl();
10546         
10547         if(ae){
10548             ae.dom.style.display = '';
10549             ae.dom.style.visibility = 'visible';
10550         }
10551     },
10552
10553     // private
10554     
10555     onHide : function(){
10556         var ae = this.getActionEl();
10557         ae.dom.style.display = 'none';
10558     },
10559
10560     /**
10561      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10562      * by an implementing function.
10563      * @method
10564      * @param {EventObject} e
10565      */
10566     onTriggerClick : Roo.emptyFn
10567 });
10568  /*
10569  * Based on:
10570  * Ext JS Library 1.1.1
10571  * Copyright(c) 2006-2007, Ext JS, LLC.
10572  *
10573  * Originally Released Under LGPL - original licence link has changed is not relivant.
10574  *
10575  * Fork - LGPL
10576  * <script type="text/javascript">
10577  */
10578
10579
10580 /**
10581  * @class Roo.data.SortTypes
10582  * @singleton
10583  * Defines the default sorting (casting?) comparison functions used when sorting data.
10584  */
10585 Roo.data.SortTypes = {
10586     /**
10587      * Default sort that does nothing
10588      * @param {Mixed} s The value being converted
10589      * @return {Mixed} The comparison value
10590      */
10591     none : function(s){
10592         return s;
10593     },
10594     
10595     /**
10596      * The regular expression used to strip tags
10597      * @type {RegExp}
10598      * @property
10599      */
10600     stripTagsRE : /<\/?[^>]+>/gi,
10601     
10602     /**
10603      * Strips all HTML tags to sort on text only
10604      * @param {Mixed} s The value being converted
10605      * @return {String} The comparison value
10606      */
10607     asText : function(s){
10608         return String(s).replace(this.stripTagsRE, "");
10609     },
10610     
10611     /**
10612      * Strips all HTML tags to sort on text only - Case insensitive
10613      * @param {Mixed} s The value being converted
10614      * @return {String} The comparison value
10615      */
10616     asUCText : function(s){
10617         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10618     },
10619     
10620     /**
10621      * Case insensitive string
10622      * @param {Mixed} s The value being converted
10623      * @return {String} The comparison value
10624      */
10625     asUCString : function(s) {
10626         return String(s).toUpperCase();
10627     },
10628     
10629     /**
10630      * Date sorting
10631      * @param {Mixed} s The value being converted
10632      * @return {Number} The comparison value
10633      */
10634     asDate : function(s) {
10635         if(!s){
10636             return 0;
10637         }
10638         if(s instanceof Date){
10639             return s.getTime();
10640         }
10641         return Date.parse(String(s));
10642     },
10643     
10644     /**
10645      * Float sorting
10646      * @param {Mixed} s The value being converted
10647      * @return {Float} The comparison value
10648      */
10649     asFloat : function(s) {
10650         var val = parseFloat(String(s).replace(/,/g, ""));
10651         if(isNaN(val)) {
10652             val = 0;
10653         }
10654         return val;
10655     },
10656     
10657     /**
10658      * Integer sorting
10659      * @param {Mixed} s The value being converted
10660      * @return {Number} The comparison value
10661      */
10662     asInt : function(s) {
10663         var val = parseInt(String(s).replace(/,/g, ""));
10664         if(isNaN(val)) {
10665             val = 0;
10666         }
10667         return val;
10668     }
10669 };/*
10670  * Based on:
10671  * Ext JS Library 1.1.1
10672  * Copyright(c) 2006-2007, Ext JS, LLC.
10673  *
10674  * Originally Released Under LGPL - original licence link has changed is not relivant.
10675  *
10676  * Fork - LGPL
10677  * <script type="text/javascript">
10678  */
10679
10680 /**
10681 * @class Roo.data.Record
10682  * Instances of this class encapsulate both record <em>definition</em> information, and record
10683  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10684  * to access Records cached in an {@link Roo.data.Store} object.<br>
10685  * <p>
10686  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10687  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10688  * objects.<br>
10689  * <p>
10690  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10691  * @constructor
10692  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10693  * {@link #create}. The parameters are the same.
10694  * @param {Array} data An associative Array of data values keyed by the field name.
10695  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10696  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10697  * not specified an integer id is generated.
10698  */
10699 Roo.data.Record = function(data, id){
10700     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10701     this.data = data;
10702 };
10703
10704 /**
10705  * Generate a constructor for a specific record layout.
10706  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10707  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10708  * Each field definition object may contain the following properties: <ul>
10709  * <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,
10710  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10711  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10712  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10713  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10714  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10715  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10716  * this may be omitted.</p></li>
10717  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10718  * <ul><li>auto (Default, implies no conversion)</li>
10719  * <li>string</li>
10720  * <li>int</li>
10721  * <li>float</li>
10722  * <li>boolean</li>
10723  * <li>date</li></ul></p></li>
10724  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10725  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10726  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10727  * by the Reader into an object that will be stored in the Record. It is passed the
10728  * following parameters:<ul>
10729  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10730  * </ul></p></li>
10731  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10732  * </ul>
10733  * <br>usage:<br><pre><code>
10734 var TopicRecord = Roo.data.Record.create(
10735     {name: 'title', mapping: 'topic_title'},
10736     {name: 'author', mapping: 'username'},
10737     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10738     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10739     {name: 'lastPoster', mapping: 'user2'},
10740     {name: 'excerpt', mapping: 'post_text'}
10741 );
10742
10743 var myNewRecord = new TopicRecord({
10744     title: 'Do my job please',
10745     author: 'noobie',
10746     totalPosts: 1,
10747     lastPost: new Date(),
10748     lastPoster: 'Animal',
10749     excerpt: 'No way dude!'
10750 });
10751 myStore.add(myNewRecord);
10752 </code></pre>
10753  * @method create
10754  * @static
10755  */
10756 Roo.data.Record.create = function(o){
10757     var f = function(){
10758         f.superclass.constructor.apply(this, arguments);
10759     };
10760     Roo.extend(f, Roo.data.Record);
10761     var p = f.prototype;
10762     p.fields = new Roo.util.MixedCollection(false, function(field){
10763         return field.name;
10764     });
10765     for(var i = 0, len = o.length; i < len; i++){
10766         p.fields.add(new Roo.data.Field(o[i]));
10767     }
10768     f.getField = function(name){
10769         return p.fields.get(name);  
10770     };
10771     return f;
10772 };
10773
10774 Roo.data.Record.AUTO_ID = 1000;
10775 Roo.data.Record.EDIT = 'edit';
10776 Roo.data.Record.REJECT = 'reject';
10777 Roo.data.Record.COMMIT = 'commit';
10778
10779 Roo.data.Record.prototype = {
10780     /**
10781      * Readonly flag - true if this record has been modified.
10782      * @type Boolean
10783      */
10784     dirty : false,
10785     editing : false,
10786     error: null,
10787     modified: null,
10788
10789     // private
10790     join : function(store){
10791         this.store = store;
10792     },
10793
10794     /**
10795      * Set the named field to the specified value.
10796      * @param {String} name The name of the field to set.
10797      * @param {Object} value The value to set the field to.
10798      */
10799     set : function(name, value){
10800         if(this.data[name] == value){
10801             return;
10802         }
10803         this.dirty = true;
10804         if(!this.modified){
10805             this.modified = {};
10806         }
10807         if(typeof this.modified[name] == 'undefined'){
10808             this.modified[name] = this.data[name];
10809         }
10810         this.data[name] = value;
10811         if(!this.editing && this.store){
10812             this.store.afterEdit(this);
10813         }       
10814     },
10815
10816     /**
10817      * Get the value of the named field.
10818      * @param {String} name The name of the field to get the value of.
10819      * @return {Object} The value of the field.
10820      */
10821     get : function(name){
10822         return this.data[name]; 
10823     },
10824
10825     // private
10826     beginEdit : function(){
10827         this.editing = true;
10828         this.modified = {}; 
10829     },
10830
10831     // private
10832     cancelEdit : function(){
10833         this.editing = false;
10834         delete this.modified;
10835     },
10836
10837     // private
10838     endEdit : function(){
10839         this.editing = false;
10840         if(this.dirty && this.store){
10841             this.store.afterEdit(this);
10842         }
10843     },
10844
10845     /**
10846      * Usually called by the {@link Roo.data.Store} which owns the Record.
10847      * Rejects all changes made to the Record since either creation, or the last commit operation.
10848      * Modified fields are reverted to their original values.
10849      * <p>
10850      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10851      * of reject operations.
10852      */
10853     reject : function(){
10854         var m = this.modified;
10855         for(var n in m){
10856             if(typeof m[n] != "function"){
10857                 this.data[n] = m[n];
10858             }
10859         }
10860         this.dirty = false;
10861         delete this.modified;
10862         this.editing = false;
10863         if(this.store){
10864             this.store.afterReject(this);
10865         }
10866     },
10867
10868     /**
10869      * Usually called by the {@link Roo.data.Store} which owns the Record.
10870      * Commits all changes made to the Record since either creation, or the last commit operation.
10871      * <p>
10872      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10873      * of commit operations.
10874      */
10875     commit : function(){
10876         this.dirty = false;
10877         delete this.modified;
10878         this.editing = false;
10879         if(this.store){
10880             this.store.afterCommit(this);
10881         }
10882     },
10883
10884     // private
10885     hasError : function(){
10886         return this.error != null;
10887     },
10888
10889     // private
10890     clearError : function(){
10891         this.error = null;
10892     },
10893
10894     /**
10895      * Creates a copy of this record.
10896      * @param {String} id (optional) A new record id if you don't want to use this record's id
10897      * @return {Record}
10898      */
10899     copy : function(newId) {
10900         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10901     }
10902 };/*
10903  * Based on:
10904  * Ext JS Library 1.1.1
10905  * Copyright(c) 2006-2007, Ext JS, LLC.
10906  *
10907  * Originally Released Under LGPL - original licence link has changed is not relivant.
10908  *
10909  * Fork - LGPL
10910  * <script type="text/javascript">
10911  */
10912
10913
10914
10915 /**
10916  * @class Roo.data.Store
10917  * @extends Roo.util.Observable
10918  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10919  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10920  * <p>
10921  * 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
10922  * has no knowledge of the format of the data returned by the Proxy.<br>
10923  * <p>
10924  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10925  * instances from the data object. These records are cached and made available through accessor functions.
10926  * @constructor
10927  * Creates a new Store.
10928  * @param {Object} config A config object containing the objects needed for the Store to access data,
10929  * and read the data into Records.
10930  */
10931 Roo.data.Store = function(config){
10932     this.data = new Roo.util.MixedCollection(false);
10933     this.data.getKey = function(o){
10934         return o.id;
10935     };
10936     this.baseParams = {};
10937     // private
10938     this.paramNames = {
10939         "start" : "start",
10940         "limit" : "limit",
10941         "sort" : "sort",
10942         "dir" : "dir",
10943         "multisort" : "_multisort"
10944     };
10945
10946     if(config && config.data){
10947         this.inlineData = config.data;
10948         delete config.data;
10949     }
10950
10951     Roo.apply(this, config);
10952     
10953     if(this.reader){ // reader passed
10954         this.reader = Roo.factory(this.reader, Roo.data);
10955         this.reader.xmodule = this.xmodule || false;
10956         if(!this.recordType){
10957             this.recordType = this.reader.recordType;
10958         }
10959         if(this.reader.onMetaChange){
10960             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10961         }
10962     }
10963
10964     if(this.recordType){
10965         this.fields = this.recordType.prototype.fields;
10966     }
10967     this.modified = [];
10968
10969     this.addEvents({
10970         /**
10971          * @event datachanged
10972          * Fires when the data cache has changed, and a widget which is using this Store
10973          * as a Record cache should refresh its view.
10974          * @param {Store} this
10975          */
10976         datachanged : true,
10977         /**
10978          * @event metachange
10979          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10980          * @param {Store} this
10981          * @param {Object} meta The JSON metadata
10982          */
10983         metachange : true,
10984         /**
10985          * @event add
10986          * Fires when Records have been added to the Store
10987          * @param {Store} this
10988          * @param {Roo.data.Record[]} records The array of Records added
10989          * @param {Number} index The index at which the record(s) were added
10990          */
10991         add : true,
10992         /**
10993          * @event remove
10994          * Fires when a Record has been removed from the Store
10995          * @param {Store} this
10996          * @param {Roo.data.Record} record The Record that was removed
10997          * @param {Number} index The index at which the record was removed
10998          */
10999         remove : true,
11000         /**
11001          * @event update
11002          * Fires when a Record has been updated
11003          * @param {Store} this
11004          * @param {Roo.data.Record} record The Record that was updated
11005          * @param {String} operation The update operation being performed.  Value may be one of:
11006          * <pre><code>
11007  Roo.data.Record.EDIT
11008  Roo.data.Record.REJECT
11009  Roo.data.Record.COMMIT
11010          * </code></pre>
11011          */
11012         update : true,
11013         /**
11014          * @event clear
11015          * Fires when the data cache has been cleared.
11016          * @param {Store} this
11017          */
11018         clear : true,
11019         /**
11020          * @event beforeload
11021          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11022          * the load action will be canceled.
11023          * @param {Store} this
11024          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11025          */
11026         beforeload : true,
11027         /**
11028          * @event beforeloadadd
11029          * Fires after a new set of Records has been loaded.
11030          * @param {Store} this
11031          * @param {Roo.data.Record[]} records The Records that were loaded
11032          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11033          */
11034         beforeloadadd : true,
11035         /**
11036          * @event load
11037          * Fires after a new set of Records has been loaded, before they are added to the store.
11038          * @param {Store} this
11039          * @param {Roo.data.Record[]} records The Records that were loaded
11040          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11041          * @params {Object} return from reader
11042          */
11043         load : true,
11044         /**
11045          * @event loadexception
11046          * Fires if an exception occurs in the Proxy during loading.
11047          * Called with the signature of the Proxy's "loadexception" event.
11048          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11049          * 
11050          * @param {Proxy} 
11051          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11052          * @param {Object} load options 
11053          * @param {Object} jsonData from your request (normally this contains the Exception)
11054          */
11055         loadexception : true
11056     });
11057     
11058     if(this.proxy){
11059         this.proxy = Roo.factory(this.proxy, Roo.data);
11060         this.proxy.xmodule = this.xmodule || false;
11061         this.relayEvents(this.proxy,  ["loadexception"]);
11062     }
11063     this.sortToggle = {};
11064     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11065
11066     Roo.data.Store.superclass.constructor.call(this);
11067
11068     if(this.inlineData){
11069         this.loadData(this.inlineData);
11070         delete this.inlineData;
11071     }
11072 };
11073
11074 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11075      /**
11076     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11077     * without a remote query - used by combo/forms at present.
11078     */
11079     
11080     /**
11081     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11082     */
11083     /**
11084     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11085     */
11086     /**
11087     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11088     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11089     */
11090     /**
11091     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11092     * on any HTTP request
11093     */
11094     /**
11095     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11096     */
11097     /**
11098     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11099     */
11100     multiSort: false,
11101     /**
11102     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11103     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11104     */
11105     remoteSort : false,
11106
11107     /**
11108     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11109      * loaded or when a record is removed. (defaults to false).
11110     */
11111     pruneModifiedRecords : false,
11112
11113     // private
11114     lastOptions : null,
11115
11116     /**
11117      * Add Records to the Store and fires the add event.
11118      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11119      */
11120     add : function(records){
11121         records = [].concat(records);
11122         for(var i = 0, len = records.length; i < len; i++){
11123             records[i].join(this);
11124         }
11125         var index = this.data.length;
11126         this.data.addAll(records);
11127         this.fireEvent("add", this, records, index);
11128     },
11129
11130     /**
11131      * Remove a Record from the Store and fires the remove event.
11132      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11133      */
11134     remove : function(record){
11135         var index = this.data.indexOf(record);
11136         this.data.removeAt(index);
11137  
11138         if(this.pruneModifiedRecords){
11139             this.modified.remove(record);
11140         }
11141         this.fireEvent("remove", this, record, index);
11142     },
11143
11144     /**
11145      * Remove all Records from the Store and fires the clear event.
11146      */
11147     removeAll : function(){
11148         this.data.clear();
11149         if(this.pruneModifiedRecords){
11150             this.modified = [];
11151         }
11152         this.fireEvent("clear", this);
11153     },
11154
11155     /**
11156      * Inserts Records to the Store at the given index and fires the add event.
11157      * @param {Number} index The start index at which to insert the passed Records.
11158      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11159      */
11160     insert : function(index, records){
11161         records = [].concat(records);
11162         for(var i = 0, len = records.length; i < len; i++){
11163             this.data.insert(index, records[i]);
11164             records[i].join(this);
11165         }
11166         this.fireEvent("add", this, records, index);
11167     },
11168
11169     /**
11170      * Get the index within the cache of the passed Record.
11171      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11172      * @return {Number} The index of the passed Record. Returns -1 if not found.
11173      */
11174     indexOf : function(record){
11175         return this.data.indexOf(record);
11176     },
11177
11178     /**
11179      * Get the index within the cache of the Record with the passed id.
11180      * @param {String} id The id of the Record to find.
11181      * @return {Number} The index of the Record. Returns -1 if not found.
11182      */
11183     indexOfId : function(id){
11184         return this.data.indexOfKey(id);
11185     },
11186
11187     /**
11188      * Get the Record with the specified id.
11189      * @param {String} id The id of the Record to find.
11190      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11191      */
11192     getById : function(id){
11193         return this.data.key(id);
11194     },
11195
11196     /**
11197      * Get the Record at the specified index.
11198      * @param {Number} index The index of the Record to find.
11199      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11200      */
11201     getAt : function(index){
11202         return this.data.itemAt(index);
11203     },
11204
11205     /**
11206      * Returns a range of Records between specified indices.
11207      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11208      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11209      * @return {Roo.data.Record[]} An array of Records
11210      */
11211     getRange : function(start, end){
11212         return this.data.getRange(start, end);
11213     },
11214
11215     // private
11216     storeOptions : function(o){
11217         o = Roo.apply({}, o);
11218         delete o.callback;
11219         delete o.scope;
11220         this.lastOptions = o;
11221     },
11222
11223     /**
11224      * Loads the Record cache from the configured Proxy using the configured Reader.
11225      * <p>
11226      * If using remote paging, then the first load call must specify the <em>start</em>
11227      * and <em>limit</em> properties in the options.params property to establish the initial
11228      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11229      * <p>
11230      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11231      * and this call will return before the new data has been loaded. Perform any post-processing
11232      * in a callback function, or in a "load" event handler.</strong>
11233      * <p>
11234      * @param {Object} options An object containing properties which control loading options:<ul>
11235      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11236      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11237      * passed the following arguments:<ul>
11238      * <li>r : Roo.data.Record[]</li>
11239      * <li>options: Options object from the load call</li>
11240      * <li>success: Boolean success indicator</li></ul></li>
11241      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11242      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11243      * </ul>
11244      */
11245     load : function(options){
11246         options = options || {};
11247         if(this.fireEvent("beforeload", this, options) !== false){
11248             this.storeOptions(options);
11249             var p = Roo.apply(options.params || {}, this.baseParams);
11250             // if meta was not loaded from remote source.. try requesting it.
11251             if (!this.reader.metaFromRemote) {
11252                 p._requestMeta = 1;
11253             }
11254             if(this.sortInfo && this.remoteSort){
11255                 var pn = this.paramNames;
11256                 p[pn["sort"]] = this.sortInfo.field;
11257                 p[pn["dir"]] = this.sortInfo.direction;
11258             }
11259             if (this.multiSort) {
11260                 var pn = this.paramNames;
11261                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11262             }
11263             
11264             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11265         }
11266     },
11267
11268     /**
11269      * Reloads the Record cache from the configured Proxy using the configured Reader and
11270      * the options from the last load operation performed.
11271      * @param {Object} options (optional) An object containing properties which may override the options
11272      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11273      * the most recently used options are reused).
11274      */
11275     reload : function(options){
11276         this.load(Roo.applyIf(options||{}, this.lastOptions));
11277     },
11278
11279     // private
11280     // Called as a callback by the Reader during a load operation.
11281     loadRecords : function(o, options, success){
11282         if(!o || success === false){
11283             if(success !== false){
11284                 this.fireEvent("load", this, [], options, o);
11285             }
11286             if(options.callback){
11287                 options.callback.call(options.scope || this, [], options, false);
11288             }
11289             return;
11290         }
11291         // if data returned failure - throw an exception.
11292         if (o.success === false) {
11293             // show a message if no listener is registered.
11294             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11295                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11296             }
11297             // loadmask wil be hooked into this..
11298             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11299             return;
11300         }
11301         var r = o.records, t = o.totalRecords || r.length;
11302         
11303         this.fireEvent("beforeloadadd", this, r, options, o);
11304         
11305         if(!options || options.add !== true){
11306             if(this.pruneModifiedRecords){
11307                 this.modified = [];
11308             }
11309             for(var i = 0, len = r.length; i < len; i++){
11310                 r[i].join(this);
11311             }
11312             if(this.snapshot){
11313                 this.data = this.snapshot;
11314                 delete this.snapshot;
11315             }
11316             this.data.clear();
11317             this.data.addAll(r);
11318             this.totalLength = t;
11319             this.applySort();
11320             this.fireEvent("datachanged", this);
11321         }else{
11322             this.totalLength = Math.max(t, this.data.length+r.length);
11323             this.add(r);
11324         }
11325         
11326         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11327                 
11328             var e = new Roo.data.Record({});
11329
11330             e.set(this.parent.displayField, this.parent.emptyTitle);
11331             e.set(this.parent.valueField, '');
11332
11333             this.insert(0, e);
11334         }
11335             
11336         this.fireEvent("load", this, r, options, o);
11337         if(options.callback){
11338             options.callback.call(options.scope || this, r, options, true);
11339         }
11340     },
11341
11342
11343     /**
11344      * Loads data from a passed data block. A Reader which understands the format of the data
11345      * must have been configured in the constructor.
11346      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11347      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11348      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11349      */
11350     loadData : function(o, append){
11351         var r = this.reader.readRecords(o);
11352         this.loadRecords(r, {add: append}, true);
11353     },
11354
11355     /**
11356      * Gets the number of cached records.
11357      * <p>
11358      * <em>If using paging, this may not be the total size of the dataset. If the data object
11359      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11360      * the data set size</em>
11361      */
11362     getCount : function(){
11363         return this.data.length || 0;
11364     },
11365
11366     /**
11367      * Gets the total number of records in the dataset as returned by the server.
11368      * <p>
11369      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11370      * the dataset size</em>
11371      */
11372     getTotalCount : function(){
11373         return this.totalLength || 0;
11374     },
11375
11376     /**
11377      * Returns the sort state of the Store as an object with two properties:
11378      * <pre><code>
11379  field {String} The name of the field by which the Records are sorted
11380  direction {String} The sort order, "ASC" or "DESC"
11381      * </code></pre>
11382      */
11383     getSortState : function(){
11384         return this.sortInfo;
11385     },
11386
11387     // private
11388     applySort : function(){
11389         if(this.sortInfo && !this.remoteSort){
11390             var s = this.sortInfo, f = s.field;
11391             var st = this.fields.get(f).sortType;
11392             var fn = function(r1, r2){
11393                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11394                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11395             };
11396             this.data.sort(s.direction, fn);
11397             if(this.snapshot && this.snapshot != this.data){
11398                 this.snapshot.sort(s.direction, fn);
11399             }
11400         }
11401     },
11402
11403     /**
11404      * Sets the default sort column and order to be used by the next load operation.
11405      * @param {String} fieldName The name of the field to sort by.
11406      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11407      */
11408     setDefaultSort : function(field, dir){
11409         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11410     },
11411
11412     /**
11413      * Sort the Records.
11414      * If remote sorting is used, the sort is performed on the server, and the cache is
11415      * reloaded. If local sorting is used, the cache is sorted internally.
11416      * @param {String} fieldName The name of the field to sort by.
11417      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11418      */
11419     sort : function(fieldName, dir){
11420         var f = this.fields.get(fieldName);
11421         if(!dir){
11422             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11423             
11424             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11425                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11426             }else{
11427                 dir = f.sortDir;
11428             }
11429         }
11430         this.sortToggle[f.name] = dir;
11431         this.sortInfo = {field: f.name, direction: dir};
11432         if(!this.remoteSort){
11433             this.applySort();
11434             this.fireEvent("datachanged", this);
11435         }else{
11436             this.load(this.lastOptions);
11437         }
11438     },
11439
11440     /**
11441      * Calls the specified function for each of the Records in the cache.
11442      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11443      * Returning <em>false</em> aborts and exits the iteration.
11444      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11445      */
11446     each : function(fn, scope){
11447         this.data.each(fn, scope);
11448     },
11449
11450     /**
11451      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11452      * (e.g., during paging).
11453      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11454      */
11455     getModifiedRecords : function(){
11456         return this.modified;
11457     },
11458
11459     // private
11460     createFilterFn : function(property, value, anyMatch){
11461         if(!value.exec){ // not a regex
11462             value = String(value);
11463             if(value.length == 0){
11464                 return false;
11465             }
11466             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11467         }
11468         return function(r){
11469             return value.test(r.data[property]);
11470         };
11471     },
11472
11473     /**
11474      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11475      * @param {String} property A field on your records
11476      * @param {Number} start The record index to start at (defaults to 0)
11477      * @param {Number} end The last record index to include (defaults to length - 1)
11478      * @return {Number} The sum
11479      */
11480     sum : function(property, start, end){
11481         var rs = this.data.items, v = 0;
11482         start = start || 0;
11483         end = (end || end === 0) ? end : rs.length-1;
11484
11485         for(var i = start; i <= end; i++){
11486             v += (rs[i].data[property] || 0);
11487         }
11488         return v;
11489     },
11490
11491     /**
11492      * Filter the records by a specified property.
11493      * @param {String} field A field on your records
11494      * @param {String/RegExp} value Either a string that the field
11495      * should start with or a RegExp to test against the field
11496      * @param {Boolean} anyMatch True to match any part not just the beginning
11497      */
11498     filter : function(property, value, anyMatch){
11499         var fn = this.createFilterFn(property, value, anyMatch);
11500         return fn ? this.filterBy(fn) : this.clearFilter();
11501     },
11502
11503     /**
11504      * Filter by a function. The specified function will be called with each
11505      * record in this data source. If the function returns true the record is included,
11506      * otherwise it is filtered.
11507      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11508      * @param {Object} scope (optional) The scope of the function (defaults to this)
11509      */
11510     filterBy : function(fn, scope){
11511         this.snapshot = this.snapshot || this.data;
11512         this.data = this.queryBy(fn, scope||this);
11513         this.fireEvent("datachanged", this);
11514     },
11515
11516     /**
11517      * Query the records by a specified property.
11518      * @param {String} field A field on your records
11519      * @param {String/RegExp} value Either a string that the field
11520      * should start with or a RegExp to test against the field
11521      * @param {Boolean} anyMatch True to match any part not just the beginning
11522      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11523      */
11524     query : function(property, value, anyMatch){
11525         var fn = this.createFilterFn(property, value, anyMatch);
11526         return fn ? this.queryBy(fn) : this.data.clone();
11527     },
11528
11529     /**
11530      * Query by a function. The specified function will be called with each
11531      * record in this data source. If the function returns true the record is included
11532      * in the results.
11533      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11534      * @param {Object} scope (optional) The scope of the function (defaults to this)
11535       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11536      **/
11537     queryBy : function(fn, scope){
11538         var data = this.snapshot || this.data;
11539         return data.filterBy(fn, scope||this);
11540     },
11541
11542     /**
11543      * Collects unique values for a particular dataIndex from this store.
11544      * @param {String} dataIndex The property to collect
11545      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11546      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11547      * @return {Array} An array of the unique values
11548      **/
11549     collect : function(dataIndex, allowNull, bypassFilter){
11550         var d = (bypassFilter === true && this.snapshot) ?
11551                 this.snapshot.items : this.data.items;
11552         var v, sv, r = [], l = {};
11553         for(var i = 0, len = d.length; i < len; i++){
11554             v = d[i].data[dataIndex];
11555             sv = String(v);
11556             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11557                 l[sv] = true;
11558                 r[r.length] = v;
11559             }
11560         }
11561         return r;
11562     },
11563
11564     /**
11565      * Revert to a view of the Record cache with no filtering applied.
11566      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11567      */
11568     clearFilter : function(suppressEvent){
11569         if(this.snapshot && this.snapshot != this.data){
11570             this.data = this.snapshot;
11571             delete this.snapshot;
11572             if(suppressEvent !== true){
11573                 this.fireEvent("datachanged", this);
11574             }
11575         }
11576     },
11577
11578     // private
11579     afterEdit : function(record){
11580         if(this.modified.indexOf(record) == -1){
11581             this.modified.push(record);
11582         }
11583         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11584     },
11585     
11586     // private
11587     afterReject : function(record){
11588         this.modified.remove(record);
11589         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11590     },
11591
11592     // private
11593     afterCommit : function(record){
11594         this.modified.remove(record);
11595         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11596     },
11597
11598     /**
11599      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11600      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11601      */
11602     commitChanges : function(){
11603         var m = this.modified.slice(0);
11604         this.modified = [];
11605         for(var i = 0, len = m.length; i < len; i++){
11606             m[i].commit();
11607         }
11608     },
11609
11610     /**
11611      * Cancel outstanding changes on all changed records.
11612      */
11613     rejectChanges : function(){
11614         var m = this.modified.slice(0);
11615         this.modified = [];
11616         for(var i = 0, len = m.length; i < len; i++){
11617             m[i].reject();
11618         }
11619     },
11620
11621     onMetaChange : function(meta, rtype, o){
11622         this.recordType = rtype;
11623         this.fields = rtype.prototype.fields;
11624         delete this.snapshot;
11625         this.sortInfo = meta.sortInfo || this.sortInfo;
11626         this.modified = [];
11627         this.fireEvent('metachange', this, this.reader.meta);
11628     },
11629     
11630     moveIndex : function(data, type)
11631     {
11632         var index = this.indexOf(data);
11633         
11634         var newIndex = index + type;
11635         
11636         this.remove(data);
11637         
11638         this.insert(newIndex, data);
11639         
11640     }
11641 });/*
11642  * Based on:
11643  * Ext JS Library 1.1.1
11644  * Copyright(c) 2006-2007, Ext JS, LLC.
11645  *
11646  * Originally Released Under LGPL - original licence link has changed is not relivant.
11647  *
11648  * Fork - LGPL
11649  * <script type="text/javascript">
11650  */
11651
11652 /**
11653  * @class Roo.data.SimpleStore
11654  * @extends Roo.data.Store
11655  * Small helper class to make creating Stores from Array data easier.
11656  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11657  * @cfg {Array} fields An array of field definition objects, or field name strings.
11658  * @cfg {Array} data The multi-dimensional array of data
11659  * @constructor
11660  * @param {Object} config
11661  */
11662 Roo.data.SimpleStore = function(config){
11663     Roo.data.SimpleStore.superclass.constructor.call(this, {
11664         isLocal : true,
11665         reader: new Roo.data.ArrayReader({
11666                 id: config.id
11667             },
11668             Roo.data.Record.create(config.fields)
11669         ),
11670         proxy : new Roo.data.MemoryProxy(config.data)
11671     });
11672     this.load();
11673 };
11674 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11675  * Based on:
11676  * Ext JS Library 1.1.1
11677  * Copyright(c) 2006-2007, Ext JS, LLC.
11678  *
11679  * Originally Released Under LGPL - original licence link has changed is not relivant.
11680  *
11681  * Fork - LGPL
11682  * <script type="text/javascript">
11683  */
11684
11685 /**
11686 /**
11687  * @extends Roo.data.Store
11688  * @class Roo.data.JsonStore
11689  * Small helper class to make creating Stores for JSON data easier. <br/>
11690 <pre><code>
11691 var store = new Roo.data.JsonStore({
11692     url: 'get-images.php',
11693     root: 'images',
11694     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11695 });
11696 </code></pre>
11697  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11698  * JsonReader and HttpProxy (unless inline data is provided).</b>
11699  * @cfg {Array} fields An array of field definition objects, or field name strings.
11700  * @constructor
11701  * @param {Object} config
11702  */
11703 Roo.data.JsonStore = function(c){
11704     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11705         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11706         reader: new Roo.data.JsonReader(c, c.fields)
11707     }));
11708 };
11709 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11710  * Based on:
11711  * Ext JS Library 1.1.1
11712  * Copyright(c) 2006-2007, Ext JS, LLC.
11713  *
11714  * Originally Released Under LGPL - original licence link has changed is not relivant.
11715  *
11716  * Fork - LGPL
11717  * <script type="text/javascript">
11718  */
11719
11720  
11721 Roo.data.Field = function(config){
11722     if(typeof config == "string"){
11723         config = {name: config};
11724     }
11725     Roo.apply(this, config);
11726     
11727     if(!this.type){
11728         this.type = "auto";
11729     }
11730     
11731     var st = Roo.data.SortTypes;
11732     // named sortTypes are supported, here we look them up
11733     if(typeof this.sortType == "string"){
11734         this.sortType = st[this.sortType];
11735     }
11736     
11737     // set default sortType for strings and dates
11738     if(!this.sortType){
11739         switch(this.type){
11740             case "string":
11741                 this.sortType = st.asUCString;
11742                 break;
11743             case "date":
11744                 this.sortType = st.asDate;
11745                 break;
11746             default:
11747                 this.sortType = st.none;
11748         }
11749     }
11750
11751     // define once
11752     var stripRe = /[\$,%]/g;
11753
11754     // prebuilt conversion function for this field, instead of
11755     // switching every time we're reading a value
11756     if(!this.convert){
11757         var cv, dateFormat = this.dateFormat;
11758         switch(this.type){
11759             case "":
11760             case "auto":
11761             case undefined:
11762                 cv = function(v){ return v; };
11763                 break;
11764             case "string":
11765                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11766                 break;
11767             case "int":
11768                 cv = function(v){
11769                     return v !== undefined && v !== null && v !== '' ?
11770                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11771                     };
11772                 break;
11773             case "float":
11774                 cv = function(v){
11775                     return v !== undefined && v !== null && v !== '' ?
11776                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11777                     };
11778                 break;
11779             case "bool":
11780             case "boolean":
11781                 cv = function(v){ return v === true || v === "true" || v == 1; };
11782                 break;
11783             case "date":
11784                 cv = function(v){
11785                     if(!v){
11786                         return '';
11787                     }
11788                     if(v instanceof Date){
11789                         return v;
11790                     }
11791                     if(dateFormat){
11792                         if(dateFormat == "timestamp"){
11793                             return new Date(v*1000);
11794                         }
11795                         return Date.parseDate(v, dateFormat);
11796                     }
11797                     var parsed = Date.parse(v);
11798                     return parsed ? new Date(parsed) : null;
11799                 };
11800              break;
11801             
11802         }
11803         this.convert = cv;
11804     }
11805 };
11806
11807 Roo.data.Field.prototype = {
11808     dateFormat: null,
11809     defaultValue: "",
11810     mapping: null,
11811     sortType : null,
11812     sortDir : "ASC"
11813 };/*
11814  * Based on:
11815  * Ext JS Library 1.1.1
11816  * Copyright(c) 2006-2007, Ext JS, LLC.
11817  *
11818  * Originally Released Under LGPL - original licence link has changed is not relivant.
11819  *
11820  * Fork - LGPL
11821  * <script type="text/javascript">
11822  */
11823  
11824 // Base class for reading structured data from a data source.  This class is intended to be
11825 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11826
11827 /**
11828  * @class Roo.data.DataReader
11829  * Base class for reading structured data from a data source.  This class is intended to be
11830  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11831  */
11832
11833 Roo.data.DataReader = function(meta, recordType){
11834     
11835     this.meta = meta;
11836     
11837     this.recordType = recordType instanceof Array ? 
11838         Roo.data.Record.create(recordType) : recordType;
11839 };
11840
11841 Roo.data.DataReader.prototype = {
11842      /**
11843      * Create an empty record
11844      * @param {Object} data (optional) - overlay some values
11845      * @return {Roo.data.Record} record created.
11846      */
11847     newRow :  function(d) {
11848         var da =  {};
11849         this.recordType.prototype.fields.each(function(c) {
11850             switch( c.type) {
11851                 case 'int' : da[c.name] = 0; break;
11852                 case 'date' : da[c.name] = new Date(); break;
11853                 case 'float' : da[c.name] = 0.0; break;
11854                 case 'boolean' : da[c.name] = false; break;
11855                 default : da[c.name] = ""; break;
11856             }
11857             
11858         });
11859         return new this.recordType(Roo.apply(da, d));
11860     }
11861     
11862 };/*
11863  * Based on:
11864  * Ext JS Library 1.1.1
11865  * Copyright(c) 2006-2007, Ext JS, LLC.
11866  *
11867  * Originally Released Under LGPL - original licence link has changed is not relivant.
11868  *
11869  * Fork - LGPL
11870  * <script type="text/javascript">
11871  */
11872
11873 /**
11874  * @class Roo.data.DataProxy
11875  * @extends Roo.data.Observable
11876  * This class is an abstract base class for implementations which provide retrieval of
11877  * unformatted data objects.<br>
11878  * <p>
11879  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11880  * (of the appropriate type which knows how to parse the data object) to provide a block of
11881  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11882  * <p>
11883  * Custom implementations must implement the load method as described in
11884  * {@link Roo.data.HttpProxy#load}.
11885  */
11886 Roo.data.DataProxy = function(){
11887     this.addEvents({
11888         /**
11889          * @event beforeload
11890          * Fires before a network request is made to retrieve a data object.
11891          * @param {Object} This DataProxy object.
11892          * @param {Object} params The params parameter to the load function.
11893          */
11894         beforeload : true,
11895         /**
11896          * @event load
11897          * Fires before the load method's callback is called.
11898          * @param {Object} This DataProxy object.
11899          * @param {Object} o The data object.
11900          * @param {Object} arg The callback argument object passed to the load function.
11901          */
11902         load : true,
11903         /**
11904          * @event loadexception
11905          * Fires if an Exception occurs during data retrieval.
11906          * @param {Object} This DataProxy object.
11907          * @param {Object} o The data object.
11908          * @param {Object} arg The callback argument object passed to the load function.
11909          * @param {Object} e The Exception.
11910          */
11911         loadexception : true
11912     });
11913     Roo.data.DataProxy.superclass.constructor.call(this);
11914 };
11915
11916 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11917
11918     /**
11919      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11920      */
11921 /*
11922  * Based on:
11923  * Ext JS Library 1.1.1
11924  * Copyright(c) 2006-2007, Ext JS, LLC.
11925  *
11926  * Originally Released Under LGPL - original licence link has changed is not relivant.
11927  *
11928  * Fork - LGPL
11929  * <script type="text/javascript">
11930  */
11931 /**
11932  * @class Roo.data.MemoryProxy
11933  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11934  * to the Reader when its load method is called.
11935  * @constructor
11936  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11937  */
11938 Roo.data.MemoryProxy = function(data){
11939     if (data.data) {
11940         data = data.data;
11941     }
11942     Roo.data.MemoryProxy.superclass.constructor.call(this);
11943     this.data = data;
11944 };
11945
11946 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11947     
11948     /**
11949      * Load data from the requested source (in this case an in-memory
11950      * data object passed to the constructor), read the data object into
11951      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11952      * process that block using the passed callback.
11953      * @param {Object} params This parameter is not used by the MemoryProxy class.
11954      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11955      * object into a block of Roo.data.Records.
11956      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11957      * The function must be passed <ul>
11958      * <li>The Record block object</li>
11959      * <li>The "arg" argument from the load function</li>
11960      * <li>A boolean success indicator</li>
11961      * </ul>
11962      * @param {Object} scope The scope in which to call the callback
11963      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11964      */
11965     load : function(params, reader, callback, scope, arg){
11966         params = params || {};
11967         var result;
11968         try {
11969             result = reader.readRecords(this.data);
11970         }catch(e){
11971             this.fireEvent("loadexception", this, arg, null, e);
11972             callback.call(scope, null, arg, false);
11973             return;
11974         }
11975         callback.call(scope, result, arg, true);
11976     },
11977     
11978     // private
11979     update : function(params, records){
11980         
11981     }
11982 });/*
11983  * Based on:
11984  * Ext JS Library 1.1.1
11985  * Copyright(c) 2006-2007, Ext JS, LLC.
11986  *
11987  * Originally Released Under LGPL - original licence link has changed is not relivant.
11988  *
11989  * Fork - LGPL
11990  * <script type="text/javascript">
11991  */
11992 /**
11993  * @class Roo.data.HttpProxy
11994  * @extends Roo.data.DataProxy
11995  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11996  * configured to reference a certain URL.<br><br>
11997  * <p>
11998  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11999  * from which the running page was served.<br><br>
12000  * <p>
12001  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12002  * <p>
12003  * Be aware that to enable the browser to parse an XML document, the server must set
12004  * the Content-Type header in the HTTP response to "text/xml".
12005  * @constructor
12006  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12007  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12008  * will be used to make the request.
12009  */
12010 Roo.data.HttpProxy = function(conn){
12011     Roo.data.HttpProxy.superclass.constructor.call(this);
12012     // is conn a conn config or a real conn?
12013     this.conn = conn;
12014     this.useAjax = !conn || !conn.events;
12015   
12016 };
12017
12018 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12019     // thse are take from connection...
12020     
12021     /**
12022      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12023      */
12024     /**
12025      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12026      * extra parameters to each request made by this object. (defaults to undefined)
12027      */
12028     /**
12029      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12030      *  to each request made by this object. (defaults to undefined)
12031      */
12032     /**
12033      * @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)
12034      */
12035     /**
12036      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12037      */
12038      /**
12039      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12040      * @type Boolean
12041      */
12042   
12043
12044     /**
12045      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12046      * @type Boolean
12047      */
12048     /**
12049      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12050      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12051      * a finer-grained basis than the DataProxy events.
12052      */
12053     getConnection : function(){
12054         return this.useAjax ? Roo.Ajax : this.conn;
12055     },
12056
12057     /**
12058      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12059      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12060      * process that block using the passed callback.
12061      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12062      * for the request to the remote server.
12063      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12064      * object into a block of Roo.data.Records.
12065      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12066      * The function must be passed <ul>
12067      * <li>The Record block object</li>
12068      * <li>The "arg" argument from the load function</li>
12069      * <li>A boolean success indicator</li>
12070      * </ul>
12071      * @param {Object} scope The scope in which to call the callback
12072      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12073      */
12074     load : function(params, reader, callback, scope, arg){
12075         if(this.fireEvent("beforeload", this, params) !== false){
12076             var  o = {
12077                 params : params || {},
12078                 request: {
12079                     callback : callback,
12080                     scope : scope,
12081                     arg : arg
12082                 },
12083                 reader: reader,
12084                 callback : this.loadResponse,
12085                 scope: this
12086             };
12087             if(this.useAjax){
12088                 Roo.applyIf(o, this.conn);
12089                 if(this.activeRequest){
12090                     Roo.Ajax.abort(this.activeRequest);
12091                 }
12092                 this.activeRequest = Roo.Ajax.request(o);
12093             }else{
12094                 this.conn.request(o);
12095             }
12096         }else{
12097             callback.call(scope||this, null, arg, false);
12098         }
12099     },
12100
12101     // private
12102     loadResponse : function(o, success, response){
12103         delete this.activeRequest;
12104         if(!success){
12105             this.fireEvent("loadexception", this, o, response);
12106             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12107             return;
12108         }
12109         var result;
12110         try {
12111             result = o.reader.read(response);
12112         }catch(e){
12113             this.fireEvent("loadexception", this, o, response, e);
12114             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12115             return;
12116         }
12117         
12118         this.fireEvent("load", this, o, o.request.arg);
12119         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12120     },
12121
12122     // private
12123     update : function(dataSet){
12124
12125     },
12126
12127     // private
12128     updateResponse : function(dataSet){
12129
12130     }
12131 });/*
12132  * Based on:
12133  * Ext JS Library 1.1.1
12134  * Copyright(c) 2006-2007, Ext JS, LLC.
12135  *
12136  * Originally Released Under LGPL - original licence link has changed is not relivant.
12137  *
12138  * Fork - LGPL
12139  * <script type="text/javascript">
12140  */
12141
12142 /**
12143  * @class Roo.data.ScriptTagProxy
12144  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12145  * other than the originating domain of the running page.<br><br>
12146  * <p>
12147  * <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
12148  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12149  * <p>
12150  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12151  * source code that is used as the source inside a &lt;script> tag.<br><br>
12152  * <p>
12153  * In order for the browser to process the returned data, the server must wrap the data object
12154  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12155  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12156  * depending on whether the callback name was passed:
12157  * <p>
12158  * <pre><code>
12159 boolean scriptTag = false;
12160 String cb = request.getParameter("callback");
12161 if (cb != null) {
12162     scriptTag = true;
12163     response.setContentType("text/javascript");
12164 } else {
12165     response.setContentType("application/x-json");
12166 }
12167 Writer out = response.getWriter();
12168 if (scriptTag) {
12169     out.write(cb + "(");
12170 }
12171 out.print(dataBlock.toJsonString());
12172 if (scriptTag) {
12173     out.write(");");
12174 }
12175 </pre></code>
12176  *
12177  * @constructor
12178  * @param {Object} config A configuration object.
12179  */
12180 Roo.data.ScriptTagProxy = function(config){
12181     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12182     Roo.apply(this, config);
12183     this.head = document.getElementsByTagName("head")[0];
12184 };
12185
12186 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12187
12188 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12189     /**
12190      * @cfg {String} url The URL from which to request the data object.
12191      */
12192     /**
12193      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12194      */
12195     timeout : 30000,
12196     /**
12197      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12198      * the server the name of the callback function set up by the load call to process the returned data object.
12199      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12200      * javascript output which calls this named function passing the data object as its only parameter.
12201      */
12202     callbackParam : "callback",
12203     /**
12204      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12205      * name to the request.
12206      */
12207     nocache : true,
12208
12209     /**
12210      * Load data from the configured URL, read the data object into
12211      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12212      * process that block using the passed callback.
12213      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12214      * for the request to the remote server.
12215      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12216      * object into a block of Roo.data.Records.
12217      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12218      * The function must be passed <ul>
12219      * <li>The Record block object</li>
12220      * <li>The "arg" argument from the load function</li>
12221      * <li>A boolean success indicator</li>
12222      * </ul>
12223      * @param {Object} scope The scope in which to call the callback
12224      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12225      */
12226     load : function(params, reader, callback, scope, arg){
12227         if(this.fireEvent("beforeload", this, params) !== false){
12228
12229             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12230
12231             var url = this.url;
12232             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12233             if(this.nocache){
12234                 url += "&_dc=" + (new Date().getTime());
12235             }
12236             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12237             var trans = {
12238                 id : transId,
12239                 cb : "stcCallback"+transId,
12240                 scriptId : "stcScript"+transId,
12241                 params : params,
12242                 arg : arg,
12243                 url : url,
12244                 callback : callback,
12245                 scope : scope,
12246                 reader : reader
12247             };
12248             var conn = this;
12249
12250             window[trans.cb] = function(o){
12251                 conn.handleResponse(o, trans);
12252             };
12253
12254             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12255
12256             if(this.autoAbort !== false){
12257                 this.abort();
12258             }
12259
12260             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12261
12262             var script = document.createElement("script");
12263             script.setAttribute("src", url);
12264             script.setAttribute("type", "text/javascript");
12265             script.setAttribute("id", trans.scriptId);
12266             this.head.appendChild(script);
12267
12268             this.trans = trans;
12269         }else{
12270             callback.call(scope||this, null, arg, false);
12271         }
12272     },
12273
12274     // private
12275     isLoading : function(){
12276         return this.trans ? true : false;
12277     },
12278
12279     /**
12280      * Abort the current server request.
12281      */
12282     abort : function(){
12283         if(this.isLoading()){
12284             this.destroyTrans(this.trans);
12285         }
12286     },
12287
12288     // private
12289     destroyTrans : function(trans, isLoaded){
12290         this.head.removeChild(document.getElementById(trans.scriptId));
12291         clearTimeout(trans.timeoutId);
12292         if(isLoaded){
12293             window[trans.cb] = undefined;
12294             try{
12295                 delete window[trans.cb];
12296             }catch(e){}
12297         }else{
12298             // if hasn't been loaded, wait for load to remove it to prevent script error
12299             window[trans.cb] = function(){
12300                 window[trans.cb] = undefined;
12301                 try{
12302                     delete window[trans.cb];
12303                 }catch(e){}
12304             };
12305         }
12306     },
12307
12308     // private
12309     handleResponse : function(o, trans){
12310         this.trans = false;
12311         this.destroyTrans(trans, true);
12312         var result;
12313         try {
12314             result = trans.reader.readRecords(o);
12315         }catch(e){
12316             this.fireEvent("loadexception", this, o, trans.arg, e);
12317             trans.callback.call(trans.scope||window, null, trans.arg, false);
12318             return;
12319         }
12320         this.fireEvent("load", this, o, trans.arg);
12321         trans.callback.call(trans.scope||window, result, trans.arg, true);
12322     },
12323
12324     // private
12325     handleFailure : function(trans){
12326         this.trans = false;
12327         this.destroyTrans(trans, false);
12328         this.fireEvent("loadexception", this, null, trans.arg);
12329         trans.callback.call(trans.scope||window, null, trans.arg, false);
12330     }
12331 });/*
12332  * Based on:
12333  * Ext JS Library 1.1.1
12334  * Copyright(c) 2006-2007, Ext JS, LLC.
12335  *
12336  * Originally Released Under LGPL - original licence link has changed is not relivant.
12337  *
12338  * Fork - LGPL
12339  * <script type="text/javascript">
12340  */
12341
12342 /**
12343  * @class Roo.data.JsonReader
12344  * @extends Roo.data.DataReader
12345  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12346  * based on mappings in a provided Roo.data.Record constructor.
12347  * 
12348  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12349  * in the reply previously. 
12350  * 
12351  * <p>
12352  * Example code:
12353  * <pre><code>
12354 var RecordDef = Roo.data.Record.create([
12355     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12356     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12357 ]);
12358 var myReader = new Roo.data.JsonReader({
12359     totalProperty: "results",    // The property which contains the total dataset size (optional)
12360     root: "rows",                // The property which contains an Array of row objects
12361     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12362 }, RecordDef);
12363 </code></pre>
12364  * <p>
12365  * This would consume a JSON file like this:
12366  * <pre><code>
12367 { 'results': 2, 'rows': [
12368     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12369     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12370 }
12371 </code></pre>
12372  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12373  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12374  * paged from the remote server.
12375  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12376  * @cfg {String} root name of the property which contains the Array of row objects.
12377  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12378  * @cfg {Array} fields Array of field definition objects
12379  * @constructor
12380  * Create a new JsonReader
12381  * @param {Object} meta Metadata configuration options
12382  * @param {Object} recordType Either an Array of field definition objects,
12383  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12384  */
12385 Roo.data.JsonReader = function(meta, recordType){
12386     
12387     meta = meta || {};
12388     // set some defaults:
12389     Roo.applyIf(meta, {
12390         totalProperty: 'total',
12391         successProperty : 'success',
12392         root : 'data',
12393         id : 'id'
12394     });
12395     
12396     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12397 };
12398 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12399     
12400     /**
12401      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12402      * Used by Store query builder to append _requestMeta to params.
12403      * 
12404      */
12405     metaFromRemote : false,
12406     /**
12407      * This method is only used by a DataProxy which has retrieved data from a remote server.
12408      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12409      * @return {Object} data A data block which is used by an Roo.data.Store object as
12410      * a cache of Roo.data.Records.
12411      */
12412     read : function(response){
12413         var json = response.responseText;
12414        
12415         var o = /* eval:var:o */ eval("("+json+")");
12416         if(!o) {
12417             throw {message: "JsonReader.read: Json object not found"};
12418         }
12419         
12420         if(o.metaData){
12421             
12422             delete this.ef;
12423             this.metaFromRemote = true;
12424             this.meta = o.metaData;
12425             this.recordType = Roo.data.Record.create(o.metaData.fields);
12426             this.onMetaChange(this.meta, this.recordType, o);
12427         }
12428         return this.readRecords(o);
12429     },
12430
12431     // private function a store will implement
12432     onMetaChange : function(meta, recordType, o){
12433
12434     },
12435
12436     /**
12437          * @ignore
12438          */
12439     simpleAccess: function(obj, subsc) {
12440         return obj[subsc];
12441     },
12442
12443         /**
12444          * @ignore
12445          */
12446     getJsonAccessor: function(){
12447         var re = /[\[\.]/;
12448         return function(expr) {
12449             try {
12450                 return(re.test(expr))
12451                     ? new Function("obj", "return obj." + expr)
12452                     : function(obj){
12453                         return obj[expr];
12454                     };
12455             } catch(e){}
12456             return Roo.emptyFn;
12457         };
12458     }(),
12459
12460     /**
12461      * Create a data block containing Roo.data.Records from an XML document.
12462      * @param {Object} o An object which contains an Array of row objects in the property specified
12463      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12464      * which contains the total size of the dataset.
12465      * @return {Object} data A data block which is used by an Roo.data.Store object as
12466      * a cache of Roo.data.Records.
12467      */
12468     readRecords : function(o){
12469         /**
12470          * After any data loads, the raw JSON data is available for further custom processing.
12471          * @type Object
12472          */
12473         this.o = o;
12474         var s = this.meta, Record = this.recordType,
12475             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12476
12477 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12478         if (!this.ef) {
12479             if(s.totalProperty) {
12480                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12481                 }
12482                 if(s.successProperty) {
12483                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12484                 }
12485                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12486                 if (s.id) {
12487                         var g = this.getJsonAccessor(s.id);
12488                         this.getId = function(rec) {
12489                                 var r = g(rec);  
12490                                 return (r === undefined || r === "") ? null : r;
12491                         };
12492                 } else {
12493                         this.getId = function(){return null;};
12494                 }
12495             this.ef = [];
12496             for(var jj = 0; jj < fl; jj++){
12497                 f = fi[jj];
12498                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12499                 this.ef[jj] = this.getJsonAccessor(map);
12500             }
12501         }
12502
12503         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12504         if(s.totalProperty){
12505             var vt = parseInt(this.getTotal(o), 10);
12506             if(!isNaN(vt)){
12507                 totalRecords = vt;
12508             }
12509         }
12510         if(s.successProperty){
12511             var vs = this.getSuccess(o);
12512             if(vs === false || vs === 'false'){
12513                 success = false;
12514             }
12515         }
12516         var records = [];
12517         for(var i = 0; i < c; i++){
12518                 var n = root[i];
12519             var values = {};
12520             var id = this.getId(n);
12521             for(var j = 0; j < fl; j++){
12522                 f = fi[j];
12523             var v = this.ef[j](n);
12524             if (!f.convert) {
12525                 Roo.log('missing convert for ' + f.name);
12526                 Roo.log(f);
12527                 continue;
12528             }
12529             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12530             }
12531             var record = new Record(values, id);
12532             record.json = n;
12533             records[i] = record;
12534         }
12535         return {
12536             raw : o,
12537             success : success,
12538             records : records,
12539             totalRecords : totalRecords
12540         };
12541     }
12542 });/*
12543  * Based on:
12544  * Ext JS Library 1.1.1
12545  * Copyright(c) 2006-2007, Ext JS, LLC.
12546  *
12547  * Originally Released Under LGPL - original licence link has changed is not relivant.
12548  *
12549  * Fork - LGPL
12550  * <script type="text/javascript">
12551  */
12552
12553 /**
12554  * @class Roo.data.ArrayReader
12555  * @extends Roo.data.DataReader
12556  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12557  * Each element of that Array represents a row of data fields. The
12558  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12559  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12560  * <p>
12561  * Example code:.
12562  * <pre><code>
12563 var RecordDef = Roo.data.Record.create([
12564     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12565     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12566 ]);
12567 var myReader = new Roo.data.ArrayReader({
12568     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12569 }, RecordDef);
12570 </code></pre>
12571  * <p>
12572  * This would consume an Array like this:
12573  * <pre><code>
12574 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12575   </code></pre>
12576  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12577  * @constructor
12578  * Create a new JsonReader
12579  * @param {Object} meta Metadata configuration options.
12580  * @param {Object} recordType Either an Array of field definition objects
12581  * as specified to {@link Roo.data.Record#create},
12582  * or an {@link Roo.data.Record} object
12583  * created using {@link Roo.data.Record#create}.
12584  */
12585 Roo.data.ArrayReader = function(meta, recordType){
12586     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12587 };
12588
12589 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12590     /**
12591      * Create a data block containing Roo.data.Records from an XML document.
12592      * @param {Object} o An Array of row objects which represents the dataset.
12593      * @return {Object} data A data block which is used by an Roo.data.Store object as
12594      * a cache of Roo.data.Records.
12595      */
12596     readRecords : function(o){
12597         var sid = this.meta ? this.meta.id : null;
12598         var recordType = this.recordType, fields = recordType.prototype.fields;
12599         var records = [];
12600         var root = o;
12601             for(var i = 0; i < root.length; i++){
12602                     var n = root[i];
12603                 var values = {};
12604                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12605                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12606                 var f = fields.items[j];
12607                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12608                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12609                 v = f.convert(v);
12610                 values[f.name] = v;
12611             }
12612                 var record = new recordType(values, id);
12613                 record.json = n;
12614                 records[records.length] = record;
12615             }
12616             return {
12617                 records : records,
12618                 totalRecords : records.length
12619             };
12620     }
12621 });/*
12622  * - LGPL
12623  * * 
12624  */
12625
12626 /**
12627  * @class Roo.bootstrap.ComboBox
12628  * @extends Roo.bootstrap.TriggerField
12629  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12630  * @cfg {Boolean} append (true|false) default false
12631  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12632  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12633  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12634  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12635  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12636  * @cfg {Boolean} animate default true
12637  * @cfg {Boolean} emptyResultText only for touch device
12638  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12639  * @cfg {String} emptyTitle default ''
12640  * @constructor
12641  * Create a new ComboBox.
12642  * @param {Object} config Configuration options
12643  */
12644 Roo.bootstrap.ComboBox = function(config){
12645     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12646     this.addEvents({
12647         /**
12648          * @event expand
12649          * Fires when the dropdown list is expanded
12650         * @param {Roo.bootstrap.ComboBox} combo This combo box
12651         */
12652         'expand' : true,
12653         /**
12654          * @event collapse
12655          * Fires when the dropdown list is collapsed
12656         * @param {Roo.bootstrap.ComboBox} combo This combo box
12657         */
12658         'collapse' : true,
12659         /**
12660          * @event beforeselect
12661          * Fires before a list item is selected. Return false to cancel the selection.
12662         * @param {Roo.bootstrap.ComboBox} combo This combo box
12663         * @param {Roo.data.Record} record The data record returned from the underlying store
12664         * @param {Number} index The index of the selected item in the dropdown list
12665         */
12666         'beforeselect' : true,
12667         /**
12668          * @event select
12669          * Fires when a list item is selected
12670         * @param {Roo.bootstrap.ComboBox} combo This combo box
12671         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12672         * @param {Number} index The index of the selected item in the dropdown list
12673         */
12674         'select' : true,
12675         /**
12676          * @event beforequery
12677          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12678          * The event object passed has these properties:
12679         * @param {Roo.bootstrap.ComboBox} combo This combo box
12680         * @param {String} query The query
12681         * @param {Boolean} forceAll true to force "all" query
12682         * @param {Boolean} cancel true to cancel the query
12683         * @param {Object} e The query event object
12684         */
12685         'beforequery': true,
12686          /**
12687          * @event add
12688          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12689         * @param {Roo.bootstrap.ComboBox} combo This combo box
12690         */
12691         'add' : true,
12692         /**
12693          * @event edit
12694          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12695         * @param {Roo.bootstrap.ComboBox} combo This combo box
12696         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12697         */
12698         'edit' : true,
12699         /**
12700          * @event remove
12701          * Fires when the remove value from the combobox array
12702         * @param {Roo.bootstrap.ComboBox} combo This combo box
12703         */
12704         'remove' : true,
12705         /**
12706          * @event afterremove
12707          * Fires when the remove value from the combobox array
12708         * @param {Roo.bootstrap.ComboBox} combo This combo box
12709         */
12710         'afterremove' : true,
12711         /**
12712          * @event specialfilter
12713          * Fires when specialfilter
12714             * @param {Roo.bootstrap.ComboBox} combo This combo box
12715             */
12716         'specialfilter' : true,
12717         /**
12718          * @event tick
12719          * Fires when tick the element
12720             * @param {Roo.bootstrap.ComboBox} combo This combo box
12721             */
12722         'tick' : true,
12723         /**
12724          * @event touchviewdisplay
12725          * Fires when touch view require special display (default is using displayField)
12726             * @param {Roo.bootstrap.ComboBox} combo This combo box
12727             * @param {Object} cfg set html .
12728             */
12729         'touchviewdisplay' : true
12730         
12731     });
12732     
12733     this.item = [];
12734     this.tickItems = [];
12735     
12736     this.selectedIndex = -1;
12737     if(this.mode == 'local'){
12738         if(config.queryDelay === undefined){
12739             this.queryDelay = 10;
12740         }
12741         if(config.minChars === undefined){
12742             this.minChars = 0;
12743         }
12744     }
12745 };
12746
12747 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12748      
12749     /**
12750      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12751      * rendering into an Roo.Editor, defaults to false)
12752      */
12753     /**
12754      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12755      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12756      */
12757     /**
12758      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12759      */
12760     /**
12761      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12762      * the dropdown list (defaults to undefined, with no header element)
12763      */
12764
12765      /**
12766      * @cfg {String/Roo.Template} tpl The template to use to render the output
12767      */
12768      
12769      /**
12770      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12771      */
12772     listWidth: undefined,
12773     /**
12774      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12775      * mode = 'remote' or 'text' if mode = 'local')
12776      */
12777     displayField: undefined,
12778     
12779     /**
12780      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12781      * mode = 'remote' or 'value' if mode = 'local'). 
12782      * Note: use of a valueField requires the user make a selection
12783      * in order for a value to be mapped.
12784      */
12785     valueField: undefined,
12786     /**
12787      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12788      */
12789     modalTitle : '',
12790     
12791     /**
12792      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12793      * field's data value (defaults to the underlying DOM element's name)
12794      */
12795     hiddenName: undefined,
12796     /**
12797      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12798      */
12799     listClass: '',
12800     /**
12801      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12802      */
12803     selectedClass: 'active',
12804     
12805     /**
12806      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12807      */
12808     shadow:'sides',
12809     /**
12810      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12811      * anchor positions (defaults to 'tl-bl')
12812      */
12813     listAlign: 'tl-bl?',
12814     /**
12815      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12816      */
12817     maxHeight: 300,
12818     /**
12819      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12820      * query specified by the allQuery config option (defaults to 'query')
12821      */
12822     triggerAction: 'query',
12823     /**
12824      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12825      * (defaults to 4, does not apply if editable = false)
12826      */
12827     minChars : 4,
12828     /**
12829      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12830      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12831      */
12832     typeAhead: false,
12833     /**
12834      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12835      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12836      */
12837     queryDelay: 500,
12838     /**
12839      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12840      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12841      */
12842     pageSize: 0,
12843     /**
12844      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12845      * when editable = true (defaults to false)
12846      */
12847     selectOnFocus:false,
12848     /**
12849      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12850      */
12851     queryParam: 'query',
12852     /**
12853      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12854      * when mode = 'remote' (defaults to 'Loading...')
12855      */
12856     loadingText: 'Loading...',
12857     /**
12858      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12859      */
12860     resizable: false,
12861     /**
12862      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12863      */
12864     handleHeight : 8,
12865     /**
12866      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12867      * traditional select (defaults to true)
12868      */
12869     editable: true,
12870     /**
12871      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12872      */
12873     allQuery: '',
12874     /**
12875      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12876      */
12877     mode: 'remote',
12878     /**
12879      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12880      * listWidth has a higher value)
12881      */
12882     minListWidth : 70,
12883     /**
12884      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12885      * allow the user to set arbitrary text into the field (defaults to false)
12886      */
12887     forceSelection:false,
12888     /**
12889      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12890      * if typeAhead = true (defaults to 250)
12891      */
12892     typeAheadDelay : 250,
12893     /**
12894      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12895      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12896      */
12897     valueNotFoundText : undefined,
12898     /**
12899      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12900      */
12901     blockFocus : false,
12902     
12903     /**
12904      * @cfg {Boolean} disableClear Disable showing of clear button.
12905      */
12906     disableClear : false,
12907     /**
12908      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12909      */
12910     alwaysQuery : false,
12911     
12912     /**
12913      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12914      */
12915     multiple : false,
12916     
12917     /**
12918      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12919      */
12920     invalidClass : "has-warning",
12921     
12922     /**
12923      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12924      */
12925     validClass : "has-success",
12926     
12927     /**
12928      * @cfg {Boolean} specialFilter (true|false) special filter default false
12929      */
12930     specialFilter : false,
12931     
12932     /**
12933      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12934      */
12935     mobileTouchView : true,
12936     
12937     /**
12938      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12939      */
12940     useNativeIOS : false,
12941     
12942     ios_options : false,
12943     
12944     //private
12945     addicon : false,
12946     editicon: false,
12947     
12948     page: 0,
12949     hasQuery: false,
12950     append: false,
12951     loadNext: false,
12952     autoFocus : true,
12953     tickable : false,
12954     btnPosition : 'right',
12955     triggerList : true,
12956     showToggleBtn : true,
12957     animate : true,
12958     emptyResultText: 'Empty',
12959     triggerText : 'Select',
12960     emptyTitle : '',
12961     
12962     // element that contains real text value.. (when hidden is used..)
12963     
12964     getAutoCreate : function()
12965     {   
12966         var cfg = false;
12967         //render
12968         /*
12969          * Render classic select for iso
12970          */
12971         
12972         if(Roo.isIOS && this.useNativeIOS){
12973             cfg = this.getAutoCreateNativeIOS();
12974             return cfg;
12975         }
12976         
12977         /*
12978          * Touch Devices
12979          */
12980         
12981         if(Roo.isTouch && this.mobileTouchView){
12982             cfg = this.getAutoCreateTouchView();
12983             return cfg;;
12984         }
12985         
12986         /*
12987          *  Normal ComboBox
12988          */
12989         if(!this.tickable){
12990             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12991             return cfg;
12992         }
12993         
12994         /*
12995          *  ComboBox with tickable selections
12996          */
12997              
12998         var align = this.labelAlign || this.parentLabelAlign();
12999         
13000         cfg = {
13001             cls : 'form-group roo-combobox-tickable' //input-group
13002         };
13003         
13004         var btn_text_select = '';
13005         var btn_text_done = '';
13006         var btn_text_cancel = '';
13007         
13008         if (this.btn_text_show) {
13009             btn_text_select = 'Select';
13010             btn_text_done = 'Done';
13011             btn_text_cancel = 'Cancel'; 
13012         }
13013         
13014         var buttons = {
13015             tag : 'div',
13016             cls : 'tickable-buttons',
13017             cn : [
13018                 {
13019                     tag : 'button',
13020                     type : 'button',
13021                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13022                     //html : this.triggerText
13023                     html: btn_text_select
13024                 },
13025                 {
13026                     tag : 'button',
13027                     type : 'button',
13028                     name : 'ok',
13029                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13030                     //html : 'Done'
13031                     html: btn_text_done
13032                 },
13033                 {
13034                     tag : 'button',
13035                     type : 'button',
13036                     name : 'cancel',
13037                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13038                     //html : 'Cancel'
13039                     html: btn_text_cancel
13040                 }
13041             ]
13042         };
13043         
13044         if(this.editable){
13045             buttons.cn.unshift({
13046                 tag: 'input',
13047                 cls: 'roo-select2-search-field-input'
13048             });
13049         }
13050         
13051         var _this = this;
13052         
13053         Roo.each(buttons.cn, function(c){
13054             if (_this.size) {
13055                 c.cls += ' btn-' + _this.size;
13056             }
13057
13058             if (_this.disabled) {
13059                 c.disabled = true;
13060             }
13061         });
13062         
13063         var box = {
13064             tag: 'div',
13065             cn: [
13066                 {
13067                     tag: 'input',
13068                     type : 'hidden',
13069                     cls: 'form-hidden-field'
13070                 },
13071                 {
13072                     tag: 'ul',
13073                     cls: 'roo-select2-choices',
13074                     cn:[
13075                         {
13076                             tag: 'li',
13077                             cls: 'roo-select2-search-field',
13078                             cn: [
13079                                 buttons
13080                             ]
13081                         }
13082                     ]
13083                 }
13084             ]
13085         };
13086         
13087         var combobox = {
13088             cls: 'roo-select2-container input-group roo-select2-container-multi',
13089             cn: [
13090                 box
13091 //                {
13092 //                    tag: 'ul',
13093 //                    cls: 'typeahead typeahead-long dropdown-menu',
13094 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13095 //                }
13096             ]
13097         };
13098         
13099         if(this.hasFeedback && !this.allowBlank){
13100             
13101             var feedback = {
13102                 tag: 'span',
13103                 cls: 'glyphicon form-control-feedback'
13104             };
13105
13106             combobox.cn.push(feedback);
13107         }
13108         
13109         
13110         if (align ==='left' && this.fieldLabel.length) {
13111             
13112             cfg.cls += ' roo-form-group-label-left';
13113             
13114             cfg.cn = [
13115                 {
13116                     tag : 'i',
13117                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13118                     tooltip : 'This field is required'
13119                 },
13120                 {
13121                     tag: 'label',
13122                     'for' :  id,
13123                     cls : 'control-label',
13124                     html : this.fieldLabel
13125
13126                 },
13127                 {
13128                     cls : "", 
13129                     cn: [
13130                         combobox
13131                     ]
13132                 }
13133
13134             ];
13135             
13136             var labelCfg = cfg.cn[1];
13137             var contentCfg = cfg.cn[2];
13138             
13139
13140             if(this.indicatorpos == 'right'){
13141                 
13142                 cfg.cn = [
13143                     {
13144                         tag: 'label',
13145                         'for' :  id,
13146                         cls : 'control-label',
13147                         cn : [
13148                             {
13149                                 tag : 'span',
13150                                 html : this.fieldLabel
13151                             },
13152                             {
13153                                 tag : 'i',
13154                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13155                                 tooltip : 'This field is required'
13156                             }
13157                         ]
13158                     },
13159                     {
13160                         cls : "",
13161                         cn: [
13162                             combobox
13163                         ]
13164                     }
13165
13166                 ];
13167                 
13168                 
13169                 
13170                 labelCfg = cfg.cn[0];
13171                 contentCfg = cfg.cn[1];
13172             
13173             }
13174             
13175             if(this.labelWidth > 12){
13176                 labelCfg.style = "width: " + this.labelWidth + 'px';
13177             }
13178             
13179             if(this.labelWidth < 13 && this.labelmd == 0){
13180                 this.labelmd = this.labelWidth;
13181             }
13182             
13183             if(this.labellg > 0){
13184                 labelCfg.cls += ' col-lg-' + this.labellg;
13185                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13186             }
13187             
13188             if(this.labelmd > 0){
13189                 labelCfg.cls += ' col-md-' + this.labelmd;
13190                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13191             }
13192             
13193             if(this.labelsm > 0){
13194                 labelCfg.cls += ' col-sm-' + this.labelsm;
13195                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13196             }
13197             
13198             if(this.labelxs > 0){
13199                 labelCfg.cls += ' col-xs-' + this.labelxs;
13200                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13201             }
13202                 
13203                 
13204         } else if ( this.fieldLabel.length) {
13205 //                Roo.log(" label");
13206                  cfg.cn = [
13207                     {
13208                         tag : 'i',
13209                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13210                         tooltip : 'This field is required'
13211                     },
13212                     {
13213                         tag: 'label',
13214                         //cls : 'input-group-addon',
13215                         html : this.fieldLabel
13216                     },
13217                     combobox
13218                 ];
13219                 
13220                 if(this.indicatorpos == 'right'){
13221                     cfg.cn = [
13222                         {
13223                             tag: 'label',
13224                             //cls : 'input-group-addon',
13225                             html : this.fieldLabel
13226                         },
13227                         {
13228                             tag : 'i',
13229                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13230                             tooltip : 'This field is required'
13231                         },
13232                         combobox
13233                     ];
13234                     
13235                 }
13236
13237         } else {
13238             
13239 //                Roo.log(" no label && no align");
13240                 cfg = combobox
13241                      
13242                 
13243         }
13244          
13245         var settings=this;
13246         ['xs','sm','md','lg'].map(function(size){
13247             if (settings[size]) {
13248                 cfg.cls += ' col-' + size + '-' + settings[size];
13249             }
13250         });
13251         
13252         return cfg;
13253         
13254     },
13255     
13256     _initEventsCalled : false,
13257     
13258     // private
13259     initEvents: function()
13260     {   
13261         if (this._initEventsCalled) { // as we call render... prevent looping...
13262             return;
13263         }
13264         this._initEventsCalled = true;
13265         
13266         if (!this.store) {
13267             throw "can not find store for combo";
13268         }
13269         
13270         this.indicator = this.indicatorEl();
13271         
13272         this.store = Roo.factory(this.store, Roo.data);
13273         this.store.parent = this;
13274         
13275         // if we are building from html. then this element is so complex, that we can not really
13276         // use the rendered HTML.
13277         // so we have to trash and replace the previous code.
13278         if (Roo.XComponent.build_from_html) {
13279             // remove this element....
13280             var e = this.el.dom, k=0;
13281             while (e ) { e = e.previousSibling;  ++k;}
13282
13283             this.el.remove();
13284             
13285             this.el=false;
13286             this.rendered = false;
13287             
13288             this.render(this.parent().getChildContainer(true), k);
13289         }
13290         
13291         if(Roo.isIOS && this.useNativeIOS){
13292             this.initIOSView();
13293             return;
13294         }
13295         
13296         /*
13297          * Touch Devices
13298          */
13299         
13300         if(Roo.isTouch && this.mobileTouchView){
13301             this.initTouchView();
13302             return;
13303         }
13304         
13305         if(this.tickable){
13306             this.initTickableEvents();
13307             return;
13308         }
13309         
13310         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13311         
13312         if(this.hiddenName){
13313             
13314             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13315             
13316             this.hiddenField.dom.value =
13317                 this.hiddenValue !== undefined ? this.hiddenValue :
13318                 this.value !== undefined ? this.value : '';
13319
13320             // prevent input submission
13321             this.el.dom.removeAttribute('name');
13322             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13323              
13324              
13325         }
13326         //if(Roo.isGecko){
13327         //    this.el.dom.setAttribute('autocomplete', 'off');
13328         //}
13329         
13330         var cls = 'x-combo-list';
13331         
13332         //this.list = new Roo.Layer({
13333         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13334         //});
13335         
13336         var _this = this;
13337         
13338         (function(){
13339             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13340             _this.list.setWidth(lw);
13341         }).defer(100);
13342         
13343         this.list.on('mouseover', this.onViewOver, this);
13344         this.list.on('mousemove', this.onViewMove, this);
13345         this.list.on('scroll', this.onViewScroll, this);
13346         
13347         /*
13348         this.list.swallowEvent('mousewheel');
13349         this.assetHeight = 0;
13350
13351         if(this.title){
13352             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13353             this.assetHeight += this.header.getHeight();
13354         }
13355
13356         this.innerList = this.list.createChild({cls:cls+'-inner'});
13357         this.innerList.on('mouseover', this.onViewOver, this);
13358         this.innerList.on('mousemove', this.onViewMove, this);
13359         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13360         
13361         if(this.allowBlank && !this.pageSize && !this.disableClear){
13362             this.footer = this.list.createChild({cls:cls+'-ft'});
13363             this.pageTb = new Roo.Toolbar(this.footer);
13364            
13365         }
13366         if(this.pageSize){
13367             this.footer = this.list.createChild({cls:cls+'-ft'});
13368             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13369                     {pageSize: this.pageSize});
13370             
13371         }
13372         
13373         if (this.pageTb && this.allowBlank && !this.disableClear) {
13374             var _this = this;
13375             this.pageTb.add(new Roo.Toolbar.Fill(), {
13376                 cls: 'x-btn-icon x-btn-clear',
13377                 text: '&#160;',
13378                 handler: function()
13379                 {
13380                     _this.collapse();
13381                     _this.clearValue();
13382                     _this.onSelect(false, -1);
13383                 }
13384             });
13385         }
13386         if (this.footer) {
13387             this.assetHeight += this.footer.getHeight();
13388         }
13389         */
13390             
13391         if(!this.tpl){
13392             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13393         }
13394
13395         this.view = new Roo.View(this.list, this.tpl, {
13396             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13397         });
13398         //this.view.wrapEl.setDisplayed(false);
13399         this.view.on('click', this.onViewClick, this);
13400         
13401         
13402         this.store.on('beforeload', this.onBeforeLoad, this);
13403         this.store.on('load', this.onLoad, this);
13404         this.store.on('loadexception', this.onLoadException, this);
13405         /*
13406         if(this.resizable){
13407             this.resizer = new Roo.Resizable(this.list,  {
13408                pinned:true, handles:'se'
13409             });
13410             this.resizer.on('resize', function(r, w, h){
13411                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13412                 this.listWidth = w;
13413                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13414                 this.restrictHeight();
13415             }, this);
13416             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13417         }
13418         */
13419         if(!this.editable){
13420             this.editable = true;
13421             this.setEditable(false);
13422         }
13423         
13424         /*
13425         
13426         if (typeof(this.events.add.listeners) != 'undefined') {
13427             
13428             this.addicon = this.wrap.createChild(
13429                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13430        
13431             this.addicon.on('click', function(e) {
13432                 this.fireEvent('add', this);
13433             }, this);
13434         }
13435         if (typeof(this.events.edit.listeners) != 'undefined') {
13436             
13437             this.editicon = this.wrap.createChild(
13438                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13439             if (this.addicon) {
13440                 this.editicon.setStyle('margin-left', '40px');
13441             }
13442             this.editicon.on('click', function(e) {
13443                 
13444                 // we fire even  if inothing is selected..
13445                 this.fireEvent('edit', this, this.lastData );
13446                 
13447             }, this);
13448         }
13449         */
13450         
13451         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13452             "up" : function(e){
13453                 this.inKeyMode = true;
13454                 this.selectPrev();
13455             },
13456
13457             "down" : function(e){
13458                 if(!this.isExpanded()){
13459                     this.onTriggerClick();
13460                 }else{
13461                     this.inKeyMode = true;
13462                     this.selectNext();
13463                 }
13464             },
13465
13466             "enter" : function(e){
13467 //                this.onViewClick();
13468                 //return true;
13469                 this.collapse();
13470                 
13471                 if(this.fireEvent("specialkey", this, e)){
13472                     this.onViewClick(false);
13473                 }
13474                 
13475                 return true;
13476             },
13477
13478             "esc" : function(e){
13479                 this.collapse();
13480             },
13481
13482             "tab" : function(e){
13483                 this.collapse();
13484                 
13485                 if(this.fireEvent("specialkey", this, e)){
13486                     this.onViewClick(false);
13487                 }
13488                 
13489                 return true;
13490             },
13491
13492             scope : this,
13493
13494             doRelay : function(foo, bar, hname){
13495                 if(hname == 'down' || this.scope.isExpanded()){
13496                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13497                 }
13498                 return true;
13499             },
13500
13501             forceKeyDown: true
13502         });
13503         
13504         
13505         this.queryDelay = Math.max(this.queryDelay || 10,
13506                 this.mode == 'local' ? 10 : 250);
13507         
13508         
13509         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13510         
13511         if(this.typeAhead){
13512             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13513         }
13514         if(this.editable !== false){
13515             this.inputEl().on("keyup", this.onKeyUp, this);
13516         }
13517         if(this.forceSelection){
13518             this.inputEl().on('blur', this.doForce, this);
13519         }
13520         
13521         if(this.multiple){
13522             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13523             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13524         }
13525     },
13526     
13527     initTickableEvents: function()
13528     {   
13529         this.createList();
13530         
13531         if(this.hiddenName){
13532             
13533             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13534             
13535             this.hiddenField.dom.value =
13536                 this.hiddenValue !== undefined ? this.hiddenValue :
13537                 this.value !== undefined ? this.value : '';
13538
13539             // prevent input submission
13540             this.el.dom.removeAttribute('name');
13541             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13542              
13543              
13544         }
13545         
13546 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13547         
13548         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13549         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13550         if(this.triggerList){
13551             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13552         }
13553          
13554         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13555         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13556         
13557         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13558         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13559         
13560         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13561         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13562         
13563         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13564         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13565         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13566         
13567         this.okBtn.hide();
13568         this.cancelBtn.hide();
13569         
13570         var _this = this;
13571         
13572         (function(){
13573             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13574             _this.list.setWidth(lw);
13575         }).defer(100);
13576         
13577         this.list.on('mouseover', this.onViewOver, this);
13578         this.list.on('mousemove', this.onViewMove, this);
13579         
13580         this.list.on('scroll', this.onViewScroll, this);
13581         
13582         if(!this.tpl){
13583             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>';
13584         }
13585
13586         this.view = new Roo.View(this.list, this.tpl, {
13587             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13588         });
13589         
13590         //this.view.wrapEl.setDisplayed(false);
13591         this.view.on('click', this.onViewClick, this);
13592         
13593         
13594         
13595         this.store.on('beforeload', this.onBeforeLoad, this);
13596         this.store.on('load', this.onLoad, this);
13597         this.store.on('loadexception', this.onLoadException, this);
13598         
13599         if(this.editable){
13600             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13601                 "up" : function(e){
13602                     this.inKeyMode = true;
13603                     this.selectPrev();
13604                 },
13605
13606                 "down" : function(e){
13607                     this.inKeyMode = true;
13608                     this.selectNext();
13609                 },
13610
13611                 "enter" : function(e){
13612                     if(this.fireEvent("specialkey", this, e)){
13613                         this.onViewClick(false);
13614                     }
13615                     
13616                     return true;
13617                 },
13618
13619                 "esc" : function(e){
13620                     this.onTickableFooterButtonClick(e, false, false);
13621                 },
13622
13623                 "tab" : function(e){
13624                     this.fireEvent("specialkey", this, e);
13625                     
13626                     this.onTickableFooterButtonClick(e, false, false);
13627                     
13628                     return true;
13629                 },
13630
13631                 scope : this,
13632
13633                 doRelay : function(e, fn, key){
13634                     if(this.scope.isExpanded()){
13635                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13636                     }
13637                     return true;
13638                 },
13639
13640                 forceKeyDown: true
13641             });
13642         }
13643         
13644         this.queryDelay = Math.max(this.queryDelay || 10,
13645                 this.mode == 'local' ? 10 : 250);
13646         
13647         
13648         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13649         
13650         if(this.typeAhead){
13651             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13652         }
13653         
13654         if(this.editable !== false){
13655             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13656         }
13657         
13658         this.indicator = this.indicatorEl();
13659         
13660         if(this.indicator){
13661             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13662             this.indicator.hide();
13663         }
13664         
13665     },
13666
13667     onDestroy : function(){
13668         if(this.view){
13669             this.view.setStore(null);
13670             this.view.el.removeAllListeners();
13671             this.view.el.remove();
13672             this.view.purgeListeners();
13673         }
13674         if(this.list){
13675             this.list.dom.innerHTML  = '';
13676         }
13677         
13678         if(this.store){
13679             this.store.un('beforeload', this.onBeforeLoad, this);
13680             this.store.un('load', this.onLoad, this);
13681             this.store.un('loadexception', this.onLoadException, this);
13682         }
13683         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13684     },
13685
13686     // private
13687     fireKey : function(e){
13688         if(e.isNavKeyPress() && !this.list.isVisible()){
13689             this.fireEvent("specialkey", this, e);
13690         }
13691     },
13692
13693     // private
13694     onResize: function(w, h){
13695 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13696 //        
13697 //        if(typeof w != 'number'){
13698 //            // we do not handle it!?!?
13699 //            return;
13700 //        }
13701 //        var tw = this.trigger.getWidth();
13702 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13703 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13704 //        var x = w - tw;
13705 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13706 //            
13707 //        //this.trigger.setStyle('left', x+'px');
13708 //        
13709 //        if(this.list && this.listWidth === undefined){
13710 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13711 //            this.list.setWidth(lw);
13712 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13713 //        }
13714         
13715     
13716         
13717     },
13718
13719     /**
13720      * Allow or prevent the user from directly editing the field text.  If false is passed,
13721      * the user will only be able to select from the items defined in the dropdown list.  This method
13722      * is the runtime equivalent of setting the 'editable' config option at config time.
13723      * @param {Boolean} value True to allow the user to directly edit the field text
13724      */
13725     setEditable : function(value){
13726         if(value == this.editable){
13727             return;
13728         }
13729         this.editable = value;
13730         if(!value){
13731             this.inputEl().dom.setAttribute('readOnly', true);
13732             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13733             this.inputEl().addClass('x-combo-noedit');
13734         }else{
13735             this.inputEl().dom.setAttribute('readOnly', false);
13736             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13737             this.inputEl().removeClass('x-combo-noedit');
13738         }
13739     },
13740
13741     // private
13742     
13743     onBeforeLoad : function(combo,opts){
13744         if(!this.hasFocus){
13745             return;
13746         }
13747          if (!opts.add) {
13748             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13749          }
13750         this.restrictHeight();
13751         this.selectedIndex = -1;
13752     },
13753
13754     // private
13755     onLoad : function(){
13756         
13757         this.hasQuery = false;
13758         
13759         if(!this.hasFocus){
13760             return;
13761         }
13762         
13763         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13764             this.loading.hide();
13765         }
13766         
13767         if(this.store.getCount() > 0){
13768             
13769             this.expand();
13770             this.restrictHeight();
13771             if(this.lastQuery == this.allQuery){
13772                 if(this.editable && !this.tickable){
13773                     this.inputEl().dom.select();
13774                 }
13775                 
13776                 if(
13777                     !this.selectByValue(this.value, true) &&
13778                     this.autoFocus && 
13779                     (
13780                         !this.store.lastOptions ||
13781                         typeof(this.store.lastOptions.add) == 'undefined' || 
13782                         this.store.lastOptions.add != true
13783                     )
13784                 ){
13785                     this.select(0, true);
13786                 }
13787             }else{
13788                 if(this.autoFocus){
13789                     this.selectNext();
13790                 }
13791                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13792                     this.taTask.delay(this.typeAheadDelay);
13793                 }
13794             }
13795         }else{
13796             this.onEmptyResults();
13797         }
13798         
13799         //this.el.focus();
13800     },
13801     // private
13802     onLoadException : function()
13803     {
13804         this.hasQuery = false;
13805         
13806         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13807             this.loading.hide();
13808         }
13809         
13810         if(this.tickable && this.editable){
13811             return;
13812         }
13813         
13814         this.collapse();
13815         // only causes errors at present
13816         //Roo.log(this.store.reader.jsonData);
13817         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13818             // fixme
13819             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13820         //}
13821         
13822         
13823     },
13824     // private
13825     onTypeAhead : function(){
13826         if(this.store.getCount() > 0){
13827             var r = this.store.getAt(0);
13828             var newValue = r.data[this.displayField];
13829             var len = newValue.length;
13830             var selStart = this.getRawValue().length;
13831             
13832             if(selStart != len){
13833                 this.setRawValue(newValue);
13834                 this.selectText(selStart, newValue.length);
13835             }
13836         }
13837     },
13838
13839     // private
13840     onSelect : function(record, index){
13841         
13842         if(this.fireEvent('beforeselect', this, record, index) !== false){
13843         
13844             this.setFromData(index > -1 ? record.data : false);
13845             
13846             this.collapse();
13847             this.fireEvent('select', this, record, index);
13848         }
13849     },
13850
13851     /**
13852      * Returns the currently selected field value or empty string if no value is set.
13853      * @return {String} value The selected value
13854      */
13855     getValue : function()
13856     {
13857         if(Roo.isIOS && this.useNativeIOS){
13858             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13859         }
13860         
13861         if(this.multiple){
13862             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13863         }
13864         
13865         if(this.valueField){
13866             return typeof this.value != 'undefined' ? this.value : '';
13867         }else{
13868             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13869         }
13870     },
13871     
13872     getRawValue : function()
13873     {
13874         if(Roo.isIOS && this.useNativeIOS){
13875             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13876         }
13877         
13878         var v = this.inputEl().getValue();
13879         
13880         return v;
13881     },
13882
13883     /**
13884      * Clears any text/value currently set in the field
13885      */
13886     clearValue : function(){
13887         
13888         if(this.hiddenField){
13889             this.hiddenField.dom.value = '';
13890         }
13891         this.value = '';
13892         this.setRawValue('');
13893         this.lastSelectionText = '';
13894         this.lastData = false;
13895         
13896         var close = this.closeTriggerEl();
13897         
13898         if(close){
13899             close.hide();
13900         }
13901         
13902         this.validate();
13903         
13904     },
13905
13906     /**
13907      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13908      * will be displayed in the field.  If the value does not match the data value of an existing item,
13909      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13910      * Otherwise the field will be blank (although the value will still be set).
13911      * @param {String} value The value to match
13912      */
13913     setValue : function(v)
13914     {
13915         if(Roo.isIOS && this.useNativeIOS){
13916             this.setIOSValue(v);
13917             return;
13918         }
13919         
13920         if(this.multiple){
13921             this.syncValue();
13922             return;
13923         }
13924         
13925         var text = v;
13926         if(this.valueField){
13927             var r = this.findRecord(this.valueField, v);
13928             if(r){
13929                 text = r.data[this.displayField];
13930             }else if(this.valueNotFoundText !== undefined){
13931                 text = this.valueNotFoundText;
13932             }
13933         }
13934         this.lastSelectionText = text;
13935         if(this.hiddenField){
13936             this.hiddenField.dom.value = v;
13937         }
13938         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13939         this.value = v;
13940         
13941         var close = this.closeTriggerEl();
13942         
13943         if(close){
13944             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13945         }
13946         
13947         this.validate();
13948     },
13949     /**
13950      * @property {Object} the last set data for the element
13951      */
13952     
13953     lastData : false,
13954     /**
13955      * Sets the value of the field based on a object which is related to the record format for the store.
13956      * @param {Object} value the value to set as. or false on reset?
13957      */
13958     setFromData : function(o){
13959         
13960         if(this.multiple){
13961             this.addItem(o);
13962             return;
13963         }
13964             
13965         var dv = ''; // display value
13966         var vv = ''; // value value..
13967         this.lastData = o;
13968         if (this.displayField) {
13969             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13970         } else {
13971             // this is an error condition!!!
13972             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13973         }
13974         
13975         if(this.valueField){
13976             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13977         }
13978         
13979         var close = this.closeTriggerEl();
13980         
13981         if(close){
13982             if(dv.length || vv * 1 > 0){
13983                 close.show() ;
13984                 this.blockFocus=true;
13985             } else {
13986                 close.hide();
13987             }             
13988         }
13989         
13990         if(this.hiddenField){
13991             this.hiddenField.dom.value = vv;
13992             
13993             this.lastSelectionText = dv;
13994             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13995             this.value = vv;
13996             return;
13997         }
13998         // no hidden field.. - we store the value in 'value', but still display
13999         // display field!!!!
14000         this.lastSelectionText = dv;
14001         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14002         this.value = vv;
14003         
14004         
14005         
14006     },
14007     // private
14008     reset : function(){
14009         // overridden so that last data is reset..
14010         
14011         if(this.multiple){
14012             this.clearItem();
14013             return;
14014         }
14015         
14016         this.setValue(this.originalValue);
14017         //this.clearInvalid();
14018         this.lastData = false;
14019         if (this.view) {
14020             this.view.clearSelections();
14021         }
14022         
14023         this.validate();
14024     },
14025     // private
14026     findRecord : function(prop, value){
14027         var record;
14028         if(this.store.getCount() > 0){
14029             this.store.each(function(r){
14030                 if(r.data[prop] == value){
14031                     record = r;
14032                     return false;
14033                 }
14034                 return true;
14035             });
14036         }
14037         return record;
14038     },
14039     
14040     getName: function()
14041     {
14042         // returns hidden if it's set..
14043         if (!this.rendered) {return ''};
14044         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14045         
14046     },
14047     // private
14048     onViewMove : function(e, t){
14049         this.inKeyMode = false;
14050     },
14051
14052     // private
14053     onViewOver : function(e, t){
14054         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14055             return;
14056         }
14057         var item = this.view.findItemFromChild(t);
14058         
14059         if(item){
14060             var index = this.view.indexOf(item);
14061             this.select(index, false);
14062         }
14063     },
14064
14065     // private
14066     onViewClick : function(view, doFocus, el, e)
14067     {
14068         var index = this.view.getSelectedIndexes()[0];
14069         
14070         var r = this.store.getAt(index);
14071         
14072         if(this.tickable){
14073             
14074             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14075                 return;
14076             }
14077             
14078             var rm = false;
14079             var _this = this;
14080             
14081             Roo.each(this.tickItems, function(v,k){
14082                 
14083                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14084                     Roo.log(v);
14085                     _this.tickItems.splice(k, 1);
14086                     
14087                     if(typeof(e) == 'undefined' && view == false){
14088                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14089                     }
14090                     
14091                     rm = true;
14092                     return;
14093                 }
14094             });
14095             
14096             if(rm){
14097                 return;
14098             }
14099             
14100             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14101                 this.tickItems.push(r.data);
14102             }
14103             
14104             if(typeof(e) == 'undefined' && view == false){
14105                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14106             }
14107                     
14108             return;
14109         }
14110         
14111         if(r){
14112             this.onSelect(r, index);
14113         }
14114         if(doFocus !== false && !this.blockFocus){
14115             this.inputEl().focus();
14116         }
14117     },
14118
14119     // private
14120     restrictHeight : function(){
14121         //this.innerList.dom.style.height = '';
14122         //var inner = this.innerList.dom;
14123         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14124         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14125         //this.list.beginUpdate();
14126         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14127         this.list.alignTo(this.inputEl(), this.listAlign);
14128         this.list.alignTo(this.inputEl(), this.listAlign);
14129         //this.list.endUpdate();
14130     },
14131
14132     // private
14133     onEmptyResults : function(){
14134         
14135         if(this.tickable && this.editable){
14136             this.hasFocus = false;
14137             this.restrictHeight();
14138             return;
14139         }
14140         
14141         this.collapse();
14142     },
14143
14144     /**
14145      * Returns true if the dropdown list is expanded, else false.
14146      */
14147     isExpanded : function(){
14148         return this.list.isVisible();
14149     },
14150
14151     /**
14152      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14153      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14154      * @param {String} value The data value of the item to select
14155      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14156      * selected item if it is not currently in view (defaults to true)
14157      * @return {Boolean} True if the value matched an item in the list, else false
14158      */
14159     selectByValue : function(v, scrollIntoView){
14160         if(v !== undefined && v !== null){
14161             var r = this.findRecord(this.valueField || this.displayField, v);
14162             if(r){
14163                 this.select(this.store.indexOf(r), scrollIntoView);
14164                 return true;
14165             }
14166         }
14167         return false;
14168     },
14169
14170     /**
14171      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14172      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14173      * @param {Number} index The zero-based index of the list item to select
14174      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14175      * selected item if it is not currently in view (defaults to true)
14176      */
14177     select : function(index, scrollIntoView){
14178         this.selectedIndex = index;
14179         this.view.select(index);
14180         if(scrollIntoView !== false){
14181             var el = this.view.getNode(index);
14182             /*
14183              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14184              */
14185             if(el){
14186                 this.list.scrollChildIntoView(el, false);
14187             }
14188         }
14189     },
14190
14191     // private
14192     selectNext : function(){
14193         var ct = this.store.getCount();
14194         if(ct > 0){
14195             if(this.selectedIndex == -1){
14196                 this.select(0);
14197             }else if(this.selectedIndex < ct-1){
14198                 this.select(this.selectedIndex+1);
14199             }
14200         }
14201     },
14202
14203     // private
14204     selectPrev : function(){
14205         var ct = this.store.getCount();
14206         if(ct > 0){
14207             if(this.selectedIndex == -1){
14208                 this.select(0);
14209             }else if(this.selectedIndex != 0){
14210                 this.select(this.selectedIndex-1);
14211             }
14212         }
14213     },
14214
14215     // private
14216     onKeyUp : function(e){
14217         if(this.editable !== false && !e.isSpecialKey()){
14218             this.lastKey = e.getKey();
14219             this.dqTask.delay(this.queryDelay);
14220         }
14221     },
14222
14223     // private
14224     validateBlur : function(){
14225         return !this.list || !this.list.isVisible();   
14226     },
14227
14228     // private
14229     initQuery : function(){
14230         
14231         var v = this.getRawValue();
14232         
14233         if(this.tickable && this.editable){
14234             v = this.tickableInputEl().getValue();
14235         }
14236         
14237         this.doQuery(v);
14238     },
14239
14240     // private
14241     doForce : function(){
14242         if(this.inputEl().dom.value.length > 0){
14243             this.inputEl().dom.value =
14244                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14245              
14246         }
14247     },
14248
14249     /**
14250      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14251      * query allowing the query action to be canceled if needed.
14252      * @param {String} query The SQL query to execute
14253      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14254      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14255      * saved in the current store (defaults to false)
14256      */
14257     doQuery : function(q, forceAll){
14258         
14259         if(q === undefined || q === null){
14260             q = '';
14261         }
14262         var qe = {
14263             query: q,
14264             forceAll: forceAll,
14265             combo: this,
14266             cancel:false
14267         };
14268         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14269             return false;
14270         }
14271         q = qe.query;
14272         
14273         forceAll = qe.forceAll;
14274         if(forceAll === true || (q.length >= this.minChars)){
14275             
14276             this.hasQuery = true;
14277             
14278             if(this.lastQuery != q || this.alwaysQuery){
14279                 this.lastQuery = q;
14280                 if(this.mode == 'local'){
14281                     this.selectedIndex = -1;
14282                     if(forceAll){
14283                         this.store.clearFilter();
14284                     }else{
14285                         
14286                         if(this.specialFilter){
14287                             this.fireEvent('specialfilter', this);
14288                             this.onLoad();
14289                             return;
14290                         }
14291                         
14292                         this.store.filter(this.displayField, q);
14293                     }
14294                     
14295                     this.store.fireEvent("datachanged", this.store);
14296                     
14297                     this.onLoad();
14298                     
14299                     
14300                 }else{
14301                     
14302                     this.store.baseParams[this.queryParam] = q;
14303                     
14304                     var options = {params : this.getParams(q)};
14305                     
14306                     if(this.loadNext){
14307                         options.add = true;
14308                         options.params.start = this.page * this.pageSize;
14309                     }
14310                     
14311                     this.store.load(options);
14312                     
14313                     /*
14314                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14315                      *  we should expand the list on onLoad
14316                      *  so command out it
14317                      */
14318 //                    this.expand();
14319                 }
14320             }else{
14321                 this.selectedIndex = -1;
14322                 this.onLoad();   
14323             }
14324         }
14325         
14326         this.loadNext = false;
14327     },
14328     
14329     // private
14330     getParams : function(q){
14331         var p = {};
14332         //p[this.queryParam] = q;
14333         
14334         if(this.pageSize){
14335             p.start = 0;
14336             p.limit = this.pageSize;
14337         }
14338         return p;
14339     },
14340
14341     /**
14342      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14343      */
14344     collapse : function(){
14345         if(!this.isExpanded()){
14346             return;
14347         }
14348         
14349         this.list.hide();
14350         
14351         this.hasFocus = false;
14352         
14353         if(this.tickable){
14354             this.okBtn.hide();
14355             this.cancelBtn.hide();
14356             this.trigger.show();
14357             
14358             if(this.editable){
14359                 this.tickableInputEl().dom.value = '';
14360                 this.tickableInputEl().blur();
14361             }
14362             
14363         }
14364         
14365         Roo.get(document).un('mousedown', this.collapseIf, this);
14366         Roo.get(document).un('mousewheel', this.collapseIf, this);
14367         if (!this.editable) {
14368             Roo.get(document).un('keydown', this.listKeyPress, this);
14369         }
14370         this.fireEvent('collapse', this);
14371         
14372         this.validate();
14373     },
14374
14375     // private
14376     collapseIf : function(e){
14377         var in_combo  = e.within(this.el);
14378         var in_list =  e.within(this.list);
14379         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14380         
14381         if (in_combo || in_list || is_list) {
14382             //e.stopPropagation();
14383             return;
14384         }
14385         
14386         if(this.tickable){
14387             this.onTickableFooterButtonClick(e, false, false);
14388         }
14389
14390         this.collapse();
14391         
14392     },
14393
14394     /**
14395      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14396      */
14397     expand : function(){
14398        
14399         if(this.isExpanded() || !this.hasFocus){
14400             return;
14401         }
14402         
14403         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14404         this.list.setWidth(lw);
14405         
14406         Roo.log('expand');
14407         
14408         this.list.show();
14409         
14410         this.restrictHeight();
14411         
14412         if(this.tickable){
14413             
14414             this.tickItems = Roo.apply([], this.item);
14415             
14416             this.okBtn.show();
14417             this.cancelBtn.show();
14418             this.trigger.hide();
14419             
14420             if(this.editable){
14421                 this.tickableInputEl().focus();
14422             }
14423             
14424         }
14425         
14426         Roo.get(document).on('mousedown', this.collapseIf, this);
14427         Roo.get(document).on('mousewheel', this.collapseIf, this);
14428         if (!this.editable) {
14429             Roo.get(document).on('keydown', this.listKeyPress, this);
14430         }
14431         
14432         this.fireEvent('expand', this);
14433     },
14434
14435     // private
14436     // Implements the default empty TriggerField.onTriggerClick function
14437     onTriggerClick : function(e)
14438     {
14439         Roo.log('trigger click');
14440         
14441         if(this.disabled || !this.triggerList){
14442             return;
14443         }
14444         
14445         this.page = 0;
14446         this.loadNext = false;
14447         
14448         if(this.isExpanded()){
14449             this.collapse();
14450             if (!this.blockFocus) {
14451                 this.inputEl().focus();
14452             }
14453             
14454         }else {
14455             this.hasFocus = true;
14456             if(this.triggerAction == 'all') {
14457                 this.doQuery(this.allQuery, true);
14458             } else {
14459                 this.doQuery(this.getRawValue());
14460             }
14461             if (!this.blockFocus) {
14462                 this.inputEl().focus();
14463             }
14464         }
14465     },
14466     
14467     onTickableTriggerClick : function(e)
14468     {
14469         if(this.disabled){
14470             return;
14471         }
14472         
14473         this.page = 0;
14474         this.loadNext = false;
14475         this.hasFocus = true;
14476         
14477         if(this.triggerAction == 'all') {
14478             this.doQuery(this.allQuery, true);
14479         } else {
14480             this.doQuery(this.getRawValue());
14481         }
14482     },
14483     
14484     onSearchFieldClick : function(e)
14485     {
14486         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14487             this.onTickableFooterButtonClick(e, false, false);
14488             return;
14489         }
14490         
14491         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14492             return;
14493         }
14494         
14495         this.page = 0;
14496         this.loadNext = false;
14497         this.hasFocus = true;
14498         
14499         if(this.triggerAction == 'all') {
14500             this.doQuery(this.allQuery, true);
14501         } else {
14502             this.doQuery(this.getRawValue());
14503         }
14504     },
14505     
14506     listKeyPress : function(e)
14507     {
14508         //Roo.log('listkeypress');
14509         // scroll to first matching element based on key pres..
14510         if (e.isSpecialKey()) {
14511             return false;
14512         }
14513         var k = String.fromCharCode(e.getKey()).toUpperCase();
14514         //Roo.log(k);
14515         var match  = false;
14516         var csel = this.view.getSelectedNodes();
14517         var cselitem = false;
14518         if (csel.length) {
14519             var ix = this.view.indexOf(csel[0]);
14520             cselitem  = this.store.getAt(ix);
14521             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14522                 cselitem = false;
14523             }
14524             
14525         }
14526         
14527         this.store.each(function(v) { 
14528             if (cselitem) {
14529                 // start at existing selection.
14530                 if (cselitem.id == v.id) {
14531                     cselitem = false;
14532                 }
14533                 return true;
14534             }
14535                 
14536             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14537                 match = this.store.indexOf(v);
14538                 return false;
14539             }
14540             return true;
14541         }, this);
14542         
14543         if (match === false) {
14544             return true; // no more action?
14545         }
14546         // scroll to?
14547         this.view.select(match);
14548         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14549         sn.scrollIntoView(sn.dom.parentNode, false);
14550     },
14551     
14552     onViewScroll : function(e, t){
14553         
14554         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){
14555             return;
14556         }
14557         
14558         this.hasQuery = true;
14559         
14560         this.loading = this.list.select('.loading', true).first();
14561         
14562         if(this.loading === null){
14563             this.list.createChild({
14564                 tag: 'div',
14565                 cls: 'loading roo-select2-more-results roo-select2-active',
14566                 html: 'Loading more results...'
14567             });
14568             
14569             this.loading = this.list.select('.loading', true).first();
14570             
14571             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14572             
14573             this.loading.hide();
14574         }
14575         
14576         this.loading.show();
14577         
14578         var _combo = this;
14579         
14580         this.page++;
14581         this.loadNext = true;
14582         
14583         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14584         
14585         return;
14586     },
14587     
14588     addItem : function(o)
14589     {   
14590         var dv = ''; // display value
14591         
14592         if (this.displayField) {
14593             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14594         } else {
14595             // this is an error condition!!!
14596             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14597         }
14598         
14599         if(!dv.length){
14600             return;
14601         }
14602         
14603         var choice = this.choices.createChild({
14604             tag: 'li',
14605             cls: 'roo-select2-search-choice',
14606             cn: [
14607                 {
14608                     tag: 'div',
14609                     html: dv
14610                 },
14611                 {
14612                     tag: 'a',
14613                     href: '#',
14614                     cls: 'roo-select2-search-choice-close fa fa-times',
14615                     tabindex: '-1'
14616                 }
14617             ]
14618             
14619         }, this.searchField);
14620         
14621         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14622         
14623         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14624         
14625         this.item.push(o);
14626         
14627         this.lastData = o;
14628         
14629         this.syncValue();
14630         
14631         this.inputEl().dom.value = '';
14632         
14633         this.validate();
14634     },
14635     
14636     onRemoveItem : function(e, _self, o)
14637     {
14638         e.preventDefault();
14639         
14640         this.lastItem = Roo.apply([], this.item);
14641         
14642         var index = this.item.indexOf(o.data) * 1;
14643         
14644         if( index < 0){
14645             Roo.log('not this item?!');
14646             return;
14647         }
14648         
14649         this.item.splice(index, 1);
14650         o.item.remove();
14651         
14652         this.syncValue();
14653         
14654         this.fireEvent('remove', this, e);
14655         
14656         this.validate();
14657         
14658     },
14659     
14660     syncValue : function()
14661     {
14662         if(!this.item.length){
14663             this.clearValue();
14664             return;
14665         }
14666             
14667         var value = [];
14668         var _this = this;
14669         Roo.each(this.item, function(i){
14670             if(_this.valueField){
14671                 value.push(i[_this.valueField]);
14672                 return;
14673             }
14674
14675             value.push(i);
14676         });
14677
14678         this.value = value.join(',');
14679
14680         if(this.hiddenField){
14681             this.hiddenField.dom.value = this.value;
14682         }
14683         
14684         this.store.fireEvent("datachanged", this.store);
14685         
14686         this.validate();
14687     },
14688     
14689     clearItem : function()
14690     {
14691         if(!this.multiple){
14692             return;
14693         }
14694         
14695         this.item = [];
14696         
14697         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14698            c.remove();
14699         });
14700         
14701         this.syncValue();
14702         
14703         this.validate();
14704         
14705         if(this.tickable && !Roo.isTouch){
14706             this.view.refresh();
14707         }
14708     },
14709     
14710     inputEl: function ()
14711     {
14712         if(Roo.isIOS && this.useNativeIOS){
14713             return this.el.select('select.roo-ios-select', true).first();
14714         }
14715         
14716         if(Roo.isTouch && this.mobileTouchView){
14717             return this.el.select('input.form-control',true).first();
14718         }
14719         
14720         if(this.tickable){
14721             return this.searchField;
14722         }
14723         
14724         return this.el.select('input.form-control',true).first();
14725     },
14726     
14727     onTickableFooterButtonClick : function(e, btn, el)
14728     {
14729         e.preventDefault();
14730         
14731         this.lastItem = Roo.apply([], this.item);
14732         
14733         if(btn && btn.name == 'cancel'){
14734             this.tickItems = Roo.apply([], this.item);
14735             this.collapse();
14736             return;
14737         }
14738         
14739         this.clearItem();
14740         
14741         var _this = this;
14742         
14743         Roo.each(this.tickItems, function(o){
14744             _this.addItem(o);
14745         });
14746         
14747         this.collapse();
14748         
14749     },
14750     
14751     validate : function()
14752     {
14753         if(this.getVisibilityEl().hasClass('hidden')){
14754             return true;
14755         }
14756         
14757         var v = this.getRawValue();
14758         
14759         if(this.multiple){
14760             v = this.getValue();
14761         }
14762         
14763         if(this.disabled || this.allowBlank || v.length){
14764             this.markValid();
14765             return true;
14766         }
14767         
14768         this.markInvalid();
14769         return false;
14770     },
14771     
14772     tickableInputEl : function()
14773     {
14774         if(!this.tickable || !this.editable){
14775             return this.inputEl();
14776         }
14777         
14778         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14779     },
14780     
14781     
14782     getAutoCreateTouchView : function()
14783     {
14784         var id = Roo.id();
14785         
14786         var cfg = {
14787             cls: 'form-group' //input-group
14788         };
14789         
14790         var input =  {
14791             tag: 'input',
14792             id : id,
14793             type : this.inputType,
14794             cls : 'form-control x-combo-noedit',
14795             autocomplete: 'new-password',
14796             placeholder : this.placeholder || '',
14797             readonly : true
14798         };
14799         
14800         if (this.name) {
14801             input.name = this.name;
14802         }
14803         
14804         if (this.size) {
14805             input.cls += ' input-' + this.size;
14806         }
14807         
14808         if (this.disabled) {
14809             input.disabled = true;
14810         }
14811         
14812         var inputblock = {
14813             cls : '',
14814             cn : [
14815                 input
14816             ]
14817         };
14818         
14819         if(this.before){
14820             inputblock.cls += ' input-group';
14821             
14822             inputblock.cn.unshift({
14823                 tag :'span',
14824                 cls : 'input-group-addon',
14825                 html : this.before
14826             });
14827         }
14828         
14829         if(this.removable && !this.multiple){
14830             inputblock.cls += ' roo-removable';
14831             
14832             inputblock.cn.push({
14833                 tag: 'button',
14834                 html : 'x',
14835                 cls : 'roo-combo-removable-btn close'
14836             });
14837         }
14838
14839         if(this.hasFeedback && !this.allowBlank){
14840             
14841             inputblock.cls += ' has-feedback';
14842             
14843             inputblock.cn.push({
14844                 tag: 'span',
14845                 cls: 'glyphicon form-control-feedback'
14846             });
14847             
14848         }
14849         
14850         if (this.after) {
14851             
14852             inputblock.cls += (this.before) ? '' : ' input-group';
14853             
14854             inputblock.cn.push({
14855                 tag :'span',
14856                 cls : 'input-group-addon',
14857                 html : this.after
14858             });
14859         }
14860
14861         var box = {
14862             tag: 'div',
14863             cn: [
14864                 {
14865                     tag: 'input',
14866                     type : 'hidden',
14867                     cls: 'form-hidden-field'
14868                 },
14869                 inputblock
14870             ]
14871             
14872         };
14873         
14874         if(this.multiple){
14875             box = {
14876                 tag: 'div',
14877                 cn: [
14878                     {
14879                         tag: 'input',
14880                         type : 'hidden',
14881                         cls: 'form-hidden-field'
14882                     },
14883                     {
14884                         tag: 'ul',
14885                         cls: 'roo-select2-choices',
14886                         cn:[
14887                             {
14888                                 tag: 'li',
14889                                 cls: 'roo-select2-search-field',
14890                                 cn: [
14891
14892                                     inputblock
14893                                 ]
14894                             }
14895                         ]
14896                     }
14897                 ]
14898             }
14899         };
14900         
14901         var combobox = {
14902             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14903             cn: [
14904                 box
14905             ]
14906         };
14907         
14908         if(!this.multiple && this.showToggleBtn){
14909             
14910             var caret = {
14911                         tag: 'span',
14912                         cls: 'caret'
14913             };
14914             
14915             if (this.caret != false) {
14916                 caret = {
14917                      tag: 'i',
14918                      cls: 'fa fa-' + this.caret
14919                 };
14920                 
14921             }
14922             
14923             combobox.cn.push({
14924                 tag :'span',
14925                 cls : 'input-group-addon btn dropdown-toggle',
14926                 cn : [
14927                     caret,
14928                     {
14929                         tag: 'span',
14930                         cls: 'combobox-clear',
14931                         cn  : [
14932                             {
14933                                 tag : 'i',
14934                                 cls: 'icon-remove'
14935                             }
14936                         ]
14937                     }
14938                 ]
14939
14940             })
14941         }
14942         
14943         if(this.multiple){
14944             combobox.cls += ' roo-select2-container-multi';
14945         }
14946         
14947         var align = this.labelAlign || this.parentLabelAlign();
14948         
14949         if (align ==='left' && this.fieldLabel.length) {
14950
14951             cfg.cn = [
14952                 {
14953                    tag : 'i',
14954                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14955                    tooltip : 'This field is required'
14956                 },
14957                 {
14958                     tag: 'label',
14959                     cls : 'control-label',
14960                     html : this.fieldLabel
14961
14962                 },
14963                 {
14964                     cls : '', 
14965                     cn: [
14966                         combobox
14967                     ]
14968                 }
14969             ];
14970             
14971             var labelCfg = cfg.cn[1];
14972             var contentCfg = cfg.cn[2];
14973             
14974
14975             if(this.indicatorpos == 'right'){
14976                 cfg.cn = [
14977                     {
14978                         tag: 'label',
14979                         'for' :  id,
14980                         cls : 'control-label',
14981                         cn : [
14982                             {
14983                                 tag : 'span',
14984                                 html : this.fieldLabel
14985                             },
14986                             {
14987                                 tag : 'i',
14988                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14989                                 tooltip : 'This field is required'
14990                             }
14991                         ]
14992                     },
14993                     {
14994                         cls : "",
14995                         cn: [
14996                             combobox
14997                         ]
14998                     }
14999
15000                 ];
15001                 
15002                 labelCfg = cfg.cn[0];
15003                 contentCfg = cfg.cn[1];
15004             }
15005             
15006            
15007             
15008             if(this.labelWidth > 12){
15009                 labelCfg.style = "width: " + this.labelWidth + 'px';
15010             }
15011             
15012             if(this.labelWidth < 13 && this.labelmd == 0){
15013                 this.labelmd = this.labelWidth;
15014             }
15015             
15016             if(this.labellg > 0){
15017                 labelCfg.cls += ' col-lg-' + this.labellg;
15018                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15019             }
15020             
15021             if(this.labelmd > 0){
15022                 labelCfg.cls += ' col-md-' + this.labelmd;
15023                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15024             }
15025             
15026             if(this.labelsm > 0){
15027                 labelCfg.cls += ' col-sm-' + this.labelsm;
15028                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15029             }
15030             
15031             if(this.labelxs > 0){
15032                 labelCfg.cls += ' col-xs-' + this.labelxs;
15033                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15034             }
15035                 
15036                 
15037         } else if ( this.fieldLabel.length) {
15038             cfg.cn = [
15039                 {
15040                    tag : 'i',
15041                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15042                    tooltip : 'This field is required'
15043                 },
15044                 {
15045                     tag: 'label',
15046                     cls : 'control-label',
15047                     html : this.fieldLabel
15048
15049                 },
15050                 {
15051                     cls : '', 
15052                     cn: [
15053                         combobox
15054                     ]
15055                 }
15056             ];
15057             
15058             if(this.indicatorpos == 'right'){
15059                 cfg.cn = [
15060                     {
15061                         tag: 'label',
15062                         cls : 'control-label',
15063                         html : this.fieldLabel,
15064                         cn : [
15065                             {
15066                                tag : 'i',
15067                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15068                                tooltip : 'This field is required'
15069                             }
15070                         ]
15071                     },
15072                     {
15073                         cls : '', 
15074                         cn: [
15075                             combobox
15076                         ]
15077                     }
15078                 ];
15079             }
15080         } else {
15081             cfg.cn = combobox;    
15082         }
15083         
15084         
15085         var settings = this;
15086         
15087         ['xs','sm','md','lg'].map(function(size){
15088             if (settings[size]) {
15089                 cfg.cls += ' col-' + size + '-' + settings[size];
15090             }
15091         });
15092         
15093         return cfg;
15094     },
15095     
15096     initTouchView : function()
15097     {
15098         this.renderTouchView();
15099         
15100         this.touchViewEl.on('scroll', function(){
15101             this.el.dom.scrollTop = 0;
15102         }, this);
15103         
15104         this.originalValue = this.getValue();
15105         
15106         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15107         
15108         this.inputEl().on("click", this.showTouchView, this);
15109         if (this.triggerEl) {
15110             this.triggerEl.on("click", this.showTouchView, this);
15111         }
15112         
15113         
15114         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15115         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15116         
15117         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15118         
15119         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15120         this.store.on('load', this.onTouchViewLoad, this);
15121         this.store.on('loadexception', this.onTouchViewLoadException, this);
15122         
15123         if(this.hiddenName){
15124             
15125             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15126             
15127             this.hiddenField.dom.value =
15128                 this.hiddenValue !== undefined ? this.hiddenValue :
15129                 this.value !== undefined ? this.value : '';
15130         
15131             this.el.dom.removeAttribute('name');
15132             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15133         }
15134         
15135         if(this.multiple){
15136             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15137             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15138         }
15139         
15140         if(this.removable && !this.multiple){
15141             var close = this.closeTriggerEl();
15142             if(close){
15143                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15144                 close.on('click', this.removeBtnClick, this, close);
15145             }
15146         }
15147         /*
15148          * fix the bug in Safari iOS8
15149          */
15150         this.inputEl().on("focus", function(e){
15151             document.activeElement.blur();
15152         }, this);
15153         
15154         return;
15155         
15156         
15157     },
15158     
15159     renderTouchView : function()
15160     {
15161         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15162         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15163         
15164         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15165         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15166         
15167         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15168         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15169         this.touchViewBodyEl.setStyle('overflow', 'auto');
15170         
15171         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15172         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15173         
15174         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15175         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15176         
15177     },
15178     
15179     showTouchView : function()
15180     {
15181         if(this.disabled){
15182             return;
15183         }
15184         
15185         this.touchViewHeaderEl.hide();
15186
15187         if(this.modalTitle.length){
15188             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15189             this.touchViewHeaderEl.show();
15190         }
15191
15192         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15193         this.touchViewEl.show();
15194
15195         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15196         
15197         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15198         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15199
15200         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15201
15202         if(this.modalTitle.length){
15203             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15204         }
15205         
15206         this.touchViewBodyEl.setHeight(bodyHeight);
15207
15208         if(this.animate){
15209             var _this = this;
15210             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15211         }else{
15212             this.touchViewEl.addClass('in');
15213         }
15214
15215         this.doTouchViewQuery();
15216         
15217     },
15218     
15219     hideTouchView : function()
15220     {
15221         this.touchViewEl.removeClass('in');
15222
15223         if(this.animate){
15224             var _this = this;
15225             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15226         }else{
15227             this.touchViewEl.setStyle('display', 'none');
15228         }
15229         
15230     },
15231     
15232     setTouchViewValue : function()
15233     {
15234         if(this.multiple){
15235             this.clearItem();
15236         
15237             var _this = this;
15238
15239             Roo.each(this.tickItems, function(o){
15240                 this.addItem(o);
15241             }, this);
15242         }
15243         
15244         this.hideTouchView();
15245     },
15246     
15247     doTouchViewQuery : function()
15248     {
15249         var qe = {
15250             query: '',
15251             forceAll: true,
15252             combo: this,
15253             cancel:false
15254         };
15255         
15256         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15257             return false;
15258         }
15259         
15260         if(!this.alwaysQuery || this.mode == 'local'){
15261             this.onTouchViewLoad();
15262             return;
15263         }
15264         
15265         this.store.load();
15266     },
15267     
15268     onTouchViewBeforeLoad : function(combo,opts)
15269     {
15270         return;
15271     },
15272
15273     // private
15274     onTouchViewLoad : function()
15275     {
15276         if(this.store.getCount() < 1){
15277             this.onTouchViewEmptyResults();
15278             return;
15279         }
15280         
15281         this.clearTouchView();
15282         
15283         var rawValue = this.getRawValue();
15284         
15285         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15286         
15287         this.tickItems = [];
15288         
15289         this.store.data.each(function(d, rowIndex){
15290             var row = this.touchViewListGroup.createChild(template);
15291             
15292             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15293                 row.addClass(d.data.cls);
15294             }
15295             
15296             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15297                 var cfg = {
15298                     data : d.data,
15299                     html : d.data[this.displayField]
15300                 };
15301                 
15302                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15303                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15304                 }
15305             }
15306             row.removeClass('selected');
15307             if(!this.multiple && this.valueField &&
15308                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15309             {
15310                 // radio buttons..
15311                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15312                 row.addClass('selected');
15313             }
15314             
15315             if(this.multiple && this.valueField &&
15316                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15317             {
15318                 
15319                 // checkboxes...
15320                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15321                 this.tickItems.push(d.data);
15322             }
15323             
15324             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15325             
15326         }, this);
15327         
15328         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15329         
15330         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15331
15332         if(this.modalTitle.length){
15333             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15334         }
15335
15336         var listHeight = this.touchViewListGroup.getHeight();
15337         
15338         var _this = this;
15339         
15340         if(firstChecked && listHeight > bodyHeight){
15341             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15342         }
15343         
15344     },
15345     
15346     onTouchViewLoadException : function()
15347     {
15348         this.hideTouchView();
15349     },
15350     
15351     onTouchViewEmptyResults : function()
15352     {
15353         this.clearTouchView();
15354         
15355         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15356         
15357         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15358         
15359     },
15360     
15361     clearTouchView : function()
15362     {
15363         this.touchViewListGroup.dom.innerHTML = '';
15364     },
15365     
15366     onTouchViewClick : function(e, el, o)
15367     {
15368         e.preventDefault();
15369         
15370         var row = o.row;
15371         var rowIndex = o.rowIndex;
15372         
15373         var r = this.store.getAt(rowIndex);
15374         
15375         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15376             
15377             if(!this.multiple){
15378                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15379                     c.dom.removeAttribute('checked');
15380                 }, this);
15381
15382                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15383
15384                 this.setFromData(r.data);
15385
15386                 var close = this.closeTriggerEl();
15387
15388                 if(close){
15389                     close.show();
15390                 }
15391
15392                 this.hideTouchView();
15393
15394                 this.fireEvent('select', this, r, rowIndex);
15395
15396                 return;
15397             }
15398
15399             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15400                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15401                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15402                 return;
15403             }
15404
15405             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15406             this.addItem(r.data);
15407             this.tickItems.push(r.data);
15408         }
15409     },
15410     
15411     getAutoCreateNativeIOS : function()
15412     {
15413         var cfg = {
15414             cls: 'form-group' //input-group,
15415         };
15416         
15417         var combobox =  {
15418             tag: 'select',
15419             cls : 'roo-ios-select'
15420         };
15421         
15422         if (this.name) {
15423             combobox.name = this.name;
15424         }
15425         
15426         if (this.disabled) {
15427             combobox.disabled = true;
15428         }
15429         
15430         var settings = this;
15431         
15432         ['xs','sm','md','lg'].map(function(size){
15433             if (settings[size]) {
15434                 cfg.cls += ' col-' + size + '-' + settings[size];
15435             }
15436         });
15437         
15438         cfg.cn = combobox;
15439         
15440         return cfg;
15441         
15442     },
15443     
15444     initIOSView : function()
15445     {
15446         this.store.on('load', this.onIOSViewLoad, this);
15447         
15448         return;
15449     },
15450     
15451     onIOSViewLoad : function()
15452     {
15453         if(this.store.getCount() < 1){
15454             return;
15455         }
15456         
15457         this.clearIOSView();
15458         
15459         if(this.allowBlank) {
15460             
15461             var default_text = '-- SELECT --';
15462             
15463             if(this.placeholder.length){
15464                 default_text = this.placeholder;
15465             }
15466             
15467             if(this.emptyTitle.length){
15468                 default_text += ' - ' + this.emptyTitle + ' -';
15469             }
15470             
15471             var opt = this.inputEl().createChild({
15472                 tag: 'option',
15473                 value : 0,
15474                 html : default_text
15475             });
15476             
15477             var o = {};
15478             o[this.valueField] = 0;
15479             o[this.displayField] = default_text;
15480             
15481             this.ios_options.push({
15482                 data : o,
15483                 el : opt
15484             });
15485             
15486         }
15487         
15488         this.store.data.each(function(d, rowIndex){
15489             
15490             var html = '';
15491             
15492             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15493                 html = d.data[this.displayField];
15494             }
15495             
15496             var value = '';
15497             
15498             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15499                 value = d.data[this.valueField];
15500             }
15501             
15502             var option = {
15503                 tag: 'option',
15504                 value : value,
15505                 html : html
15506             };
15507             
15508             if(this.value == d.data[this.valueField]){
15509                 option['selected'] = true;
15510             }
15511             
15512             var opt = this.inputEl().createChild(option);
15513             
15514             this.ios_options.push({
15515                 data : d.data,
15516                 el : opt
15517             });
15518             
15519         }, this);
15520         
15521         this.inputEl().on('change', function(){
15522            this.fireEvent('select', this);
15523         }, this);
15524         
15525     },
15526     
15527     clearIOSView: function()
15528     {
15529         this.inputEl().dom.innerHTML = '';
15530         
15531         this.ios_options = [];
15532     },
15533     
15534     setIOSValue: function(v)
15535     {
15536         this.value = v;
15537         
15538         if(!this.ios_options){
15539             return;
15540         }
15541         
15542         Roo.each(this.ios_options, function(opts){
15543            
15544            opts.el.dom.removeAttribute('selected');
15545            
15546            if(opts.data[this.valueField] != v){
15547                return;
15548            }
15549            
15550            opts.el.dom.setAttribute('selected', true);
15551            
15552         }, this);
15553     }
15554
15555     /** 
15556     * @cfg {Boolean} grow 
15557     * @hide 
15558     */
15559     /** 
15560     * @cfg {Number} growMin 
15561     * @hide 
15562     */
15563     /** 
15564     * @cfg {Number} growMax 
15565     * @hide 
15566     */
15567     /**
15568      * @hide
15569      * @method autoSize
15570      */
15571 });
15572
15573 Roo.apply(Roo.bootstrap.ComboBox,  {
15574     
15575     header : {
15576         tag: 'div',
15577         cls: 'modal-header',
15578         cn: [
15579             {
15580                 tag: 'h4',
15581                 cls: 'modal-title'
15582             }
15583         ]
15584     },
15585     
15586     body : {
15587         tag: 'div',
15588         cls: 'modal-body',
15589         cn: [
15590             {
15591                 tag: 'ul',
15592                 cls: 'list-group'
15593             }
15594         ]
15595     },
15596     
15597     listItemRadio : {
15598         tag: 'li',
15599         cls: 'list-group-item',
15600         cn: [
15601             {
15602                 tag: 'span',
15603                 cls: 'roo-combobox-list-group-item-value'
15604             },
15605             {
15606                 tag: 'div',
15607                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15608                 cn: [
15609                     {
15610                         tag: 'input',
15611                         type: 'radio'
15612                     },
15613                     {
15614                         tag: 'label'
15615                     }
15616                 ]
15617             }
15618         ]
15619     },
15620     
15621     listItemCheckbox : {
15622         tag: 'li',
15623         cls: 'list-group-item',
15624         cn: [
15625             {
15626                 tag: 'span',
15627                 cls: 'roo-combobox-list-group-item-value'
15628             },
15629             {
15630                 tag: 'div',
15631                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15632                 cn: [
15633                     {
15634                         tag: 'input',
15635                         type: 'checkbox'
15636                     },
15637                     {
15638                         tag: 'label'
15639                     }
15640                 ]
15641             }
15642         ]
15643     },
15644     
15645     emptyResult : {
15646         tag: 'div',
15647         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15648     },
15649     
15650     footer : {
15651         tag: 'div',
15652         cls: 'modal-footer',
15653         cn: [
15654             {
15655                 tag: 'div',
15656                 cls: 'row',
15657                 cn: [
15658                     {
15659                         tag: 'div',
15660                         cls: 'col-xs-6 text-left',
15661                         cn: {
15662                             tag: 'button',
15663                             cls: 'btn btn-danger roo-touch-view-cancel',
15664                             html: 'Cancel'
15665                         }
15666                     },
15667                     {
15668                         tag: 'div',
15669                         cls: 'col-xs-6 text-right',
15670                         cn: {
15671                             tag: 'button',
15672                             cls: 'btn btn-success roo-touch-view-ok',
15673                             html: 'OK'
15674                         }
15675                     }
15676                 ]
15677             }
15678         ]
15679         
15680     }
15681 });
15682
15683 Roo.apply(Roo.bootstrap.ComboBox,  {
15684     
15685     touchViewTemplate : {
15686         tag: 'div',
15687         cls: 'modal fade roo-combobox-touch-view',
15688         cn: [
15689             {
15690                 tag: 'div',
15691                 cls: 'modal-dialog',
15692                 style : 'position:fixed', // we have to fix position....
15693                 cn: [
15694                     {
15695                         tag: 'div',
15696                         cls: 'modal-content',
15697                         cn: [
15698                             Roo.bootstrap.ComboBox.header,
15699                             Roo.bootstrap.ComboBox.body,
15700                             Roo.bootstrap.ComboBox.footer
15701                         ]
15702                     }
15703                 ]
15704             }
15705         ]
15706     }
15707 });/*
15708  * Based on:
15709  * Ext JS Library 1.1.1
15710  * Copyright(c) 2006-2007, Ext JS, LLC.
15711  *
15712  * Originally Released Under LGPL - original licence link has changed is not relivant.
15713  *
15714  * Fork - LGPL
15715  * <script type="text/javascript">
15716  */
15717
15718 /**
15719  * @class Roo.View
15720  * @extends Roo.util.Observable
15721  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15722  * This class also supports single and multi selection modes. <br>
15723  * Create a data model bound view:
15724  <pre><code>
15725  var store = new Roo.data.Store(...);
15726
15727  var view = new Roo.View({
15728     el : "my-element",
15729     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15730  
15731     singleSelect: true,
15732     selectedClass: "ydataview-selected",
15733     store: store
15734  });
15735
15736  // listen for node click?
15737  view.on("click", function(vw, index, node, e){
15738  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15739  });
15740
15741  // load XML data
15742  dataModel.load("foobar.xml");
15743  </code></pre>
15744  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15745  * <br><br>
15746  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15747  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15748  * 
15749  * Note: old style constructor is still suported (container, template, config)
15750  * 
15751  * @constructor
15752  * Create a new View
15753  * @param {Object} config The config object
15754  * 
15755  */
15756 Roo.View = function(config, depreciated_tpl, depreciated_config){
15757     
15758     this.parent = false;
15759     
15760     if (typeof(depreciated_tpl) == 'undefined') {
15761         // new way.. - universal constructor.
15762         Roo.apply(this, config);
15763         this.el  = Roo.get(this.el);
15764     } else {
15765         // old format..
15766         this.el  = Roo.get(config);
15767         this.tpl = depreciated_tpl;
15768         Roo.apply(this, depreciated_config);
15769     }
15770     this.wrapEl  = this.el.wrap().wrap();
15771     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15772     
15773     
15774     if(typeof(this.tpl) == "string"){
15775         this.tpl = new Roo.Template(this.tpl);
15776     } else {
15777         // support xtype ctors..
15778         this.tpl = new Roo.factory(this.tpl, Roo);
15779     }
15780     
15781     
15782     this.tpl.compile();
15783     
15784     /** @private */
15785     this.addEvents({
15786         /**
15787          * @event beforeclick
15788          * Fires before a click is processed. Returns false to cancel the default action.
15789          * @param {Roo.View} this
15790          * @param {Number} index The index of the target node
15791          * @param {HTMLElement} node The target node
15792          * @param {Roo.EventObject} e The raw event object
15793          */
15794             "beforeclick" : true,
15795         /**
15796          * @event click
15797          * Fires when a template node is clicked.
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             "click" : true,
15804         /**
15805          * @event dblclick
15806          * Fires when a template node is double 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             "dblclick" : true,
15813         /**
15814          * @event contextmenu
15815          * Fires when a template node is right 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             "contextmenu" : true,
15822         /**
15823          * @event selectionchange
15824          * Fires when the selected nodes change.
15825          * @param {Roo.View} this
15826          * @param {Array} selections Array of the selected nodes
15827          */
15828             "selectionchange" : true,
15829     
15830         /**
15831          * @event beforeselect
15832          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15833          * @param {Roo.View} this
15834          * @param {HTMLElement} node The node to be selected
15835          * @param {Array} selections Array of currently selected nodes
15836          */
15837             "beforeselect" : true,
15838         /**
15839          * @event preparedata
15840          * Fires on every row to render, to allow you to change the data.
15841          * @param {Roo.View} this
15842          * @param {Object} data to be rendered (change this)
15843          */
15844           "preparedata" : true
15845           
15846           
15847         });
15848
15849
15850
15851     this.el.on({
15852         "click": this.onClick,
15853         "dblclick": this.onDblClick,
15854         "contextmenu": this.onContextMenu,
15855         scope:this
15856     });
15857
15858     this.selections = [];
15859     this.nodes = [];
15860     this.cmp = new Roo.CompositeElementLite([]);
15861     if(this.store){
15862         this.store = Roo.factory(this.store, Roo.data);
15863         this.setStore(this.store, true);
15864     }
15865     
15866     if ( this.footer && this.footer.xtype) {
15867            
15868          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15869         
15870         this.footer.dataSource = this.store;
15871         this.footer.container = fctr;
15872         this.footer = Roo.factory(this.footer, Roo);
15873         fctr.insertFirst(this.el);
15874         
15875         // this is a bit insane - as the paging toolbar seems to detach the el..
15876 //        dom.parentNode.parentNode.parentNode
15877          // they get detached?
15878     }
15879     
15880     
15881     Roo.View.superclass.constructor.call(this);
15882     
15883     
15884 };
15885
15886 Roo.extend(Roo.View, Roo.util.Observable, {
15887     
15888      /**
15889      * @cfg {Roo.data.Store} store Data store to load data from.
15890      */
15891     store : false,
15892     
15893     /**
15894      * @cfg {String|Roo.Element} el The container element.
15895      */
15896     el : '',
15897     
15898     /**
15899      * @cfg {String|Roo.Template} tpl The template used by this View 
15900      */
15901     tpl : false,
15902     /**
15903      * @cfg {String} dataName the named area of the template to use as the data area
15904      *                          Works with domtemplates roo-name="name"
15905      */
15906     dataName: false,
15907     /**
15908      * @cfg {String} selectedClass The css class to add to selected nodes
15909      */
15910     selectedClass : "x-view-selected",
15911      /**
15912      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15913      */
15914     emptyText : "",
15915     
15916     /**
15917      * @cfg {String} text to display on mask (default Loading)
15918      */
15919     mask : false,
15920     /**
15921      * @cfg {Boolean} multiSelect Allow multiple selection
15922      */
15923     multiSelect : false,
15924     /**
15925      * @cfg {Boolean} singleSelect Allow single selection
15926      */
15927     singleSelect:  false,
15928     
15929     /**
15930      * @cfg {Boolean} toggleSelect - selecting 
15931      */
15932     toggleSelect : false,
15933     
15934     /**
15935      * @cfg {Boolean} tickable - selecting 
15936      */
15937     tickable : false,
15938     
15939     /**
15940      * Returns the element this view is bound to.
15941      * @return {Roo.Element}
15942      */
15943     getEl : function(){
15944         return this.wrapEl;
15945     },
15946     
15947     
15948
15949     /**
15950      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15951      */
15952     refresh : function(){
15953         //Roo.log('refresh');
15954         var t = this.tpl;
15955         
15956         // if we are using something like 'domtemplate', then
15957         // the what gets used is:
15958         // t.applySubtemplate(NAME, data, wrapping data..)
15959         // the outer template then get' applied with
15960         //     the store 'extra data'
15961         // and the body get's added to the
15962         //      roo-name="data" node?
15963         //      <span class='roo-tpl-{name}'></span> ?????
15964         
15965         
15966         
15967         this.clearSelections();
15968         this.el.update("");
15969         var html = [];
15970         var records = this.store.getRange();
15971         if(records.length < 1) {
15972             
15973             // is this valid??  = should it render a template??
15974             
15975             this.el.update(this.emptyText);
15976             return;
15977         }
15978         var el = this.el;
15979         if (this.dataName) {
15980             this.el.update(t.apply(this.store.meta)); //????
15981             el = this.el.child('.roo-tpl-' + this.dataName);
15982         }
15983         
15984         for(var i = 0, len = records.length; i < len; i++){
15985             var data = this.prepareData(records[i].data, i, records[i]);
15986             this.fireEvent("preparedata", this, data, i, records[i]);
15987             
15988             var d = Roo.apply({}, data);
15989             
15990             if(this.tickable){
15991                 Roo.apply(d, {'roo-id' : Roo.id()});
15992                 
15993                 var _this = this;
15994             
15995                 Roo.each(this.parent.item, function(item){
15996                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15997                         return;
15998                     }
15999                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16000                 });
16001             }
16002             
16003             html[html.length] = Roo.util.Format.trim(
16004                 this.dataName ?
16005                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16006                     t.apply(d)
16007             );
16008         }
16009         
16010         
16011         
16012         el.update(html.join(""));
16013         this.nodes = el.dom.childNodes;
16014         this.updateIndexes(0);
16015     },
16016     
16017
16018     /**
16019      * Function to override to reformat the data that is sent to
16020      * the template for each node.
16021      * DEPRICATED - use the preparedata event handler.
16022      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16023      * a JSON object for an UpdateManager bound view).
16024      */
16025     prepareData : function(data, index, record)
16026     {
16027         this.fireEvent("preparedata", this, data, index, record);
16028         return data;
16029     },
16030
16031     onUpdate : function(ds, record){
16032         // Roo.log('on update');   
16033         this.clearSelections();
16034         var index = this.store.indexOf(record);
16035         var n = this.nodes[index];
16036         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16037         n.parentNode.removeChild(n);
16038         this.updateIndexes(index, index);
16039     },
16040
16041     
16042     
16043 // --------- FIXME     
16044     onAdd : function(ds, records, index)
16045     {
16046         //Roo.log(['on Add', ds, records, index] );        
16047         this.clearSelections();
16048         if(this.nodes.length == 0){
16049             this.refresh();
16050             return;
16051         }
16052         var n = this.nodes[index];
16053         for(var i = 0, len = records.length; i < len; i++){
16054             var d = this.prepareData(records[i].data, i, records[i]);
16055             if(n){
16056                 this.tpl.insertBefore(n, d);
16057             }else{
16058                 
16059                 this.tpl.append(this.el, d);
16060             }
16061         }
16062         this.updateIndexes(index);
16063     },
16064
16065     onRemove : function(ds, record, index){
16066        // Roo.log('onRemove');
16067         this.clearSelections();
16068         var el = this.dataName  ?
16069             this.el.child('.roo-tpl-' + this.dataName) :
16070             this.el; 
16071         
16072         el.dom.removeChild(this.nodes[index]);
16073         this.updateIndexes(index);
16074     },
16075
16076     /**
16077      * Refresh an individual node.
16078      * @param {Number} index
16079      */
16080     refreshNode : function(index){
16081         this.onUpdate(this.store, this.store.getAt(index));
16082     },
16083
16084     updateIndexes : function(startIndex, endIndex){
16085         var ns = this.nodes;
16086         startIndex = startIndex || 0;
16087         endIndex = endIndex || ns.length - 1;
16088         for(var i = startIndex; i <= endIndex; i++){
16089             ns[i].nodeIndex = i;
16090         }
16091     },
16092
16093     /**
16094      * Changes the data store this view uses and refresh the view.
16095      * @param {Store} store
16096      */
16097     setStore : function(store, initial){
16098         if(!initial && this.store){
16099             this.store.un("datachanged", this.refresh);
16100             this.store.un("add", this.onAdd);
16101             this.store.un("remove", this.onRemove);
16102             this.store.un("update", this.onUpdate);
16103             this.store.un("clear", this.refresh);
16104             this.store.un("beforeload", this.onBeforeLoad);
16105             this.store.un("load", this.onLoad);
16106             this.store.un("loadexception", this.onLoad);
16107         }
16108         if(store){
16109           
16110             store.on("datachanged", this.refresh, this);
16111             store.on("add", this.onAdd, this);
16112             store.on("remove", this.onRemove, this);
16113             store.on("update", this.onUpdate, this);
16114             store.on("clear", this.refresh, this);
16115             store.on("beforeload", this.onBeforeLoad, this);
16116             store.on("load", this.onLoad, this);
16117             store.on("loadexception", this.onLoad, this);
16118         }
16119         
16120         if(store){
16121             this.refresh();
16122         }
16123     },
16124     /**
16125      * onbeforeLoad - masks the loading area.
16126      *
16127      */
16128     onBeforeLoad : function(store,opts)
16129     {
16130          //Roo.log('onBeforeLoad');   
16131         if (!opts.add) {
16132             this.el.update("");
16133         }
16134         this.el.mask(this.mask ? this.mask : "Loading" ); 
16135     },
16136     onLoad : function ()
16137     {
16138         this.el.unmask();
16139     },
16140     
16141
16142     /**
16143      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16144      * @param {HTMLElement} node
16145      * @return {HTMLElement} The template node
16146      */
16147     findItemFromChild : function(node){
16148         var el = this.dataName  ?
16149             this.el.child('.roo-tpl-' + this.dataName,true) :
16150             this.el.dom; 
16151         
16152         if(!node || node.parentNode == el){
16153                     return node;
16154             }
16155             var p = node.parentNode;
16156             while(p && p != el){
16157             if(p.parentNode == el){
16158                 return p;
16159             }
16160             p = p.parentNode;
16161         }
16162             return null;
16163     },
16164
16165     /** @ignore */
16166     onClick : function(e){
16167         var item = this.findItemFromChild(e.getTarget());
16168         if(item){
16169             var index = this.indexOf(item);
16170             if(this.onItemClick(item, index, e) !== false){
16171                 this.fireEvent("click", this, index, item, e);
16172             }
16173         }else{
16174             this.clearSelections();
16175         }
16176     },
16177
16178     /** @ignore */
16179     onContextMenu : function(e){
16180         var item = this.findItemFromChild(e.getTarget());
16181         if(item){
16182             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16183         }
16184     },
16185
16186     /** @ignore */
16187     onDblClick : function(e){
16188         var item = this.findItemFromChild(e.getTarget());
16189         if(item){
16190             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16191         }
16192     },
16193
16194     onItemClick : function(item, index, e)
16195     {
16196         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16197             return false;
16198         }
16199         if (this.toggleSelect) {
16200             var m = this.isSelected(item) ? 'unselect' : 'select';
16201             //Roo.log(m);
16202             var _t = this;
16203             _t[m](item, true, false);
16204             return true;
16205         }
16206         if(this.multiSelect || this.singleSelect){
16207             if(this.multiSelect && e.shiftKey && this.lastSelection){
16208                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16209             }else{
16210                 this.select(item, this.multiSelect && e.ctrlKey);
16211                 this.lastSelection = item;
16212             }
16213             
16214             if(!this.tickable){
16215                 e.preventDefault();
16216             }
16217             
16218         }
16219         return true;
16220     },
16221
16222     /**
16223      * Get the number of selected nodes.
16224      * @return {Number}
16225      */
16226     getSelectionCount : function(){
16227         return this.selections.length;
16228     },
16229
16230     /**
16231      * Get the currently selected nodes.
16232      * @return {Array} An array of HTMLElements
16233      */
16234     getSelectedNodes : function(){
16235         return this.selections;
16236     },
16237
16238     /**
16239      * Get the indexes of the selected nodes.
16240      * @return {Array}
16241      */
16242     getSelectedIndexes : function(){
16243         var indexes = [], s = this.selections;
16244         for(var i = 0, len = s.length; i < len; i++){
16245             indexes.push(s[i].nodeIndex);
16246         }
16247         return indexes;
16248     },
16249
16250     /**
16251      * Clear all selections
16252      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16253      */
16254     clearSelections : function(suppressEvent){
16255         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16256             this.cmp.elements = this.selections;
16257             this.cmp.removeClass(this.selectedClass);
16258             this.selections = [];
16259             if(!suppressEvent){
16260                 this.fireEvent("selectionchange", this, this.selections);
16261             }
16262         }
16263     },
16264
16265     /**
16266      * Returns true if the passed node is selected
16267      * @param {HTMLElement/Number} node The node or node index
16268      * @return {Boolean}
16269      */
16270     isSelected : function(node){
16271         var s = this.selections;
16272         if(s.length < 1){
16273             return false;
16274         }
16275         node = this.getNode(node);
16276         return s.indexOf(node) !== -1;
16277     },
16278
16279     /**
16280      * Selects nodes.
16281      * @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
16282      * @param {Boolean} keepExisting (optional) true to keep existing selections
16283      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16284      */
16285     select : function(nodeInfo, keepExisting, suppressEvent){
16286         if(nodeInfo instanceof Array){
16287             if(!keepExisting){
16288                 this.clearSelections(true);
16289             }
16290             for(var i = 0, len = nodeInfo.length; i < len; i++){
16291                 this.select(nodeInfo[i], true, true);
16292             }
16293             return;
16294         } 
16295         var node = this.getNode(nodeInfo);
16296         if(!node || this.isSelected(node)){
16297             return; // already selected.
16298         }
16299         if(!keepExisting){
16300             this.clearSelections(true);
16301         }
16302         
16303         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16304             Roo.fly(node).addClass(this.selectedClass);
16305             this.selections.push(node);
16306             if(!suppressEvent){
16307                 this.fireEvent("selectionchange", this, this.selections);
16308             }
16309         }
16310         
16311         
16312     },
16313       /**
16314      * Unselects nodes.
16315      * @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
16316      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16317      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16318      */
16319     unselect : function(nodeInfo, keepExisting, suppressEvent)
16320     {
16321         if(nodeInfo instanceof Array){
16322             Roo.each(this.selections, function(s) {
16323                 this.unselect(s, nodeInfo);
16324             }, this);
16325             return;
16326         }
16327         var node = this.getNode(nodeInfo);
16328         if(!node || !this.isSelected(node)){
16329             //Roo.log("not selected");
16330             return; // not selected.
16331         }
16332         // fireevent???
16333         var ns = [];
16334         Roo.each(this.selections, function(s) {
16335             if (s == node ) {
16336                 Roo.fly(node).removeClass(this.selectedClass);
16337
16338                 return;
16339             }
16340             ns.push(s);
16341         },this);
16342         
16343         this.selections= ns;
16344         this.fireEvent("selectionchange", this, this.selections);
16345     },
16346
16347     /**
16348      * Gets a template node.
16349      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16350      * @return {HTMLElement} The node or null if it wasn't found
16351      */
16352     getNode : function(nodeInfo){
16353         if(typeof nodeInfo == "string"){
16354             return document.getElementById(nodeInfo);
16355         }else if(typeof nodeInfo == "number"){
16356             return this.nodes[nodeInfo];
16357         }
16358         return nodeInfo;
16359     },
16360
16361     /**
16362      * Gets a range template nodes.
16363      * @param {Number} startIndex
16364      * @param {Number} endIndex
16365      * @return {Array} An array of nodes
16366      */
16367     getNodes : function(start, end){
16368         var ns = this.nodes;
16369         start = start || 0;
16370         end = typeof end == "undefined" ? ns.length - 1 : end;
16371         var nodes = [];
16372         if(start <= end){
16373             for(var i = start; i <= end; i++){
16374                 nodes.push(ns[i]);
16375             }
16376         } else{
16377             for(var i = start; i >= end; i--){
16378                 nodes.push(ns[i]);
16379             }
16380         }
16381         return nodes;
16382     },
16383
16384     /**
16385      * Finds the index of the passed node
16386      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16387      * @return {Number} The index of the node or -1
16388      */
16389     indexOf : function(node){
16390         node = this.getNode(node);
16391         if(typeof node.nodeIndex == "number"){
16392             return node.nodeIndex;
16393         }
16394         var ns = this.nodes;
16395         for(var i = 0, len = ns.length; i < len; i++){
16396             if(ns[i] == node){
16397                 return i;
16398             }
16399         }
16400         return -1;
16401     }
16402 });
16403 /*
16404  * - LGPL
16405  *
16406  * based on jquery fullcalendar
16407  * 
16408  */
16409
16410 Roo.bootstrap = Roo.bootstrap || {};
16411 /**
16412  * @class Roo.bootstrap.Calendar
16413  * @extends Roo.bootstrap.Component
16414  * Bootstrap Calendar class
16415  * @cfg {Boolean} loadMask (true|false) default false
16416  * @cfg {Object} header generate the user specific header of the calendar, default false
16417
16418  * @constructor
16419  * Create a new Container
16420  * @param {Object} config The config object
16421  */
16422
16423
16424
16425 Roo.bootstrap.Calendar = function(config){
16426     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16427      this.addEvents({
16428         /**
16429              * @event select
16430              * Fires when a date is selected
16431              * @param {DatePicker} this
16432              * @param {Date} date The selected date
16433              */
16434         'select': true,
16435         /**
16436              * @event monthchange
16437              * Fires when the displayed month changes 
16438              * @param {DatePicker} this
16439              * @param {Date} date The selected month
16440              */
16441         'monthchange': true,
16442         /**
16443              * @event evententer
16444              * Fires when mouse over an event
16445              * @param {Calendar} this
16446              * @param {event} Event
16447              */
16448         'evententer': true,
16449         /**
16450              * @event eventleave
16451              * Fires when the mouse leaves an
16452              * @param {Calendar} this
16453              * @param {event}
16454              */
16455         'eventleave': true,
16456         /**
16457              * @event eventclick
16458              * Fires when the mouse click an
16459              * @param {Calendar} this
16460              * @param {event}
16461              */
16462         'eventclick': true
16463         
16464     });
16465
16466 };
16467
16468 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16469     
16470      /**
16471      * @cfg {Number} startDay
16472      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16473      */
16474     startDay : 0,
16475     
16476     loadMask : false,
16477     
16478     header : false,
16479       
16480     getAutoCreate : function(){
16481         
16482         
16483         var fc_button = function(name, corner, style, content ) {
16484             return Roo.apply({},{
16485                 tag : 'span',
16486                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16487                          (corner.length ?
16488                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16489                             ''
16490                         ),
16491                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16492                 unselectable: 'on'
16493             });
16494         };
16495         
16496         var header = {};
16497         
16498         if(!this.header){
16499             header = {
16500                 tag : 'table',
16501                 cls : 'fc-header',
16502                 style : 'width:100%',
16503                 cn : [
16504                     {
16505                         tag: 'tr',
16506                         cn : [
16507                             {
16508                                 tag : 'td',
16509                                 cls : 'fc-header-left',
16510                                 cn : [
16511                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16512                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16513                                     { tag: 'span', cls: 'fc-header-space' },
16514                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16515
16516
16517                                 ]
16518                             },
16519
16520                             {
16521                                 tag : 'td',
16522                                 cls : 'fc-header-center',
16523                                 cn : [
16524                                     {
16525                                         tag: 'span',
16526                                         cls: 'fc-header-title',
16527                                         cn : {
16528                                             tag: 'H2',
16529                                             html : 'month / year'
16530                                         }
16531                                     }
16532
16533                                 ]
16534                             },
16535                             {
16536                                 tag : 'td',
16537                                 cls : 'fc-header-right',
16538                                 cn : [
16539                               /*      fc_button('month', 'left', '', 'month' ),
16540                                     fc_button('week', '', '', 'week' ),
16541                                     fc_button('day', 'right', '', 'day' )
16542                                 */    
16543
16544                                 ]
16545                             }
16546
16547                         ]
16548                     }
16549                 ]
16550             };
16551         }
16552         
16553         header = this.header;
16554         
16555        
16556         var cal_heads = function() {
16557             var ret = [];
16558             // fixme - handle this.
16559             
16560             for (var i =0; i < Date.dayNames.length; i++) {
16561                 var d = Date.dayNames[i];
16562                 ret.push({
16563                     tag: 'th',
16564                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16565                     html : d.substring(0,3)
16566                 });
16567                 
16568             }
16569             ret[0].cls += ' fc-first';
16570             ret[6].cls += ' fc-last';
16571             return ret;
16572         };
16573         var cal_cell = function(n) {
16574             return  {
16575                 tag: 'td',
16576                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16577                 cn : [
16578                     {
16579                         cn : [
16580                             {
16581                                 cls: 'fc-day-number',
16582                                 html: 'D'
16583                             },
16584                             {
16585                                 cls: 'fc-day-content',
16586                              
16587                                 cn : [
16588                                      {
16589                                         style: 'position: relative;' // height: 17px;
16590                                     }
16591                                 ]
16592                             }
16593                             
16594                             
16595                         ]
16596                     }
16597                 ]
16598                 
16599             }
16600         };
16601         var cal_rows = function() {
16602             
16603             var ret = [];
16604             for (var r = 0; r < 6; r++) {
16605                 var row= {
16606                     tag : 'tr',
16607                     cls : 'fc-week',
16608                     cn : []
16609                 };
16610                 
16611                 for (var i =0; i < Date.dayNames.length; i++) {
16612                     var d = Date.dayNames[i];
16613                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16614
16615                 }
16616                 row.cn[0].cls+=' fc-first';
16617                 row.cn[0].cn[0].style = 'min-height:90px';
16618                 row.cn[6].cls+=' fc-last';
16619                 ret.push(row);
16620                 
16621             }
16622             ret[0].cls += ' fc-first';
16623             ret[4].cls += ' fc-prev-last';
16624             ret[5].cls += ' fc-last';
16625             return ret;
16626             
16627         };
16628         
16629         var cal_table = {
16630             tag: 'table',
16631             cls: 'fc-border-separate',
16632             style : 'width:100%',
16633             cellspacing  : 0,
16634             cn : [
16635                 { 
16636                     tag: 'thead',
16637                     cn : [
16638                         { 
16639                             tag: 'tr',
16640                             cls : 'fc-first fc-last',
16641                             cn : cal_heads()
16642                         }
16643                     ]
16644                 },
16645                 { 
16646                     tag: 'tbody',
16647                     cn : cal_rows()
16648                 }
16649                   
16650             ]
16651         };
16652          
16653          var cfg = {
16654             cls : 'fc fc-ltr',
16655             cn : [
16656                 header,
16657                 {
16658                     cls : 'fc-content',
16659                     style : "position: relative;",
16660                     cn : [
16661                         {
16662                             cls : 'fc-view fc-view-month fc-grid',
16663                             style : 'position: relative',
16664                             unselectable : 'on',
16665                             cn : [
16666                                 {
16667                                     cls : 'fc-event-container',
16668                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16669                                 },
16670                                 cal_table
16671                             ]
16672                         }
16673                     ]
16674     
16675                 }
16676            ] 
16677             
16678         };
16679         
16680          
16681         
16682         return cfg;
16683     },
16684     
16685     
16686     initEvents : function()
16687     {
16688         if(!this.store){
16689             throw "can not find store for calendar";
16690         }
16691         
16692         var mark = {
16693             tag: "div",
16694             cls:"x-dlg-mask",
16695             style: "text-align:center",
16696             cn: [
16697                 {
16698                     tag: "div",
16699                     style: "background-color:white;width:50%;margin:250 auto",
16700                     cn: [
16701                         {
16702                             tag: "img",
16703                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16704                         },
16705                         {
16706                             tag: "span",
16707                             html: "Loading"
16708                         }
16709                         
16710                     ]
16711                 }
16712             ]
16713         };
16714         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16715         
16716         var size = this.el.select('.fc-content', true).first().getSize();
16717         this.maskEl.setSize(size.width, size.height);
16718         this.maskEl.enableDisplayMode("block");
16719         if(!this.loadMask){
16720             this.maskEl.hide();
16721         }
16722         
16723         this.store = Roo.factory(this.store, Roo.data);
16724         this.store.on('load', this.onLoad, this);
16725         this.store.on('beforeload', this.onBeforeLoad, this);
16726         
16727         this.resize();
16728         
16729         this.cells = this.el.select('.fc-day',true);
16730         //Roo.log(this.cells);
16731         this.textNodes = this.el.query('.fc-day-number');
16732         this.cells.addClassOnOver('fc-state-hover');
16733         
16734         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16735         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16736         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16737         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16738         
16739         this.on('monthchange', this.onMonthChange, this);
16740         
16741         this.update(new Date().clearTime());
16742     },
16743     
16744     resize : function() {
16745         var sz  = this.el.getSize();
16746         
16747         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16748         this.el.select('.fc-day-content div',true).setHeight(34);
16749     },
16750     
16751     
16752     // private
16753     showPrevMonth : function(e){
16754         this.update(this.activeDate.add("mo", -1));
16755     },
16756     showToday : function(e){
16757         this.update(new Date().clearTime());
16758     },
16759     // private
16760     showNextMonth : function(e){
16761         this.update(this.activeDate.add("mo", 1));
16762     },
16763
16764     // private
16765     showPrevYear : function(){
16766         this.update(this.activeDate.add("y", -1));
16767     },
16768
16769     // private
16770     showNextYear : function(){
16771         this.update(this.activeDate.add("y", 1));
16772     },
16773
16774     
16775    // private
16776     update : function(date)
16777     {
16778         var vd = this.activeDate;
16779         this.activeDate = date;
16780 //        if(vd && this.el){
16781 //            var t = date.getTime();
16782 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16783 //                Roo.log('using add remove');
16784 //                
16785 //                this.fireEvent('monthchange', this, date);
16786 //                
16787 //                this.cells.removeClass("fc-state-highlight");
16788 //                this.cells.each(function(c){
16789 //                   if(c.dateValue == t){
16790 //                       c.addClass("fc-state-highlight");
16791 //                       setTimeout(function(){
16792 //                            try{c.dom.firstChild.focus();}catch(e){}
16793 //                       }, 50);
16794 //                       return false;
16795 //                   }
16796 //                   return true;
16797 //                });
16798 //                return;
16799 //            }
16800 //        }
16801         
16802         var days = date.getDaysInMonth();
16803         
16804         var firstOfMonth = date.getFirstDateOfMonth();
16805         var startingPos = firstOfMonth.getDay()-this.startDay;
16806         
16807         if(startingPos < this.startDay){
16808             startingPos += 7;
16809         }
16810         
16811         var pm = date.add(Date.MONTH, -1);
16812         var prevStart = pm.getDaysInMonth()-startingPos;
16813 //        
16814         this.cells = this.el.select('.fc-day',true);
16815         this.textNodes = this.el.query('.fc-day-number');
16816         this.cells.addClassOnOver('fc-state-hover');
16817         
16818         var cells = this.cells.elements;
16819         var textEls = this.textNodes;
16820         
16821         Roo.each(cells, function(cell){
16822             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16823         });
16824         
16825         days += startingPos;
16826
16827         // convert everything to numbers so it's fast
16828         var day = 86400000;
16829         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16830         //Roo.log(d);
16831         //Roo.log(pm);
16832         //Roo.log(prevStart);
16833         
16834         var today = new Date().clearTime().getTime();
16835         var sel = date.clearTime().getTime();
16836         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16837         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16838         var ddMatch = this.disabledDatesRE;
16839         var ddText = this.disabledDatesText;
16840         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16841         var ddaysText = this.disabledDaysText;
16842         var format = this.format;
16843         
16844         var setCellClass = function(cal, cell){
16845             cell.row = 0;
16846             cell.events = [];
16847             cell.more = [];
16848             //Roo.log('set Cell Class');
16849             cell.title = "";
16850             var t = d.getTime();
16851             
16852             //Roo.log(d);
16853             
16854             cell.dateValue = t;
16855             if(t == today){
16856                 cell.className += " fc-today";
16857                 cell.className += " fc-state-highlight";
16858                 cell.title = cal.todayText;
16859             }
16860             if(t == sel){
16861                 // disable highlight in other month..
16862                 //cell.className += " fc-state-highlight";
16863                 
16864             }
16865             // disabling
16866             if(t < min) {
16867                 cell.className = " fc-state-disabled";
16868                 cell.title = cal.minText;
16869                 return;
16870             }
16871             if(t > max) {
16872                 cell.className = " fc-state-disabled";
16873                 cell.title = cal.maxText;
16874                 return;
16875             }
16876             if(ddays){
16877                 if(ddays.indexOf(d.getDay()) != -1){
16878                     cell.title = ddaysText;
16879                     cell.className = " fc-state-disabled";
16880                 }
16881             }
16882             if(ddMatch && format){
16883                 var fvalue = d.dateFormat(format);
16884                 if(ddMatch.test(fvalue)){
16885                     cell.title = ddText.replace("%0", fvalue);
16886                     cell.className = " fc-state-disabled";
16887                 }
16888             }
16889             
16890             if (!cell.initialClassName) {
16891                 cell.initialClassName = cell.dom.className;
16892             }
16893             
16894             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16895         };
16896
16897         var i = 0;
16898         
16899         for(; i < startingPos; i++) {
16900             textEls[i].innerHTML = (++prevStart);
16901             d.setDate(d.getDate()+1);
16902             
16903             cells[i].className = "fc-past fc-other-month";
16904             setCellClass(this, cells[i]);
16905         }
16906         
16907         var intDay = 0;
16908         
16909         for(; i < days; i++){
16910             intDay = i - startingPos + 1;
16911             textEls[i].innerHTML = (intDay);
16912             d.setDate(d.getDate()+1);
16913             
16914             cells[i].className = ''; // "x-date-active";
16915             setCellClass(this, cells[i]);
16916         }
16917         var extraDays = 0;
16918         
16919         for(; i < 42; i++) {
16920             textEls[i].innerHTML = (++extraDays);
16921             d.setDate(d.getDate()+1);
16922             
16923             cells[i].className = "fc-future fc-other-month";
16924             setCellClass(this, cells[i]);
16925         }
16926         
16927         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16928         
16929         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16930         
16931         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16932         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16933         
16934         if(totalRows != 6){
16935             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16936             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16937         }
16938         
16939         this.fireEvent('monthchange', this, date);
16940         
16941         
16942         /*
16943         if(!this.internalRender){
16944             var main = this.el.dom.firstChild;
16945             var w = main.offsetWidth;
16946             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16947             Roo.fly(main).setWidth(w);
16948             this.internalRender = true;
16949             // opera does not respect the auto grow header center column
16950             // then, after it gets a width opera refuses to recalculate
16951             // without a second pass
16952             if(Roo.isOpera && !this.secondPass){
16953                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16954                 this.secondPass = true;
16955                 this.update.defer(10, this, [date]);
16956             }
16957         }
16958         */
16959         
16960     },
16961     
16962     findCell : function(dt) {
16963         dt = dt.clearTime().getTime();
16964         var ret = false;
16965         this.cells.each(function(c){
16966             //Roo.log("check " +c.dateValue + '?=' + dt);
16967             if(c.dateValue == dt){
16968                 ret = c;
16969                 return false;
16970             }
16971             return true;
16972         });
16973         
16974         return ret;
16975     },
16976     
16977     findCells : function(ev) {
16978         var s = ev.start.clone().clearTime().getTime();
16979        // Roo.log(s);
16980         var e= ev.end.clone().clearTime().getTime();
16981        // Roo.log(e);
16982         var ret = [];
16983         this.cells.each(function(c){
16984              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16985             
16986             if(c.dateValue > e){
16987                 return ;
16988             }
16989             if(c.dateValue < s){
16990                 return ;
16991             }
16992             ret.push(c);
16993         });
16994         
16995         return ret;    
16996     },
16997     
16998 //    findBestRow: function(cells)
16999 //    {
17000 //        var ret = 0;
17001 //        
17002 //        for (var i =0 ; i < cells.length;i++) {
17003 //            ret  = Math.max(cells[i].rows || 0,ret);
17004 //        }
17005 //        return ret;
17006 //        
17007 //    },
17008     
17009     
17010     addItem : function(ev)
17011     {
17012         // look for vertical location slot in
17013         var cells = this.findCells(ev);
17014         
17015 //        ev.row = this.findBestRow(cells);
17016         
17017         // work out the location.
17018         
17019         var crow = false;
17020         var rows = [];
17021         for(var i =0; i < cells.length; i++) {
17022             
17023             cells[i].row = cells[0].row;
17024             
17025             if(i == 0){
17026                 cells[i].row = cells[i].row + 1;
17027             }
17028             
17029             if (!crow) {
17030                 crow = {
17031                     start : cells[i],
17032                     end :  cells[i]
17033                 };
17034                 continue;
17035             }
17036             if (crow.start.getY() == cells[i].getY()) {
17037                 // on same row.
17038                 crow.end = cells[i];
17039                 continue;
17040             }
17041             // different row.
17042             rows.push(crow);
17043             crow = {
17044                 start: cells[i],
17045                 end : cells[i]
17046             };
17047             
17048         }
17049         
17050         rows.push(crow);
17051         ev.els = [];
17052         ev.rows = rows;
17053         ev.cells = cells;
17054         
17055         cells[0].events.push(ev);
17056         
17057         this.calevents.push(ev);
17058     },
17059     
17060     clearEvents: function() {
17061         
17062         if(!this.calevents){
17063             return;
17064         }
17065         
17066         Roo.each(this.cells.elements, function(c){
17067             c.row = 0;
17068             c.events = [];
17069             c.more = [];
17070         });
17071         
17072         Roo.each(this.calevents, function(e) {
17073             Roo.each(e.els, function(el) {
17074                 el.un('mouseenter' ,this.onEventEnter, this);
17075                 el.un('mouseleave' ,this.onEventLeave, this);
17076                 el.remove();
17077             },this);
17078         },this);
17079         
17080         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17081             e.remove();
17082         });
17083         
17084     },
17085     
17086     renderEvents: function()
17087     {   
17088         var _this = this;
17089         
17090         this.cells.each(function(c) {
17091             
17092             if(c.row < 5){
17093                 return;
17094             }
17095             
17096             var ev = c.events;
17097             
17098             var r = 4;
17099             if(c.row != c.events.length){
17100                 r = 4 - (4 - (c.row - c.events.length));
17101             }
17102             
17103             c.events = ev.slice(0, r);
17104             c.more = ev.slice(r);
17105             
17106             if(c.more.length && c.more.length == 1){
17107                 c.events.push(c.more.pop());
17108             }
17109             
17110             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17111             
17112         });
17113             
17114         this.cells.each(function(c) {
17115             
17116             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17117             
17118             
17119             for (var e = 0; e < c.events.length; e++){
17120                 var ev = c.events[e];
17121                 var rows = ev.rows;
17122                 
17123                 for(var i = 0; i < rows.length; i++) {
17124                 
17125                     // how many rows should it span..
17126
17127                     var  cfg = {
17128                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17129                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17130
17131                         unselectable : "on",
17132                         cn : [
17133                             {
17134                                 cls: 'fc-event-inner',
17135                                 cn : [
17136     //                                {
17137     //                                  tag:'span',
17138     //                                  cls: 'fc-event-time',
17139     //                                  html : cells.length > 1 ? '' : ev.time
17140     //                                },
17141                                     {
17142                                       tag:'span',
17143                                       cls: 'fc-event-title',
17144                                       html : String.format('{0}', ev.title)
17145                                     }
17146
17147
17148                                 ]
17149                             },
17150                             {
17151                                 cls: 'ui-resizable-handle ui-resizable-e',
17152                                 html : '&nbsp;&nbsp;&nbsp'
17153                             }
17154
17155                         ]
17156                     };
17157
17158                     if (i == 0) {
17159                         cfg.cls += ' fc-event-start';
17160                     }
17161                     if ((i+1) == rows.length) {
17162                         cfg.cls += ' fc-event-end';
17163                     }
17164
17165                     var ctr = _this.el.select('.fc-event-container',true).first();
17166                     var cg = ctr.createChild(cfg);
17167
17168                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17169                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17170
17171                     var r = (c.more.length) ? 1 : 0;
17172                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17173                     cg.setWidth(ebox.right - sbox.x -2);
17174
17175                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17176                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17177                     cg.on('click', _this.onEventClick, _this, ev);
17178
17179                     ev.els.push(cg);
17180                     
17181                 }
17182                 
17183             }
17184             
17185             
17186             if(c.more.length){
17187                 var  cfg = {
17188                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17189                     style : 'position: absolute',
17190                     unselectable : "on",
17191                     cn : [
17192                         {
17193                             cls: 'fc-event-inner',
17194                             cn : [
17195                                 {
17196                                   tag:'span',
17197                                   cls: 'fc-event-title',
17198                                   html : 'More'
17199                                 }
17200
17201
17202                             ]
17203                         },
17204                         {
17205                             cls: 'ui-resizable-handle ui-resizable-e',
17206                             html : '&nbsp;&nbsp;&nbsp'
17207                         }
17208
17209                     ]
17210                 };
17211
17212                 var ctr = _this.el.select('.fc-event-container',true).first();
17213                 var cg = ctr.createChild(cfg);
17214
17215                 var sbox = c.select('.fc-day-content',true).first().getBox();
17216                 var ebox = c.select('.fc-day-content',true).first().getBox();
17217                 //Roo.log(cg);
17218                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17219                 cg.setWidth(ebox.right - sbox.x -2);
17220
17221                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17222                 
17223             }
17224             
17225         });
17226         
17227         
17228         
17229     },
17230     
17231     onEventEnter: function (e, el,event,d) {
17232         this.fireEvent('evententer', this, el, event);
17233     },
17234     
17235     onEventLeave: function (e, el,event,d) {
17236         this.fireEvent('eventleave', this, el, event);
17237     },
17238     
17239     onEventClick: function (e, el,event,d) {
17240         this.fireEvent('eventclick', this, el, event);
17241     },
17242     
17243     onMonthChange: function () {
17244         this.store.load();
17245     },
17246     
17247     onMoreEventClick: function(e, el, more)
17248     {
17249         var _this = this;
17250         
17251         this.calpopover.placement = 'right';
17252         this.calpopover.setTitle('More');
17253         
17254         this.calpopover.setContent('');
17255         
17256         var ctr = this.calpopover.el.select('.popover-content', true).first();
17257         
17258         Roo.each(more, function(m){
17259             var cfg = {
17260                 cls : 'fc-event-hori fc-event-draggable',
17261                 html : m.title
17262             };
17263             var cg = ctr.createChild(cfg);
17264             
17265             cg.on('click', _this.onEventClick, _this, m);
17266         });
17267         
17268         this.calpopover.show(el);
17269         
17270         
17271     },
17272     
17273     onLoad: function () 
17274     {   
17275         this.calevents = [];
17276         var cal = this;
17277         
17278         if(this.store.getCount() > 0){
17279             this.store.data.each(function(d){
17280                cal.addItem({
17281                     id : d.data.id,
17282                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17283                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17284                     time : d.data.start_time,
17285                     title : d.data.title,
17286                     description : d.data.description,
17287                     venue : d.data.venue
17288                 });
17289             });
17290         }
17291         
17292         this.renderEvents();
17293         
17294         if(this.calevents.length && this.loadMask){
17295             this.maskEl.hide();
17296         }
17297     },
17298     
17299     onBeforeLoad: function()
17300     {
17301         this.clearEvents();
17302         if(this.loadMask){
17303             this.maskEl.show();
17304         }
17305     }
17306 });
17307
17308  
17309  /*
17310  * - LGPL
17311  *
17312  * element
17313  * 
17314  */
17315
17316 /**
17317  * @class Roo.bootstrap.Popover
17318  * @extends Roo.bootstrap.Component
17319  * Bootstrap Popover class
17320  * @cfg {String} html contents of the popover   (or false to use children..)
17321  * @cfg {String} title of popover (or false to hide)
17322  * @cfg {String} placement how it is placed
17323  * @cfg {String} trigger click || hover (or false to trigger manually)
17324  * @cfg {String} over what (parent or false to trigger manually.)
17325  * @cfg {Number} delay - delay before showing
17326  
17327  * @constructor
17328  * Create a new Popover
17329  * @param {Object} config The config object
17330  */
17331
17332 Roo.bootstrap.Popover = function(config){
17333     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17334     
17335     this.addEvents({
17336         // raw events
17337          /**
17338          * @event show
17339          * After the popover show
17340          * 
17341          * @param {Roo.bootstrap.Popover} this
17342          */
17343         "show" : true,
17344         /**
17345          * @event hide
17346          * After the popover hide
17347          * 
17348          * @param {Roo.bootstrap.Popover} this
17349          */
17350         "hide" : true
17351     });
17352 };
17353
17354 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17355     
17356     title: 'Fill in a title',
17357     html: false,
17358     
17359     placement : 'right',
17360     trigger : 'hover', // hover
17361     
17362     delay : 0,
17363     
17364     over: 'parent',
17365     
17366     can_build_overlaid : false,
17367     
17368     getChildContainer : function()
17369     {
17370         return this.el.select('.popover-content',true).first();
17371     },
17372     
17373     getAutoCreate : function(){
17374          
17375         var cfg = {
17376            cls : 'popover roo-dynamic',
17377            style: 'display:block',
17378            cn : [
17379                 {
17380                     cls : 'arrow'
17381                 },
17382                 {
17383                     cls : 'popover-inner',
17384                     cn : [
17385                         {
17386                             tag: 'h3',
17387                             cls: 'popover-title',
17388                             html : this.title
17389                         },
17390                         {
17391                             cls : 'popover-content',
17392                             html : this.html
17393                         }
17394                     ]
17395                     
17396                 }
17397            ]
17398         };
17399         
17400         return cfg;
17401     },
17402     setTitle: function(str)
17403     {
17404         this.title = str;
17405         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17406     },
17407     setContent: function(str)
17408     {
17409         this.html = str;
17410         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17411     },
17412     // as it get's added to the bottom of the page.
17413     onRender : function(ct, position)
17414     {
17415         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17416         if(!this.el){
17417             var cfg = Roo.apply({},  this.getAutoCreate());
17418             cfg.id = Roo.id();
17419             
17420             if (this.cls) {
17421                 cfg.cls += ' ' + this.cls;
17422             }
17423             if (this.style) {
17424                 cfg.style = this.style;
17425             }
17426             //Roo.log("adding to ");
17427             this.el = Roo.get(document.body).createChild(cfg, position);
17428 //            Roo.log(this.el);
17429         }
17430         this.initEvents();
17431     },
17432     
17433     initEvents : function()
17434     {
17435         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17436         this.el.enableDisplayMode('block');
17437         this.el.hide();
17438         if (this.over === false) {
17439             return; 
17440         }
17441         if (this.triggers === false) {
17442             return;
17443         }
17444         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17445         var triggers = this.trigger ? this.trigger.split(' ') : [];
17446         Roo.each(triggers, function(trigger) {
17447         
17448             if (trigger == 'click') {
17449                 on_el.on('click', this.toggle, this);
17450             } else if (trigger != 'manual') {
17451                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17452                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17453       
17454                 on_el.on(eventIn  ,this.enter, this);
17455                 on_el.on(eventOut, this.leave, this);
17456             }
17457         }, this);
17458         
17459     },
17460     
17461     
17462     // private
17463     timeout : null,
17464     hoverState : null,
17465     
17466     toggle : function () {
17467         this.hoverState == 'in' ? this.leave() : this.enter();
17468     },
17469     
17470     enter : function () {
17471         
17472         clearTimeout(this.timeout);
17473     
17474         this.hoverState = 'in';
17475     
17476         if (!this.delay || !this.delay.show) {
17477             this.show();
17478             return;
17479         }
17480         var _t = this;
17481         this.timeout = setTimeout(function () {
17482             if (_t.hoverState == 'in') {
17483                 _t.show();
17484             }
17485         }, this.delay.show)
17486     },
17487     
17488     leave : function() {
17489         clearTimeout(this.timeout);
17490     
17491         this.hoverState = 'out';
17492     
17493         if (!this.delay || !this.delay.hide) {
17494             this.hide();
17495             return;
17496         }
17497         var _t = this;
17498         this.timeout = setTimeout(function () {
17499             if (_t.hoverState == 'out') {
17500                 _t.hide();
17501             }
17502         }, this.delay.hide)
17503     },
17504     
17505     show : function (on_el)
17506     {
17507         if (!on_el) {
17508             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17509         }
17510         
17511         // set content.
17512         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17513         if (this.html !== false) {
17514             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17515         }
17516         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17517         if (!this.title.length) {
17518             this.el.select('.popover-title',true).hide();
17519         }
17520         
17521         var placement = typeof this.placement == 'function' ?
17522             this.placement.call(this, this.el, on_el) :
17523             this.placement;
17524             
17525         var autoToken = /\s?auto?\s?/i;
17526         var autoPlace = autoToken.test(placement);
17527         if (autoPlace) {
17528             placement = placement.replace(autoToken, '') || 'top';
17529         }
17530         
17531         //this.el.detach()
17532         //this.el.setXY([0,0]);
17533         this.el.show();
17534         this.el.dom.style.display='block';
17535         this.el.addClass(placement);
17536         
17537         //this.el.appendTo(on_el);
17538         
17539         var p = this.getPosition();
17540         var box = this.el.getBox();
17541         
17542         if (autoPlace) {
17543             // fixme..
17544         }
17545         var align = Roo.bootstrap.Popover.alignment[placement];
17546         
17547 //        Roo.log(align);
17548         this.el.alignTo(on_el, align[0],align[1]);
17549         //var arrow = this.el.select('.arrow',true).first();
17550         //arrow.set(align[2], 
17551         
17552         this.el.addClass('in');
17553         
17554         
17555         if (this.el.hasClass('fade')) {
17556             // fade it?
17557         }
17558         
17559         this.hoverState = 'in';
17560         
17561         this.fireEvent('show', this);
17562         
17563     },
17564     hide : function()
17565     {
17566         this.el.setXY([0,0]);
17567         this.el.removeClass('in');
17568         this.el.hide();
17569         this.hoverState = null;
17570         
17571         this.fireEvent('hide', this);
17572     }
17573     
17574 });
17575
17576 Roo.bootstrap.Popover.alignment = {
17577     'left' : ['r-l', [-10,0], 'right'],
17578     'right' : ['l-r', [10,0], 'left'],
17579     'bottom' : ['t-b', [0,10], 'top'],
17580     'top' : [ 'b-t', [0,-10], 'bottom']
17581 };
17582
17583  /*
17584  * - LGPL
17585  *
17586  * Progress
17587  * 
17588  */
17589
17590 /**
17591  * @class Roo.bootstrap.Progress
17592  * @extends Roo.bootstrap.Component
17593  * Bootstrap Progress class
17594  * @cfg {Boolean} striped striped of the progress bar
17595  * @cfg {Boolean} active animated of the progress bar
17596  * 
17597  * 
17598  * @constructor
17599  * Create a new Progress
17600  * @param {Object} config The config object
17601  */
17602
17603 Roo.bootstrap.Progress = function(config){
17604     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17605 };
17606
17607 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17608     
17609     striped : false,
17610     active: false,
17611     
17612     getAutoCreate : function(){
17613         var cfg = {
17614             tag: 'div',
17615             cls: 'progress'
17616         };
17617         
17618         
17619         if(this.striped){
17620             cfg.cls += ' progress-striped';
17621         }
17622       
17623         if(this.active){
17624             cfg.cls += ' active';
17625         }
17626         
17627         
17628         return cfg;
17629     }
17630    
17631 });
17632
17633  
17634
17635  /*
17636  * - LGPL
17637  *
17638  * ProgressBar
17639  * 
17640  */
17641
17642 /**
17643  * @class Roo.bootstrap.ProgressBar
17644  * @extends Roo.bootstrap.Component
17645  * Bootstrap ProgressBar class
17646  * @cfg {Number} aria_valuenow aria-value now
17647  * @cfg {Number} aria_valuemin aria-value min
17648  * @cfg {Number} aria_valuemax aria-value max
17649  * @cfg {String} label label for the progress bar
17650  * @cfg {String} panel (success | info | warning | danger )
17651  * @cfg {String} role role of the progress bar
17652  * @cfg {String} sr_only text
17653  * 
17654  * 
17655  * @constructor
17656  * Create a new ProgressBar
17657  * @param {Object} config The config object
17658  */
17659
17660 Roo.bootstrap.ProgressBar = function(config){
17661     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17662 };
17663
17664 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17665     
17666     aria_valuenow : 0,
17667     aria_valuemin : 0,
17668     aria_valuemax : 100,
17669     label : false,
17670     panel : false,
17671     role : false,
17672     sr_only: false,
17673     
17674     getAutoCreate : function()
17675     {
17676         
17677         var cfg = {
17678             tag: 'div',
17679             cls: 'progress-bar',
17680             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17681         };
17682         
17683         if(this.sr_only){
17684             cfg.cn = {
17685                 tag: 'span',
17686                 cls: 'sr-only',
17687                 html: this.sr_only
17688             }
17689         }
17690         
17691         if(this.role){
17692             cfg.role = this.role;
17693         }
17694         
17695         if(this.aria_valuenow){
17696             cfg['aria-valuenow'] = this.aria_valuenow;
17697         }
17698         
17699         if(this.aria_valuemin){
17700             cfg['aria-valuemin'] = this.aria_valuemin;
17701         }
17702         
17703         if(this.aria_valuemax){
17704             cfg['aria-valuemax'] = this.aria_valuemax;
17705         }
17706         
17707         if(this.label && !this.sr_only){
17708             cfg.html = this.label;
17709         }
17710         
17711         if(this.panel){
17712             cfg.cls += ' progress-bar-' + this.panel;
17713         }
17714         
17715         return cfg;
17716     },
17717     
17718     update : function(aria_valuenow)
17719     {
17720         this.aria_valuenow = aria_valuenow;
17721         
17722         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17723     }
17724    
17725 });
17726
17727  
17728
17729  /*
17730  * - LGPL
17731  *
17732  * column
17733  * 
17734  */
17735
17736 /**
17737  * @class Roo.bootstrap.TabGroup
17738  * @extends Roo.bootstrap.Column
17739  * Bootstrap Column class
17740  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17741  * @cfg {Boolean} carousel true to make the group behave like a carousel
17742  * @cfg {Boolean} bullets show bullets for the panels
17743  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17744  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17745  * @cfg {Boolean} showarrow (true|false) show arrow default true
17746  * 
17747  * @constructor
17748  * Create a new TabGroup
17749  * @param {Object} config The config object
17750  */
17751
17752 Roo.bootstrap.TabGroup = function(config){
17753     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17754     if (!this.navId) {
17755         this.navId = Roo.id();
17756     }
17757     this.tabs = [];
17758     Roo.bootstrap.TabGroup.register(this);
17759     
17760 };
17761
17762 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17763     
17764     carousel : false,
17765     transition : false,
17766     bullets : 0,
17767     timer : 0,
17768     autoslide : false,
17769     slideFn : false,
17770     slideOnTouch : false,
17771     showarrow : true,
17772     
17773     getAutoCreate : function()
17774     {
17775         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17776         
17777         cfg.cls += ' tab-content';
17778         
17779         if (this.carousel) {
17780             cfg.cls += ' carousel slide';
17781             
17782             cfg.cn = [{
17783                cls : 'carousel-inner',
17784                cn : []
17785             }];
17786         
17787             if(this.bullets  && !Roo.isTouch){
17788                 
17789                 var bullets = {
17790                     cls : 'carousel-bullets',
17791                     cn : []
17792                 };
17793                
17794                 if(this.bullets_cls){
17795                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17796                 }
17797                 
17798                 bullets.cn.push({
17799                     cls : 'clear'
17800                 });
17801                 
17802                 cfg.cn[0].cn.push(bullets);
17803             }
17804             
17805             if(this.showarrow){
17806                 cfg.cn[0].cn.push({
17807                     tag : 'div',
17808                     class : 'carousel-arrow',
17809                     cn : [
17810                         {
17811                             tag : 'div',
17812                             class : 'carousel-prev',
17813                             cn : [
17814                                 {
17815                                     tag : 'i',
17816                                     class : 'fa fa-chevron-left'
17817                                 }
17818                             ]
17819                         },
17820                         {
17821                             tag : 'div',
17822                             class : 'carousel-next',
17823                             cn : [
17824                                 {
17825                                     tag : 'i',
17826                                     class : 'fa fa-chevron-right'
17827                                 }
17828                             ]
17829                         }
17830                     ]
17831                 });
17832             }
17833             
17834         }
17835         
17836         return cfg;
17837     },
17838     
17839     initEvents:  function()
17840     {
17841 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17842 //            this.el.on("touchstart", this.onTouchStart, this);
17843 //        }
17844         
17845         if(this.autoslide){
17846             var _this = this;
17847             
17848             this.slideFn = window.setInterval(function() {
17849                 _this.showPanelNext();
17850             }, this.timer);
17851         }
17852         
17853         if(this.showarrow){
17854             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17855             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17856         }
17857         
17858         
17859     },
17860     
17861 //    onTouchStart : function(e, el, o)
17862 //    {
17863 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17864 //            return;
17865 //        }
17866 //        
17867 //        this.showPanelNext();
17868 //    },
17869     
17870     
17871     getChildContainer : function()
17872     {
17873         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17874     },
17875     
17876     /**
17877     * register a Navigation item
17878     * @param {Roo.bootstrap.NavItem} the navitem to add
17879     */
17880     register : function(item)
17881     {
17882         this.tabs.push( item);
17883         item.navId = this.navId; // not really needed..
17884         this.addBullet();
17885     
17886     },
17887     
17888     getActivePanel : function()
17889     {
17890         var r = false;
17891         Roo.each(this.tabs, function(t) {
17892             if (t.active) {
17893                 r = t;
17894                 return false;
17895             }
17896             return null;
17897         });
17898         return r;
17899         
17900     },
17901     getPanelByName : function(n)
17902     {
17903         var r = false;
17904         Roo.each(this.tabs, function(t) {
17905             if (t.tabId == n) {
17906                 r = t;
17907                 return false;
17908             }
17909             return null;
17910         });
17911         return r;
17912     },
17913     indexOfPanel : function(p)
17914     {
17915         var r = false;
17916         Roo.each(this.tabs, function(t,i) {
17917             if (t.tabId == p.tabId) {
17918                 r = i;
17919                 return false;
17920             }
17921             return null;
17922         });
17923         return r;
17924     },
17925     /**
17926      * show a specific panel
17927      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17928      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17929      */
17930     showPanel : function (pan)
17931     {
17932         if(this.transition || typeof(pan) == 'undefined'){
17933             Roo.log("waiting for the transitionend");
17934             return;
17935         }
17936         
17937         if (typeof(pan) == 'number') {
17938             pan = this.tabs[pan];
17939         }
17940         
17941         if (typeof(pan) == 'string') {
17942             pan = this.getPanelByName(pan);
17943         }
17944         
17945         var cur = this.getActivePanel();
17946         
17947         if(!pan || !cur){
17948             Roo.log('pan or acitve pan is undefined');
17949             return false;
17950         }
17951         
17952         if (pan.tabId == this.getActivePanel().tabId) {
17953             return true;
17954         }
17955         
17956         if (false === cur.fireEvent('beforedeactivate')) {
17957             return false;
17958         }
17959         
17960         if(this.bullets > 0 && !Roo.isTouch){
17961             this.setActiveBullet(this.indexOfPanel(pan));
17962         }
17963         
17964         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17965             
17966             this.transition = true;
17967             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17968             var lr = dir == 'next' ? 'left' : 'right';
17969             pan.el.addClass(dir); // or prev
17970             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17971             cur.el.addClass(lr); // or right
17972             pan.el.addClass(lr);
17973             
17974             var _this = this;
17975             cur.el.on('transitionend', function() {
17976                 Roo.log("trans end?");
17977                 
17978                 pan.el.removeClass([lr,dir]);
17979                 pan.setActive(true);
17980                 
17981                 cur.el.removeClass([lr]);
17982                 cur.setActive(false);
17983                 
17984                 _this.transition = false;
17985                 
17986             }, this, { single:  true } );
17987             
17988             return true;
17989         }
17990         
17991         cur.setActive(false);
17992         pan.setActive(true);
17993         
17994         return true;
17995         
17996     },
17997     showPanelNext : function()
17998     {
17999         var i = this.indexOfPanel(this.getActivePanel());
18000         
18001         if (i >= this.tabs.length - 1 && !this.autoslide) {
18002             return;
18003         }
18004         
18005         if (i >= this.tabs.length - 1 && this.autoslide) {
18006             i = -1;
18007         }
18008         
18009         this.showPanel(this.tabs[i+1]);
18010     },
18011     
18012     showPanelPrev : function()
18013     {
18014         var i = this.indexOfPanel(this.getActivePanel());
18015         
18016         if (i  < 1 && !this.autoslide) {
18017             return;
18018         }
18019         
18020         if (i < 1 && this.autoslide) {
18021             i = this.tabs.length;
18022         }
18023         
18024         this.showPanel(this.tabs[i-1]);
18025     },
18026     
18027     
18028     addBullet: function()
18029     {
18030         if(!this.bullets || Roo.isTouch){
18031             return;
18032         }
18033         var ctr = this.el.select('.carousel-bullets',true).first();
18034         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18035         var bullet = ctr.createChild({
18036             cls : 'bullet bullet-' + i
18037         },ctr.dom.lastChild);
18038         
18039         
18040         var _this = this;
18041         
18042         bullet.on('click', (function(e, el, o, ii, t){
18043
18044             e.preventDefault();
18045
18046             this.showPanel(ii);
18047
18048             if(this.autoslide && this.slideFn){
18049                 clearInterval(this.slideFn);
18050                 this.slideFn = window.setInterval(function() {
18051                     _this.showPanelNext();
18052                 }, this.timer);
18053             }
18054
18055         }).createDelegate(this, [i, bullet], true));
18056                 
18057         
18058     },
18059      
18060     setActiveBullet : function(i)
18061     {
18062         if(Roo.isTouch){
18063             return;
18064         }
18065         
18066         Roo.each(this.el.select('.bullet', true).elements, function(el){
18067             el.removeClass('selected');
18068         });
18069
18070         var bullet = this.el.select('.bullet-' + i, true).first();
18071         
18072         if(!bullet){
18073             return;
18074         }
18075         
18076         bullet.addClass('selected');
18077     }
18078     
18079     
18080   
18081 });
18082
18083  
18084
18085  
18086  
18087 Roo.apply(Roo.bootstrap.TabGroup, {
18088     
18089     groups: {},
18090      /**
18091     * register a Navigation Group
18092     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18093     */
18094     register : function(navgrp)
18095     {
18096         this.groups[navgrp.navId] = navgrp;
18097         
18098     },
18099     /**
18100     * fetch a Navigation Group based on the navigation ID
18101     * if one does not exist , it will get created.
18102     * @param {string} the navgroup to add
18103     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18104     */
18105     get: function(navId) {
18106         if (typeof(this.groups[navId]) == 'undefined') {
18107             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18108         }
18109         return this.groups[navId] ;
18110     }
18111     
18112     
18113     
18114 });
18115
18116  /*
18117  * - LGPL
18118  *
18119  * TabPanel
18120  * 
18121  */
18122
18123 /**
18124  * @class Roo.bootstrap.TabPanel
18125  * @extends Roo.bootstrap.Component
18126  * Bootstrap TabPanel class
18127  * @cfg {Boolean} active panel active
18128  * @cfg {String} html panel content
18129  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18130  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18131  * @cfg {String} href click to link..
18132  * 
18133  * 
18134  * @constructor
18135  * Create a new TabPanel
18136  * @param {Object} config The config object
18137  */
18138
18139 Roo.bootstrap.TabPanel = function(config){
18140     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18141     this.addEvents({
18142         /**
18143              * @event changed
18144              * Fires when the active status changes
18145              * @param {Roo.bootstrap.TabPanel} this
18146              * @param {Boolean} state the new state
18147             
18148          */
18149         'changed': true,
18150         /**
18151              * @event beforedeactivate
18152              * Fires before a tab is de-activated - can be used to do validation on a form.
18153              * @param {Roo.bootstrap.TabPanel} this
18154              * @return {Boolean} false if there is an error
18155             
18156          */
18157         'beforedeactivate': true
18158      });
18159     
18160     this.tabId = this.tabId || Roo.id();
18161   
18162 };
18163
18164 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18165     
18166     active: false,
18167     html: false,
18168     tabId: false,
18169     navId : false,
18170     href : '',
18171     
18172     getAutoCreate : function(){
18173         var cfg = {
18174             tag: 'div',
18175             // item is needed for carousel - not sure if it has any effect otherwise
18176             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18177             html: this.html || ''
18178         };
18179         
18180         if(this.active){
18181             cfg.cls += ' active';
18182         }
18183         
18184         if(this.tabId){
18185             cfg.tabId = this.tabId;
18186         }
18187         
18188         
18189         return cfg;
18190     },
18191     
18192     initEvents:  function()
18193     {
18194         var p = this.parent();
18195         
18196         this.navId = this.navId || p.navId;
18197         
18198         if (typeof(this.navId) != 'undefined') {
18199             // not really needed.. but just in case.. parent should be a NavGroup.
18200             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18201             
18202             tg.register(this);
18203             
18204             var i = tg.tabs.length - 1;
18205             
18206             if(this.active && tg.bullets > 0 && i < tg.bullets){
18207                 tg.setActiveBullet(i);
18208             }
18209         }
18210         
18211         this.el.on('click', this.onClick, this);
18212         
18213         if(Roo.isTouch){
18214             this.el.on("touchstart", this.onTouchStart, this);
18215             this.el.on("touchmove", this.onTouchMove, this);
18216             this.el.on("touchend", this.onTouchEnd, this);
18217         }
18218         
18219     },
18220     
18221     onRender : function(ct, position)
18222     {
18223         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18224     },
18225     
18226     setActive : function(state)
18227     {
18228         Roo.log("panel - set active " + this.tabId + "=" + state);
18229         
18230         this.active = state;
18231         if (!state) {
18232             this.el.removeClass('active');
18233             
18234         } else  if (!this.el.hasClass('active')) {
18235             this.el.addClass('active');
18236         }
18237         
18238         this.fireEvent('changed', this, state);
18239     },
18240     
18241     onClick : function(e)
18242     {
18243         e.preventDefault();
18244         
18245         if(!this.href.length){
18246             return;
18247         }
18248         
18249         window.location.href = this.href;
18250     },
18251     
18252     startX : 0,
18253     startY : 0,
18254     endX : 0,
18255     endY : 0,
18256     swiping : false,
18257     
18258     onTouchStart : function(e)
18259     {
18260         this.swiping = false;
18261         
18262         this.startX = e.browserEvent.touches[0].clientX;
18263         this.startY = e.browserEvent.touches[0].clientY;
18264     },
18265     
18266     onTouchMove : function(e)
18267     {
18268         this.swiping = true;
18269         
18270         this.endX = e.browserEvent.touches[0].clientX;
18271         this.endY = e.browserEvent.touches[0].clientY;
18272     },
18273     
18274     onTouchEnd : function(e)
18275     {
18276         if(!this.swiping){
18277             this.onClick(e);
18278             return;
18279         }
18280         
18281         var tabGroup = this.parent();
18282         
18283         if(this.endX > this.startX){ // swiping right
18284             tabGroup.showPanelPrev();
18285             return;
18286         }
18287         
18288         if(this.startX > this.endX){ // swiping left
18289             tabGroup.showPanelNext();
18290             return;
18291         }
18292     }
18293     
18294     
18295 });
18296  
18297
18298  
18299
18300  /*
18301  * - LGPL
18302  *
18303  * DateField
18304  * 
18305  */
18306
18307 /**
18308  * @class Roo.bootstrap.DateField
18309  * @extends Roo.bootstrap.Input
18310  * Bootstrap DateField class
18311  * @cfg {Number} weekStart default 0
18312  * @cfg {String} viewMode default empty, (months|years)
18313  * @cfg {String} minViewMode default empty, (months|years)
18314  * @cfg {Number} startDate default -Infinity
18315  * @cfg {Number} endDate default Infinity
18316  * @cfg {Boolean} todayHighlight default false
18317  * @cfg {Boolean} todayBtn default false
18318  * @cfg {Boolean} calendarWeeks default false
18319  * @cfg {Object} daysOfWeekDisabled default empty
18320  * @cfg {Boolean} singleMode default false (true | false)
18321  * 
18322  * @cfg {Boolean} keyboardNavigation default true
18323  * @cfg {String} language default en
18324  * 
18325  * @constructor
18326  * Create a new DateField
18327  * @param {Object} config The config object
18328  */
18329
18330 Roo.bootstrap.DateField = function(config){
18331     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18332      this.addEvents({
18333             /**
18334              * @event show
18335              * Fires when this field show.
18336              * @param {Roo.bootstrap.DateField} this
18337              * @param {Mixed} date The date value
18338              */
18339             show : true,
18340             /**
18341              * @event show
18342              * Fires when this field hide.
18343              * @param {Roo.bootstrap.DateField} this
18344              * @param {Mixed} date The date value
18345              */
18346             hide : true,
18347             /**
18348              * @event select
18349              * Fires when select a date.
18350              * @param {Roo.bootstrap.DateField} this
18351              * @param {Mixed} date The date value
18352              */
18353             select : true,
18354             /**
18355              * @event beforeselect
18356              * Fires when before select a date.
18357              * @param {Roo.bootstrap.DateField} this
18358              * @param {Mixed} date The date value
18359              */
18360             beforeselect : true
18361         });
18362 };
18363
18364 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18365     
18366     /**
18367      * @cfg {String} format
18368      * The default date format string which can be overriden for localization support.  The format must be
18369      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18370      */
18371     format : "m/d/y",
18372     /**
18373      * @cfg {String} altFormats
18374      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18375      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18376      */
18377     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18378     
18379     weekStart : 0,
18380     
18381     viewMode : '',
18382     
18383     minViewMode : '',
18384     
18385     todayHighlight : false,
18386     
18387     todayBtn: false,
18388     
18389     language: 'en',
18390     
18391     keyboardNavigation: true,
18392     
18393     calendarWeeks: false,
18394     
18395     startDate: -Infinity,
18396     
18397     endDate: Infinity,
18398     
18399     daysOfWeekDisabled: [],
18400     
18401     _events: [],
18402     
18403     singleMode : false,
18404     
18405     UTCDate: function()
18406     {
18407         return new Date(Date.UTC.apply(Date, arguments));
18408     },
18409     
18410     UTCToday: function()
18411     {
18412         var today = new Date();
18413         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18414     },
18415     
18416     getDate: function() {
18417             var d = this.getUTCDate();
18418             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18419     },
18420     
18421     getUTCDate: function() {
18422             return this.date;
18423     },
18424     
18425     setDate: function(d) {
18426             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18427     },
18428     
18429     setUTCDate: function(d) {
18430             this.date = d;
18431             this.setValue(this.formatDate(this.date));
18432     },
18433         
18434     onRender: function(ct, position)
18435     {
18436         
18437         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18438         
18439         this.language = this.language || 'en';
18440         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18441         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18442         
18443         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18444         this.format = this.format || 'm/d/y';
18445         this.isInline = false;
18446         this.isInput = true;
18447         this.component = this.el.select('.add-on', true).first() || false;
18448         this.component = (this.component && this.component.length === 0) ? false : this.component;
18449         this.hasInput = this.component && this.inputEl().length;
18450         
18451         if (typeof(this.minViewMode === 'string')) {
18452             switch (this.minViewMode) {
18453                 case 'months':
18454                     this.minViewMode = 1;
18455                     break;
18456                 case 'years':
18457                     this.minViewMode = 2;
18458                     break;
18459                 default:
18460                     this.minViewMode = 0;
18461                     break;
18462             }
18463         }
18464         
18465         if (typeof(this.viewMode === 'string')) {
18466             switch (this.viewMode) {
18467                 case 'months':
18468                     this.viewMode = 1;
18469                     break;
18470                 case 'years':
18471                     this.viewMode = 2;
18472                     break;
18473                 default:
18474                     this.viewMode = 0;
18475                     break;
18476             }
18477         }
18478                 
18479         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18480         
18481 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18482         
18483         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18484         
18485         this.picker().on('mousedown', this.onMousedown, this);
18486         this.picker().on('click', this.onClick, this);
18487         
18488         this.picker().addClass('datepicker-dropdown');
18489         
18490         this.startViewMode = this.viewMode;
18491         
18492         if(this.singleMode){
18493             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18494                 v.setVisibilityMode(Roo.Element.DISPLAY);
18495                 v.hide();
18496             });
18497             
18498             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18499                 v.setStyle('width', '189px');
18500             });
18501         }
18502         
18503         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18504             if(!this.calendarWeeks){
18505                 v.remove();
18506                 return;
18507             }
18508             
18509             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18510             v.attr('colspan', function(i, val){
18511                 return parseInt(val) + 1;
18512             });
18513         });
18514                         
18515         
18516         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18517         
18518         this.setStartDate(this.startDate);
18519         this.setEndDate(this.endDate);
18520         
18521         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18522         
18523         this.fillDow();
18524         this.fillMonths();
18525         this.update();
18526         this.showMode();
18527         
18528         if(this.isInline) {
18529             this.show();
18530         }
18531     },
18532     
18533     picker : function()
18534     {
18535         return this.pickerEl;
18536 //        return this.el.select('.datepicker', true).first();
18537     },
18538     
18539     fillDow: function()
18540     {
18541         var dowCnt = this.weekStart;
18542         
18543         var dow = {
18544             tag: 'tr',
18545             cn: [
18546                 
18547             ]
18548         };
18549         
18550         if(this.calendarWeeks){
18551             dow.cn.push({
18552                 tag: 'th',
18553                 cls: 'cw',
18554                 html: '&nbsp;'
18555             })
18556         }
18557         
18558         while (dowCnt < this.weekStart + 7) {
18559             dow.cn.push({
18560                 tag: 'th',
18561                 cls: 'dow',
18562                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18563             });
18564         }
18565         
18566         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18567     },
18568     
18569     fillMonths: function()
18570     {    
18571         var i = 0;
18572         var months = this.picker().select('>.datepicker-months td', true).first();
18573         
18574         months.dom.innerHTML = '';
18575         
18576         while (i < 12) {
18577             var month = {
18578                 tag: 'span',
18579                 cls: 'month',
18580                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18581             };
18582             
18583             months.createChild(month);
18584         }
18585         
18586     },
18587     
18588     update: function()
18589     {
18590         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;
18591         
18592         if (this.date < this.startDate) {
18593             this.viewDate = new Date(this.startDate);
18594         } else if (this.date > this.endDate) {
18595             this.viewDate = new Date(this.endDate);
18596         } else {
18597             this.viewDate = new Date(this.date);
18598         }
18599         
18600         this.fill();
18601     },
18602     
18603     fill: function() 
18604     {
18605         var d = new Date(this.viewDate),
18606                 year = d.getUTCFullYear(),
18607                 month = d.getUTCMonth(),
18608                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18609                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18610                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18611                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18612                 currentDate = this.date && this.date.valueOf(),
18613                 today = this.UTCToday();
18614         
18615         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18616         
18617 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18618         
18619 //        this.picker.select('>tfoot th.today').
18620 //                                              .text(dates[this.language].today)
18621 //                                              .toggle(this.todayBtn !== false);
18622     
18623         this.updateNavArrows();
18624         this.fillMonths();
18625                                                 
18626         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18627         
18628         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18629          
18630         prevMonth.setUTCDate(day);
18631         
18632         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18633         
18634         var nextMonth = new Date(prevMonth);
18635         
18636         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18637         
18638         nextMonth = nextMonth.valueOf();
18639         
18640         var fillMonths = false;
18641         
18642         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18643         
18644         while(prevMonth.valueOf() <= nextMonth) {
18645             var clsName = '';
18646             
18647             if (prevMonth.getUTCDay() === this.weekStart) {
18648                 if(fillMonths){
18649                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18650                 }
18651                     
18652                 fillMonths = {
18653                     tag: 'tr',
18654                     cn: []
18655                 };
18656                 
18657                 if(this.calendarWeeks){
18658                     // ISO 8601: First week contains first thursday.
18659                     // ISO also states week starts on Monday, but we can be more abstract here.
18660                     var
18661                     // Start of current week: based on weekstart/current date
18662                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18663                     // Thursday of this week
18664                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18665                     // First Thursday of year, year from thursday
18666                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18667                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18668                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18669                     
18670                     fillMonths.cn.push({
18671                         tag: 'td',
18672                         cls: 'cw',
18673                         html: calWeek
18674                     });
18675                 }
18676             }
18677             
18678             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18679                 clsName += ' old';
18680             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18681                 clsName += ' new';
18682             }
18683             if (this.todayHighlight &&
18684                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18685                 prevMonth.getUTCMonth() == today.getMonth() &&
18686                 prevMonth.getUTCDate() == today.getDate()) {
18687                 clsName += ' today';
18688             }
18689             
18690             if (currentDate && prevMonth.valueOf() === currentDate) {
18691                 clsName += ' active';
18692             }
18693             
18694             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18695                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18696                     clsName += ' disabled';
18697             }
18698             
18699             fillMonths.cn.push({
18700                 tag: 'td',
18701                 cls: 'day ' + clsName,
18702                 html: prevMonth.getDate()
18703             });
18704             
18705             prevMonth.setDate(prevMonth.getDate()+1);
18706         }
18707           
18708         var currentYear = this.date && this.date.getUTCFullYear();
18709         var currentMonth = this.date && this.date.getUTCMonth();
18710         
18711         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18712         
18713         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18714             v.removeClass('active');
18715             
18716             if(currentYear === year && k === currentMonth){
18717                 v.addClass('active');
18718             }
18719             
18720             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18721                 v.addClass('disabled');
18722             }
18723             
18724         });
18725         
18726         
18727         year = parseInt(year/10, 10) * 10;
18728         
18729         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18730         
18731         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18732         
18733         year -= 1;
18734         for (var i = -1; i < 11; i++) {
18735             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18736                 tag: 'span',
18737                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18738                 html: year
18739             });
18740             
18741             year += 1;
18742         }
18743     },
18744     
18745     showMode: function(dir) 
18746     {
18747         if (dir) {
18748             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18749         }
18750         
18751         Roo.each(this.picker().select('>div',true).elements, function(v){
18752             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18753             v.hide();
18754         });
18755         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18756     },
18757     
18758     place: function()
18759     {
18760         if(this.isInline) {
18761             return;
18762         }
18763         
18764         this.picker().removeClass(['bottom', 'top']);
18765         
18766         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18767             /*
18768              * place to the top of element!
18769              *
18770              */
18771             
18772             this.picker().addClass('top');
18773             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18774             
18775             return;
18776         }
18777         
18778         this.picker().addClass('bottom');
18779         
18780         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18781     },
18782     
18783     parseDate : function(value)
18784     {
18785         if(!value || value instanceof Date){
18786             return value;
18787         }
18788         var v = Date.parseDate(value, this.format);
18789         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18790             v = Date.parseDate(value, 'Y-m-d');
18791         }
18792         if(!v && this.altFormats){
18793             if(!this.altFormatsArray){
18794                 this.altFormatsArray = this.altFormats.split("|");
18795             }
18796             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18797                 v = Date.parseDate(value, this.altFormatsArray[i]);
18798             }
18799         }
18800         return v;
18801     },
18802     
18803     formatDate : function(date, fmt)
18804     {   
18805         return (!date || !(date instanceof Date)) ?
18806         date : date.dateFormat(fmt || this.format);
18807     },
18808     
18809     onFocus : function()
18810     {
18811         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18812         this.show();
18813     },
18814     
18815     onBlur : function()
18816     {
18817         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18818         
18819         var d = this.inputEl().getValue();
18820         
18821         this.setValue(d);
18822                 
18823         this.hide();
18824     },
18825     
18826     show : function()
18827     {
18828         this.picker().show();
18829         this.update();
18830         this.place();
18831         
18832         this.fireEvent('show', this, this.date);
18833     },
18834     
18835     hide : function()
18836     {
18837         if(this.isInline) {
18838             return;
18839         }
18840         this.picker().hide();
18841         this.viewMode = this.startViewMode;
18842         this.showMode();
18843         
18844         this.fireEvent('hide', this, this.date);
18845         
18846     },
18847     
18848     onMousedown: function(e)
18849     {
18850         e.stopPropagation();
18851         e.preventDefault();
18852     },
18853     
18854     keyup: function(e)
18855     {
18856         Roo.bootstrap.DateField.superclass.keyup.call(this);
18857         this.update();
18858     },
18859
18860     setValue: function(v)
18861     {
18862         if(this.fireEvent('beforeselect', this, v) !== false){
18863             var d = new Date(this.parseDate(v) ).clearTime();
18864         
18865             if(isNaN(d.getTime())){
18866                 this.date = this.viewDate = '';
18867                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18868                 return;
18869             }
18870
18871             v = this.formatDate(d);
18872
18873             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18874
18875             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18876
18877             this.update();
18878
18879             this.fireEvent('select', this, this.date);
18880         }
18881     },
18882     
18883     getValue: function()
18884     {
18885         return this.formatDate(this.date);
18886     },
18887     
18888     fireKey: function(e)
18889     {
18890         if (!this.picker().isVisible()){
18891             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18892                 this.show();
18893             }
18894             return;
18895         }
18896         
18897         var dateChanged = false,
18898         dir, day, month,
18899         newDate, newViewDate;
18900         
18901         switch(e.keyCode){
18902             case 27: // escape
18903                 this.hide();
18904                 e.preventDefault();
18905                 break;
18906             case 37: // left
18907             case 39: // right
18908                 if (!this.keyboardNavigation) {
18909                     break;
18910                 }
18911                 dir = e.keyCode == 37 ? -1 : 1;
18912                 
18913                 if (e.ctrlKey){
18914                     newDate = this.moveYear(this.date, dir);
18915                     newViewDate = this.moveYear(this.viewDate, dir);
18916                 } else if (e.shiftKey){
18917                     newDate = this.moveMonth(this.date, dir);
18918                     newViewDate = this.moveMonth(this.viewDate, dir);
18919                 } else {
18920                     newDate = new Date(this.date);
18921                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18922                     newViewDate = new Date(this.viewDate);
18923                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18924                 }
18925                 if (this.dateWithinRange(newDate)){
18926                     this.date = newDate;
18927                     this.viewDate = newViewDate;
18928                     this.setValue(this.formatDate(this.date));
18929 //                    this.update();
18930                     e.preventDefault();
18931                     dateChanged = true;
18932                 }
18933                 break;
18934             case 38: // up
18935             case 40: // down
18936                 if (!this.keyboardNavigation) {
18937                     break;
18938                 }
18939                 dir = e.keyCode == 38 ? -1 : 1;
18940                 if (e.ctrlKey){
18941                     newDate = this.moveYear(this.date, dir);
18942                     newViewDate = this.moveYear(this.viewDate, dir);
18943                 } else if (e.shiftKey){
18944                     newDate = this.moveMonth(this.date, dir);
18945                     newViewDate = this.moveMonth(this.viewDate, dir);
18946                 } else {
18947                     newDate = new Date(this.date);
18948                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18949                     newViewDate = new Date(this.viewDate);
18950                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18951                 }
18952                 if (this.dateWithinRange(newDate)){
18953                     this.date = newDate;
18954                     this.viewDate = newViewDate;
18955                     this.setValue(this.formatDate(this.date));
18956 //                    this.update();
18957                     e.preventDefault();
18958                     dateChanged = true;
18959                 }
18960                 break;
18961             case 13: // enter
18962                 this.setValue(this.formatDate(this.date));
18963                 this.hide();
18964                 e.preventDefault();
18965                 break;
18966             case 9: // tab
18967                 this.setValue(this.formatDate(this.date));
18968                 this.hide();
18969                 break;
18970             case 16: // shift
18971             case 17: // ctrl
18972             case 18: // alt
18973                 break;
18974             default :
18975                 this.hide();
18976                 
18977         }
18978     },
18979     
18980     
18981     onClick: function(e) 
18982     {
18983         e.stopPropagation();
18984         e.preventDefault();
18985         
18986         var target = e.getTarget();
18987         
18988         if(target.nodeName.toLowerCase() === 'i'){
18989             target = Roo.get(target).dom.parentNode;
18990         }
18991         
18992         var nodeName = target.nodeName;
18993         var className = target.className;
18994         var html = target.innerHTML;
18995         //Roo.log(nodeName);
18996         
18997         switch(nodeName.toLowerCase()) {
18998             case 'th':
18999                 switch(className) {
19000                     case 'switch':
19001                         this.showMode(1);
19002                         break;
19003                     case 'prev':
19004                     case 'next':
19005                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19006                         switch(this.viewMode){
19007                                 case 0:
19008                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19009                                         break;
19010                                 case 1:
19011                                 case 2:
19012                                         this.viewDate = this.moveYear(this.viewDate, dir);
19013                                         break;
19014                         }
19015                         this.fill();
19016                         break;
19017                     case 'today':
19018                         var date = new Date();
19019                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19020 //                        this.fill()
19021                         this.setValue(this.formatDate(this.date));
19022                         
19023                         this.hide();
19024                         break;
19025                 }
19026                 break;
19027             case 'span':
19028                 if (className.indexOf('disabled') < 0) {
19029                     this.viewDate.setUTCDate(1);
19030                     if (className.indexOf('month') > -1) {
19031                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19032                     } else {
19033                         var year = parseInt(html, 10) || 0;
19034                         this.viewDate.setUTCFullYear(year);
19035                         
19036                     }
19037                     
19038                     if(this.singleMode){
19039                         this.setValue(this.formatDate(this.viewDate));
19040                         this.hide();
19041                         return;
19042                     }
19043                     
19044                     this.showMode(-1);
19045                     this.fill();
19046                 }
19047                 break;
19048                 
19049             case 'td':
19050                 //Roo.log(className);
19051                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19052                     var day = parseInt(html, 10) || 1;
19053                     var year = this.viewDate.getUTCFullYear(),
19054                         month = this.viewDate.getUTCMonth();
19055
19056                     if (className.indexOf('old') > -1) {
19057                         if(month === 0 ){
19058                             month = 11;
19059                             year -= 1;
19060                         }else{
19061                             month -= 1;
19062                         }
19063                     } else if (className.indexOf('new') > -1) {
19064                         if (month == 11) {
19065                             month = 0;
19066                             year += 1;
19067                         } else {
19068                             month += 1;
19069                         }
19070                     }
19071                     //Roo.log([year,month,day]);
19072                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19073                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19074 //                    this.fill();
19075                     //Roo.log(this.formatDate(this.date));
19076                     this.setValue(this.formatDate(this.date));
19077                     this.hide();
19078                 }
19079                 break;
19080         }
19081     },
19082     
19083     setStartDate: function(startDate)
19084     {
19085         this.startDate = startDate || -Infinity;
19086         if (this.startDate !== -Infinity) {
19087             this.startDate = this.parseDate(this.startDate);
19088         }
19089         this.update();
19090         this.updateNavArrows();
19091     },
19092
19093     setEndDate: function(endDate)
19094     {
19095         this.endDate = endDate || Infinity;
19096         if (this.endDate !== Infinity) {
19097             this.endDate = this.parseDate(this.endDate);
19098         }
19099         this.update();
19100         this.updateNavArrows();
19101     },
19102     
19103     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19104     {
19105         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19106         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19107             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19108         }
19109         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19110             return parseInt(d, 10);
19111         });
19112         this.update();
19113         this.updateNavArrows();
19114     },
19115     
19116     updateNavArrows: function() 
19117     {
19118         if(this.singleMode){
19119             return;
19120         }
19121         
19122         var d = new Date(this.viewDate),
19123         year = d.getUTCFullYear(),
19124         month = d.getUTCMonth();
19125         
19126         Roo.each(this.picker().select('.prev', true).elements, function(v){
19127             v.show();
19128             switch (this.viewMode) {
19129                 case 0:
19130
19131                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19132                         v.hide();
19133                     }
19134                     break;
19135                 case 1:
19136                 case 2:
19137                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19138                         v.hide();
19139                     }
19140                     break;
19141             }
19142         });
19143         
19144         Roo.each(this.picker().select('.next', true).elements, function(v){
19145             v.show();
19146             switch (this.viewMode) {
19147                 case 0:
19148
19149                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19150                         v.hide();
19151                     }
19152                     break;
19153                 case 1:
19154                 case 2:
19155                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19156                         v.hide();
19157                     }
19158                     break;
19159             }
19160         })
19161     },
19162     
19163     moveMonth: function(date, dir)
19164     {
19165         if (!dir) {
19166             return date;
19167         }
19168         var new_date = new Date(date.valueOf()),
19169         day = new_date.getUTCDate(),
19170         month = new_date.getUTCMonth(),
19171         mag = Math.abs(dir),
19172         new_month, test;
19173         dir = dir > 0 ? 1 : -1;
19174         if (mag == 1){
19175             test = dir == -1
19176             // If going back one month, make sure month is not current month
19177             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19178             ? function(){
19179                 return new_date.getUTCMonth() == month;
19180             }
19181             // If going forward one month, make sure month is as expected
19182             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19183             : function(){
19184                 return new_date.getUTCMonth() != new_month;
19185             };
19186             new_month = month + dir;
19187             new_date.setUTCMonth(new_month);
19188             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19189             if (new_month < 0 || new_month > 11) {
19190                 new_month = (new_month + 12) % 12;
19191             }
19192         } else {
19193             // For magnitudes >1, move one month at a time...
19194             for (var i=0; i<mag; i++) {
19195                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19196                 new_date = this.moveMonth(new_date, dir);
19197             }
19198             // ...then reset the day, keeping it in the new month
19199             new_month = new_date.getUTCMonth();
19200             new_date.setUTCDate(day);
19201             test = function(){
19202                 return new_month != new_date.getUTCMonth();
19203             };
19204         }
19205         // Common date-resetting loop -- if date is beyond end of month, make it
19206         // end of month
19207         while (test()){
19208             new_date.setUTCDate(--day);
19209             new_date.setUTCMonth(new_month);
19210         }
19211         return new_date;
19212     },
19213
19214     moveYear: function(date, dir)
19215     {
19216         return this.moveMonth(date, dir*12);
19217     },
19218
19219     dateWithinRange: function(date)
19220     {
19221         return date >= this.startDate && date <= this.endDate;
19222     },
19223
19224     
19225     remove: function() 
19226     {
19227         this.picker().remove();
19228     },
19229     
19230     validateValue : function(value)
19231     {
19232         if(this.getVisibilityEl().hasClass('hidden')){
19233             return true;
19234         }
19235         
19236         if(value.length < 1)  {
19237             if(this.allowBlank){
19238                 return true;
19239             }
19240             return false;
19241         }
19242         
19243         if(value.length < this.minLength){
19244             return false;
19245         }
19246         if(value.length > this.maxLength){
19247             return false;
19248         }
19249         if(this.vtype){
19250             var vt = Roo.form.VTypes;
19251             if(!vt[this.vtype](value, this)){
19252                 return false;
19253             }
19254         }
19255         if(typeof this.validator == "function"){
19256             var msg = this.validator(value);
19257             if(msg !== true){
19258                 return false;
19259             }
19260         }
19261         
19262         if(this.regex && !this.regex.test(value)){
19263             return false;
19264         }
19265         
19266         if(typeof(this.parseDate(value)) == 'undefined'){
19267             return false;
19268         }
19269         
19270         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19271             return false;
19272         }      
19273         
19274         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19275             return false;
19276         } 
19277         
19278         
19279         return true;
19280     },
19281     
19282     setVisible : function(visible)
19283     {
19284         if(!this.getEl()){
19285             return;
19286         }
19287         
19288         this.getEl().removeClass('hidden');
19289         
19290         if(visible){
19291             return;
19292         }
19293         
19294         this.getEl().addClass('hidden');
19295     }
19296    
19297 });
19298
19299 Roo.apply(Roo.bootstrap.DateField,  {
19300     
19301     head : {
19302         tag: 'thead',
19303         cn: [
19304         {
19305             tag: 'tr',
19306             cn: [
19307             {
19308                 tag: 'th',
19309                 cls: 'prev',
19310                 html: '<i class="fa fa-arrow-left"/>'
19311             },
19312             {
19313                 tag: 'th',
19314                 cls: 'switch',
19315                 colspan: '5'
19316             },
19317             {
19318                 tag: 'th',
19319                 cls: 'next',
19320                 html: '<i class="fa fa-arrow-right"/>'
19321             }
19322
19323             ]
19324         }
19325         ]
19326     },
19327     
19328     content : {
19329         tag: 'tbody',
19330         cn: [
19331         {
19332             tag: 'tr',
19333             cn: [
19334             {
19335                 tag: 'td',
19336                 colspan: '7'
19337             }
19338             ]
19339         }
19340         ]
19341     },
19342     
19343     footer : {
19344         tag: 'tfoot',
19345         cn: [
19346         {
19347             tag: 'tr',
19348             cn: [
19349             {
19350                 tag: 'th',
19351                 colspan: '7',
19352                 cls: 'today'
19353             }
19354                     
19355             ]
19356         }
19357         ]
19358     },
19359     
19360     dates:{
19361         en: {
19362             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19363             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19364             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19365             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19366             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19367             today: "Today"
19368         }
19369     },
19370     
19371     modes: [
19372     {
19373         clsName: 'days',
19374         navFnc: 'Month',
19375         navStep: 1
19376     },
19377     {
19378         clsName: 'months',
19379         navFnc: 'FullYear',
19380         navStep: 1
19381     },
19382     {
19383         clsName: 'years',
19384         navFnc: 'FullYear',
19385         navStep: 10
19386     }]
19387 });
19388
19389 Roo.apply(Roo.bootstrap.DateField,  {
19390   
19391     template : {
19392         tag: 'div',
19393         cls: 'datepicker dropdown-menu roo-dynamic',
19394         cn: [
19395         {
19396             tag: 'div',
19397             cls: 'datepicker-days',
19398             cn: [
19399             {
19400                 tag: 'table',
19401                 cls: 'table-condensed',
19402                 cn:[
19403                 Roo.bootstrap.DateField.head,
19404                 {
19405                     tag: 'tbody'
19406                 },
19407                 Roo.bootstrap.DateField.footer
19408                 ]
19409             }
19410             ]
19411         },
19412         {
19413             tag: 'div',
19414             cls: 'datepicker-months',
19415             cn: [
19416             {
19417                 tag: 'table',
19418                 cls: 'table-condensed',
19419                 cn:[
19420                 Roo.bootstrap.DateField.head,
19421                 Roo.bootstrap.DateField.content,
19422                 Roo.bootstrap.DateField.footer
19423                 ]
19424             }
19425             ]
19426         },
19427         {
19428             tag: 'div',
19429             cls: 'datepicker-years',
19430             cn: [
19431             {
19432                 tag: 'table',
19433                 cls: 'table-condensed',
19434                 cn:[
19435                 Roo.bootstrap.DateField.head,
19436                 Roo.bootstrap.DateField.content,
19437                 Roo.bootstrap.DateField.footer
19438                 ]
19439             }
19440             ]
19441         }
19442         ]
19443     }
19444 });
19445
19446  
19447
19448  /*
19449  * - LGPL
19450  *
19451  * TimeField
19452  * 
19453  */
19454
19455 /**
19456  * @class Roo.bootstrap.TimeField
19457  * @extends Roo.bootstrap.Input
19458  * Bootstrap DateField class
19459  * 
19460  * 
19461  * @constructor
19462  * Create a new TimeField
19463  * @param {Object} config The config object
19464  */
19465
19466 Roo.bootstrap.TimeField = function(config){
19467     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19468     this.addEvents({
19469             /**
19470              * @event show
19471              * Fires when this field show.
19472              * @param {Roo.bootstrap.DateField} thisthis
19473              * @param {Mixed} date The date value
19474              */
19475             show : true,
19476             /**
19477              * @event show
19478              * Fires when this field hide.
19479              * @param {Roo.bootstrap.DateField} this
19480              * @param {Mixed} date The date value
19481              */
19482             hide : true,
19483             /**
19484              * @event select
19485              * Fires when select a date.
19486              * @param {Roo.bootstrap.DateField} this
19487              * @param {Mixed} date The date value
19488              */
19489             select : true
19490         });
19491 };
19492
19493 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19494     
19495     /**
19496      * @cfg {String} format
19497      * The default time format string which can be overriden for localization support.  The format must be
19498      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19499      */
19500     format : "H:i",
19501        
19502     onRender: function(ct, position)
19503     {
19504         
19505         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19506                 
19507         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19508         
19509         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19510         
19511         this.pop = this.picker().select('>.datepicker-time',true).first();
19512         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19513         
19514         this.picker().on('mousedown', this.onMousedown, this);
19515         this.picker().on('click', this.onClick, this);
19516         
19517         this.picker().addClass('datepicker-dropdown');
19518     
19519         this.fillTime();
19520         this.update();
19521             
19522         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19523         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19524         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19525         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19526         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19527         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19528
19529     },
19530     
19531     fireKey: function(e){
19532         if (!this.picker().isVisible()){
19533             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19534                 this.show();
19535             }
19536             return;
19537         }
19538
19539         e.preventDefault();
19540         
19541         switch(e.keyCode){
19542             case 27: // escape
19543                 this.hide();
19544                 break;
19545             case 37: // left
19546             case 39: // right
19547                 this.onTogglePeriod();
19548                 break;
19549             case 38: // up
19550                 this.onIncrementMinutes();
19551                 break;
19552             case 40: // down
19553                 this.onDecrementMinutes();
19554                 break;
19555             case 13: // enter
19556             case 9: // tab
19557                 this.setTime();
19558                 break;
19559         }
19560     },
19561     
19562     onClick: function(e) {
19563         e.stopPropagation();
19564         e.preventDefault();
19565     },
19566     
19567     picker : function()
19568     {
19569         return this.el.select('.datepicker', true).first();
19570     },
19571     
19572     fillTime: function()
19573     {    
19574         var time = this.pop.select('tbody', true).first();
19575         
19576         time.dom.innerHTML = '';
19577         
19578         time.createChild({
19579             tag: 'tr',
19580             cn: [
19581                 {
19582                     tag: 'td',
19583                     cn: [
19584                         {
19585                             tag: 'a',
19586                             href: '#',
19587                             cls: 'btn',
19588                             cn: [
19589                                 {
19590                                     tag: 'span',
19591                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19592                                 }
19593                             ]
19594                         } 
19595                     ]
19596                 },
19597                 {
19598                     tag: 'td',
19599                     cls: 'separator'
19600                 },
19601                 {
19602                     tag: 'td',
19603                     cn: [
19604                         {
19605                             tag: 'a',
19606                             href: '#',
19607                             cls: 'btn',
19608                             cn: [
19609                                 {
19610                                     tag: 'span',
19611                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19612                                 }
19613                             ]
19614                         }
19615                     ]
19616                 },
19617                 {
19618                     tag: 'td',
19619                     cls: 'separator'
19620                 }
19621             ]
19622         });
19623         
19624         time.createChild({
19625             tag: 'tr',
19626             cn: [
19627                 {
19628                     tag: 'td',
19629                     cn: [
19630                         {
19631                             tag: 'span',
19632                             cls: 'timepicker-hour',
19633                             html: '00'
19634                         }  
19635                     ]
19636                 },
19637                 {
19638                     tag: 'td',
19639                     cls: 'separator',
19640                     html: ':'
19641                 },
19642                 {
19643                     tag: 'td',
19644                     cn: [
19645                         {
19646                             tag: 'span',
19647                             cls: 'timepicker-minute',
19648                             html: '00'
19649                         }  
19650                     ]
19651                 },
19652                 {
19653                     tag: 'td',
19654                     cls: 'separator'
19655                 },
19656                 {
19657                     tag: 'td',
19658                     cn: [
19659                         {
19660                             tag: 'button',
19661                             type: 'button',
19662                             cls: 'btn btn-primary period',
19663                             html: 'AM'
19664                             
19665                         }
19666                     ]
19667                 }
19668             ]
19669         });
19670         
19671         time.createChild({
19672             tag: 'tr',
19673             cn: [
19674                 {
19675                     tag: 'td',
19676                     cn: [
19677                         {
19678                             tag: 'a',
19679                             href: '#',
19680                             cls: 'btn',
19681                             cn: [
19682                                 {
19683                                     tag: 'span',
19684                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19685                                 }
19686                             ]
19687                         }
19688                     ]
19689                 },
19690                 {
19691                     tag: 'td',
19692                     cls: 'separator'
19693                 },
19694                 {
19695                     tag: 'td',
19696                     cn: [
19697                         {
19698                             tag: 'a',
19699                             href: '#',
19700                             cls: 'btn',
19701                             cn: [
19702                                 {
19703                                     tag: 'span',
19704                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19705                                 }
19706                             ]
19707                         }
19708                     ]
19709                 },
19710                 {
19711                     tag: 'td',
19712                     cls: 'separator'
19713                 }
19714             ]
19715         });
19716         
19717     },
19718     
19719     update: function()
19720     {
19721         
19722         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19723         
19724         this.fill();
19725     },
19726     
19727     fill: function() 
19728     {
19729         var hours = this.time.getHours();
19730         var minutes = this.time.getMinutes();
19731         var period = 'AM';
19732         
19733         if(hours > 11){
19734             period = 'PM';
19735         }
19736         
19737         if(hours == 0){
19738             hours = 12;
19739         }
19740         
19741         
19742         if(hours > 12){
19743             hours = hours - 12;
19744         }
19745         
19746         if(hours < 10){
19747             hours = '0' + hours;
19748         }
19749         
19750         if(minutes < 10){
19751             minutes = '0' + minutes;
19752         }
19753         
19754         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19755         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19756         this.pop.select('button', true).first().dom.innerHTML = period;
19757         
19758     },
19759     
19760     place: function()
19761     {   
19762         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19763         
19764         var cls = ['bottom'];
19765         
19766         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19767             cls.pop();
19768             cls.push('top');
19769         }
19770         
19771         cls.push('right');
19772         
19773         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19774             cls.pop();
19775             cls.push('left');
19776         }
19777         
19778         this.picker().addClass(cls.join('-'));
19779         
19780         var _this = this;
19781         
19782         Roo.each(cls, function(c){
19783             if(c == 'bottom'){
19784                 _this.picker().setTop(_this.inputEl().getHeight());
19785                 return;
19786             }
19787             if(c == 'top'){
19788                 _this.picker().setTop(0 - _this.picker().getHeight());
19789                 return;
19790             }
19791             
19792             if(c == 'left'){
19793                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19794                 return;
19795             }
19796             if(c == 'right'){
19797                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19798                 return;
19799             }
19800         });
19801         
19802     },
19803   
19804     onFocus : function()
19805     {
19806         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19807         this.show();
19808     },
19809     
19810     onBlur : function()
19811     {
19812         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19813         this.hide();
19814     },
19815     
19816     show : function()
19817     {
19818         this.picker().show();
19819         this.pop.show();
19820         this.update();
19821         this.place();
19822         
19823         this.fireEvent('show', this, this.date);
19824     },
19825     
19826     hide : function()
19827     {
19828         this.picker().hide();
19829         this.pop.hide();
19830         
19831         this.fireEvent('hide', this, this.date);
19832     },
19833     
19834     setTime : function()
19835     {
19836         this.hide();
19837         this.setValue(this.time.format(this.format));
19838         
19839         this.fireEvent('select', this, this.date);
19840         
19841         
19842     },
19843     
19844     onMousedown: function(e){
19845         e.stopPropagation();
19846         e.preventDefault();
19847     },
19848     
19849     onIncrementHours: function()
19850     {
19851         Roo.log('onIncrementHours');
19852         this.time = this.time.add(Date.HOUR, 1);
19853         this.update();
19854         
19855     },
19856     
19857     onDecrementHours: function()
19858     {
19859         Roo.log('onDecrementHours');
19860         this.time = this.time.add(Date.HOUR, -1);
19861         this.update();
19862     },
19863     
19864     onIncrementMinutes: function()
19865     {
19866         Roo.log('onIncrementMinutes');
19867         this.time = this.time.add(Date.MINUTE, 1);
19868         this.update();
19869     },
19870     
19871     onDecrementMinutes: function()
19872     {
19873         Roo.log('onDecrementMinutes');
19874         this.time = this.time.add(Date.MINUTE, -1);
19875         this.update();
19876     },
19877     
19878     onTogglePeriod: function()
19879     {
19880         Roo.log('onTogglePeriod');
19881         this.time = this.time.add(Date.HOUR, 12);
19882         this.update();
19883     }
19884     
19885    
19886 });
19887
19888 Roo.apply(Roo.bootstrap.TimeField,  {
19889     
19890     content : {
19891         tag: 'tbody',
19892         cn: [
19893             {
19894                 tag: 'tr',
19895                 cn: [
19896                 {
19897                     tag: 'td',
19898                     colspan: '7'
19899                 }
19900                 ]
19901             }
19902         ]
19903     },
19904     
19905     footer : {
19906         tag: 'tfoot',
19907         cn: [
19908             {
19909                 tag: 'tr',
19910                 cn: [
19911                 {
19912                     tag: 'th',
19913                     colspan: '7',
19914                     cls: '',
19915                     cn: [
19916                         {
19917                             tag: 'button',
19918                             cls: 'btn btn-info ok',
19919                             html: 'OK'
19920                         }
19921                     ]
19922                 }
19923
19924                 ]
19925             }
19926         ]
19927     }
19928 });
19929
19930 Roo.apply(Roo.bootstrap.TimeField,  {
19931   
19932     template : {
19933         tag: 'div',
19934         cls: 'datepicker dropdown-menu',
19935         cn: [
19936             {
19937                 tag: 'div',
19938                 cls: 'datepicker-time',
19939                 cn: [
19940                 {
19941                     tag: 'table',
19942                     cls: 'table-condensed',
19943                     cn:[
19944                     Roo.bootstrap.TimeField.content,
19945                     Roo.bootstrap.TimeField.footer
19946                     ]
19947                 }
19948                 ]
19949             }
19950         ]
19951     }
19952 });
19953
19954  
19955
19956  /*
19957  * - LGPL
19958  *
19959  * MonthField
19960  * 
19961  */
19962
19963 /**
19964  * @class Roo.bootstrap.MonthField
19965  * @extends Roo.bootstrap.Input
19966  * Bootstrap MonthField class
19967  * 
19968  * @cfg {String} language default en
19969  * 
19970  * @constructor
19971  * Create a new MonthField
19972  * @param {Object} config The config object
19973  */
19974
19975 Roo.bootstrap.MonthField = function(config){
19976     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19977     
19978     this.addEvents({
19979         /**
19980          * @event show
19981          * Fires when this field show.
19982          * @param {Roo.bootstrap.MonthField} this
19983          * @param {Mixed} date The date value
19984          */
19985         show : true,
19986         /**
19987          * @event show
19988          * Fires when this field hide.
19989          * @param {Roo.bootstrap.MonthField} this
19990          * @param {Mixed} date The date value
19991          */
19992         hide : true,
19993         /**
19994          * @event select
19995          * Fires when select a date.
19996          * @param {Roo.bootstrap.MonthField} this
19997          * @param {String} oldvalue The old value
19998          * @param {String} newvalue The new value
19999          */
20000         select : true
20001     });
20002 };
20003
20004 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20005     
20006     onRender: function(ct, position)
20007     {
20008         
20009         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20010         
20011         this.language = this.language || 'en';
20012         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20013         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20014         
20015         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20016         this.isInline = false;
20017         this.isInput = true;
20018         this.component = this.el.select('.add-on', true).first() || false;
20019         this.component = (this.component && this.component.length === 0) ? false : this.component;
20020         this.hasInput = this.component && this.inputEL().length;
20021         
20022         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20023         
20024         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20025         
20026         this.picker().on('mousedown', this.onMousedown, this);
20027         this.picker().on('click', this.onClick, this);
20028         
20029         this.picker().addClass('datepicker-dropdown');
20030         
20031         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20032             v.setStyle('width', '189px');
20033         });
20034         
20035         this.fillMonths();
20036         
20037         this.update();
20038         
20039         if(this.isInline) {
20040             this.show();
20041         }
20042         
20043     },
20044     
20045     setValue: function(v, suppressEvent)
20046     {   
20047         var o = this.getValue();
20048         
20049         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20050         
20051         this.update();
20052
20053         if(suppressEvent !== true){
20054             this.fireEvent('select', this, o, v);
20055         }
20056         
20057     },
20058     
20059     getValue: function()
20060     {
20061         return this.value;
20062     },
20063     
20064     onClick: function(e) 
20065     {
20066         e.stopPropagation();
20067         e.preventDefault();
20068         
20069         var target = e.getTarget();
20070         
20071         if(target.nodeName.toLowerCase() === 'i'){
20072             target = Roo.get(target).dom.parentNode;
20073         }
20074         
20075         var nodeName = target.nodeName;
20076         var className = target.className;
20077         var html = target.innerHTML;
20078         
20079         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20080             return;
20081         }
20082         
20083         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20084         
20085         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20086         
20087         this.hide();
20088                         
20089     },
20090     
20091     picker : function()
20092     {
20093         return this.pickerEl;
20094     },
20095     
20096     fillMonths: function()
20097     {    
20098         var i = 0;
20099         var months = this.picker().select('>.datepicker-months td', true).first();
20100         
20101         months.dom.innerHTML = '';
20102         
20103         while (i < 12) {
20104             var month = {
20105                 tag: 'span',
20106                 cls: 'month',
20107                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20108             };
20109             
20110             months.createChild(month);
20111         }
20112         
20113     },
20114     
20115     update: function()
20116     {
20117         var _this = this;
20118         
20119         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20120             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20121         }
20122         
20123         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20124             e.removeClass('active');
20125             
20126             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20127                 e.addClass('active');
20128             }
20129         })
20130     },
20131     
20132     place: function()
20133     {
20134         if(this.isInline) {
20135             return;
20136         }
20137         
20138         this.picker().removeClass(['bottom', 'top']);
20139         
20140         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20141             /*
20142              * place to the top of element!
20143              *
20144              */
20145             
20146             this.picker().addClass('top');
20147             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20148             
20149             return;
20150         }
20151         
20152         this.picker().addClass('bottom');
20153         
20154         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20155     },
20156     
20157     onFocus : function()
20158     {
20159         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20160         this.show();
20161     },
20162     
20163     onBlur : function()
20164     {
20165         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20166         
20167         var d = this.inputEl().getValue();
20168         
20169         this.setValue(d);
20170                 
20171         this.hide();
20172     },
20173     
20174     show : function()
20175     {
20176         this.picker().show();
20177         this.picker().select('>.datepicker-months', true).first().show();
20178         this.update();
20179         this.place();
20180         
20181         this.fireEvent('show', this, this.date);
20182     },
20183     
20184     hide : function()
20185     {
20186         if(this.isInline) {
20187             return;
20188         }
20189         this.picker().hide();
20190         this.fireEvent('hide', this, this.date);
20191         
20192     },
20193     
20194     onMousedown: function(e)
20195     {
20196         e.stopPropagation();
20197         e.preventDefault();
20198     },
20199     
20200     keyup: function(e)
20201     {
20202         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20203         this.update();
20204     },
20205
20206     fireKey: function(e)
20207     {
20208         if (!this.picker().isVisible()){
20209             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20210                 this.show();
20211             }
20212             return;
20213         }
20214         
20215         var dir;
20216         
20217         switch(e.keyCode){
20218             case 27: // escape
20219                 this.hide();
20220                 e.preventDefault();
20221                 break;
20222             case 37: // left
20223             case 39: // right
20224                 dir = e.keyCode == 37 ? -1 : 1;
20225                 
20226                 this.vIndex = this.vIndex + dir;
20227                 
20228                 if(this.vIndex < 0){
20229                     this.vIndex = 0;
20230                 }
20231                 
20232                 if(this.vIndex > 11){
20233                     this.vIndex = 11;
20234                 }
20235                 
20236                 if(isNaN(this.vIndex)){
20237                     this.vIndex = 0;
20238                 }
20239                 
20240                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20241                 
20242                 break;
20243             case 38: // up
20244             case 40: // down
20245                 
20246                 dir = e.keyCode == 38 ? -1 : 1;
20247                 
20248                 this.vIndex = this.vIndex + dir * 4;
20249                 
20250                 if(this.vIndex < 0){
20251                     this.vIndex = 0;
20252                 }
20253                 
20254                 if(this.vIndex > 11){
20255                     this.vIndex = 11;
20256                 }
20257                 
20258                 if(isNaN(this.vIndex)){
20259                     this.vIndex = 0;
20260                 }
20261                 
20262                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20263                 break;
20264                 
20265             case 13: // enter
20266                 
20267                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20268                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20269                 }
20270                 
20271                 this.hide();
20272                 e.preventDefault();
20273                 break;
20274             case 9: // tab
20275                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20276                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20277                 }
20278                 this.hide();
20279                 break;
20280             case 16: // shift
20281             case 17: // ctrl
20282             case 18: // alt
20283                 break;
20284             default :
20285                 this.hide();
20286                 
20287         }
20288     },
20289     
20290     remove: function() 
20291     {
20292         this.picker().remove();
20293     }
20294    
20295 });
20296
20297 Roo.apply(Roo.bootstrap.MonthField,  {
20298     
20299     content : {
20300         tag: 'tbody',
20301         cn: [
20302         {
20303             tag: 'tr',
20304             cn: [
20305             {
20306                 tag: 'td',
20307                 colspan: '7'
20308             }
20309             ]
20310         }
20311         ]
20312     },
20313     
20314     dates:{
20315         en: {
20316             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20317             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20318         }
20319     }
20320 });
20321
20322 Roo.apply(Roo.bootstrap.MonthField,  {
20323   
20324     template : {
20325         tag: 'div',
20326         cls: 'datepicker dropdown-menu roo-dynamic',
20327         cn: [
20328             {
20329                 tag: 'div',
20330                 cls: 'datepicker-months',
20331                 cn: [
20332                 {
20333                     tag: 'table',
20334                     cls: 'table-condensed',
20335                     cn:[
20336                         Roo.bootstrap.DateField.content
20337                     ]
20338                 }
20339                 ]
20340             }
20341         ]
20342     }
20343 });
20344
20345  
20346
20347  
20348  /*
20349  * - LGPL
20350  *
20351  * CheckBox
20352  * 
20353  */
20354
20355 /**
20356  * @class Roo.bootstrap.CheckBox
20357  * @extends Roo.bootstrap.Input
20358  * Bootstrap CheckBox class
20359  * 
20360  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20361  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20362  * @cfg {String} boxLabel The text that appears beside the checkbox
20363  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20364  * @cfg {Boolean} checked initnal the element
20365  * @cfg {Boolean} inline inline the element (default false)
20366  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20367  * @cfg {String} tooltip label tooltip
20368  * 
20369  * @constructor
20370  * Create a new CheckBox
20371  * @param {Object} config The config object
20372  */
20373
20374 Roo.bootstrap.CheckBox = function(config){
20375     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20376    
20377     this.addEvents({
20378         /**
20379         * @event check
20380         * Fires when the element is checked or unchecked.
20381         * @param {Roo.bootstrap.CheckBox} this This input
20382         * @param {Boolean} checked The new checked value
20383         */
20384        check : true,
20385        /**
20386         * @event click
20387         * Fires when the element is click.
20388         * @param {Roo.bootstrap.CheckBox} this This input
20389         */
20390        click : true
20391     });
20392     
20393 };
20394
20395 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20396   
20397     inputType: 'checkbox',
20398     inputValue: 1,
20399     valueOff: 0,
20400     boxLabel: false,
20401     checked: false,
20402     weight : false,
20403     inline: false,
20404     tooltip : '',
20405     useFontAwesomeCheckBox : false,
20406     
20407     getAutoCreate : function()
20408     {
20409         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20410         
20411         var id = Roo.id();
20412         
20413         var cfg = {};
20414         
20415         cfg.cls = 'form-group ' + this.inputType; //input-group
20416         
20417         if(this.inline){
20418             cfg.cls += ' ' + this.inputType + '-inline';
20419         }
20420         
20421         var input =  {
20422             tag: 'input',
20423             id : id,
20424             type : this.inputType,
20425             value : this.inputValue,
20426             cls : 'roo-' + this.inputType, //'form-box',
20427             placeholder : this.placeholder || ''
20428             
20429         };
20430         
20431         if(this.inputType != 'radio'){
20432             var hidden =  {
20433                 tag: 'input',
20434                 type : 'hidden',
20435                 cls : 'roo-hidden-value',
20436                 value : this.checked ? this.inputValue : this.valueOff
20437             };
20438         }
20439         
20440             
20441         if (this.weight) { // Validity check?
20442             cfg.cls += " " + this.inputType + "-" + this.weight;
20443         }
20444         
20445         if (this.disabled) {
20446             input.disabled=true;
20447         }
20448         
20449         if(this.checked){
20450             input.checked = this.checked;
20451         }
20452         
20453         if (this.name) {
20454             
20455             input.name = this.name;
20456             
20457             if(this.inputType != 'radio'){
20458                 hidden.name = this.name;
20459                 input.name = '_hidden_' + this.name;
20460             }
20461         }
20462         
20463         if (this.size) {
20464             input.cls += ' input-' + this.size;
20465         }
20466         
20467         var settings=this;
20468         
20469         ['xs','sm','md','lg'].map(function(size){
20470             if (settings[size]) {
20471                 cfg.cls += ' col-' + size + '-' + settings[size];
20472             }
20473         });
20474         
20475         var inputblock = input;
20476          
20477         if (this.before || this.after) {
20478             
20479             inputblock = {
20480                 cls : 'input-group',
20481                 cn :  [] 
20482             };
20483             
20484             if (this.before) {
20485                 inputblock.cn.push({
20486                     tag :'span',
20487                     cls : 'input-group-addon',
20488                     html : this.before
20489                 });
20490             }
20491             
20492             inputblock.cn.push(input);
20493             
20494             if(this.inputType != 'radio'){
20495                 inputblock.cn.push(hidden);
20496             }
20497             
20498             if (this.after) {
20499                 inputblock.cn.push({
20500                     tag :'span',
20501                     cls : 'input-group-addon',
20502                     html : this.after
20503                 });
20504             }
20505             
20506         }
20507         
20508         if (align ==='left' && this.fieldLabel.length) {
20509 //                Roo.log("left and has label");
20510             cfg.cn = [
20511                 {
20512                     tag: 'label',
20513                     'for' :  id,
20514                     cls : 'control-label',
20515                     html : this.fieldLabel
20516                 },
20517                 {
20518                     cls : "", 
20519                     cn: [
20520                         inputblock
20521                     ]
20522                 }
20523             ];
20524             
20525             if(this.labelWidth > 12){
20526                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20527             }
20528             
20529             if(this.labelWidth < 13 && this.labelmd == 0){
20530                 this.labelmd = this.labelWidth;
20531             }
20532             
20533             if(this.labellg > 0){
20534                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20535                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20536             }
20537             
20538             if(this.labelmd > 0){
20539                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20540                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20541             }
20542             
20543             if(this.labelsm > 0){
20544                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20545                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20546             }
20547             
20548             if(this.labelxs > 0){
20549                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20550                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20551             }
20552             
20553         } else if ( this.fieldLabel.length) {
20554 //                Roo.log(" label");
20555                 cfg.cn = [
20556                    
20557                     {
20558                         tag: this.boxLabel ? 'span' : 'label',
20559                         'for': id,
20560                         cls: 'control-label box-input-label',
20561                         //cls : 'input-group-addon',
20562                         html : this.fieldLabel
20563                     },
20564                     
20565                     inputblock
20566                     
20567                 ];
20568
20569         } else {
20570             
20571 //                Roo.log(" no label && no align");
20572                 cfg.cn = [  inputblock ] ;
20573                 
20574                 
20575         }
20576         
20577         if(this.boxLabel){
20578              var boxLabelCfg = {
20579                 tag: 'label',
20580                 //'for': id, // box label is handled by onclick - so no for...
20581                 cls: 'box-label',
20582                 html: this.boxLabel
20583             };
20584             
20585             if(this.useFontAwesomeCheckBox) {
20586                 boxLabelCfg.cls = 'box-label fa-checkbox'
20587             }
20588             
20589             if(this.tooltip){
20590                 boxLabelCfg.tooltip = this.tooltip;
20591             }
20592              
20593             cfg.cn.push(boxLabelCfg);
20594         }
20595         
20596         if(this.inputType != 'radio'){
20597             cfg.cn.push(hidden);
20598         }
20599         
20600         return cfg;
20601         
20602     },
20603     
20604     /**
20605      * return the real input element.
20606      */
20607     inputEl: function ()
20608     {
20609         return this.el.select('input.roo-' + this.inputType,true).first();
20610     },
20611     hiddenEl: function ()
20612     {
20613         return this.el.select('input.roo-hidden-value',true).first();
20614     },
20615     
20616     labelEl: function()
20617     {
20618         return this.el.select('label.control-label',true).first();
20619     },
20620     /* depricated... */
20621     
20622     label: function()
20623     {
20624         return this.labelEl();
20625     },
20626     
20627     boxLabelEl: function()
20628     {
20629         return this.el.select('label.box-label',true).first();
20630     },
20631     
20632     initEvents : function()
20633     {
20634 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20635         
20636         this.inputEl().on('click', this.onClick,  this);
20637         
20638         if (this.boxLabel) { 
20639             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20640         }
20641         
20642         this.startValue = this.getValue();
20643         
20644         if(this.groupId){
20645             Roo.bootstrap.CheckBox.register(this);
20646         }
20647     },
20648     
20649     onClick : function(e)
20650     {   
20651         if(this.fireEvent('click', this, e) !== false){
20652             this.setChecked(!this.checked);
20653         }
20654         
20655     },
20656     
20657     setChecked : function(state,suppressEvent)
20658     {
20659         this.startValue = this.getValue();
20660
20661         if(this.inputType == 'radio'){
20662             
20663             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20664                 e.dom.checked = false;
20665             });
20666             
20667             this.inputEl().dom.checked = true;
20668             
20669             this.inputEl().dom.value = this.inputValue;
20670             
20671             if(suppressEvent !== true){
20672                 this.fireEvent('check', this, true);
20673             }
20674             
20675             this.validate();
20676             
20677             return;
20678         }
20679         
20680         this.checked = state;
20681         
20682         this.inputEl().dom.checked = state;
20683         
20684         
20685         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20686         
20687         if(suppressEvent !== true){
20688             this.fireEvent('check', this, state);
20689         }
20690         
20691         this.validate();
20692     },
20693     
20694     getValue : function()
20695     {
20696         if(this.inputType == 'radio'){
20697             return this.getGroupValue();
20698         }
20699         
20700         return this.hiddenEl().dom.value;
20701         
20702     },
20703     
20704     getGroupValue : function()
20705     {
20706         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20707             return '';
20708         }
20709         
20710         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20711     },
20712     
20713     setValue : function(v,suppressEvent)
20714     {
20715         if(this.inputType == 'radio'){
20716             this.setGroupValue(v, suppressEvent);
20717             return;
20718         }
20719         
20720         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20721         
20722         this.validate();
20723     },
20724     
20725     setGroupValue : function(v, suppressEvent)
20726     {
20727         this.startValue = this.getValue();
20728         
20729         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20730             e.dom.checked = false;
20731             
20732             if(e.dom.value == v){
20733                 e.dom.checked = true;
20734             }
20735         });
20736         
20737         if(suppressEvent !== true){
20738             this.fireEvent('check', this, true);
20739         }
20740
20741         this.validate();
20742         
20743         return;
20744     },
20745     
20746     validate : function()
20747     {
20748         if(this.getVisibilityEl().hasClass('hidden')){
20749             return true;
20750         }
20751         
20752         if(
20753                 this.disabled || 
20754                 (this.inputType == 'radio' && this.validateRadio()) ||
20755                 (this.inputType == 'checkbox' && this.validateCheckbox())
20756         ){
20757             this.markValid();
20758             return true;
20759         }
20760         
20761         this.markInvalid();
20762         return false;
20763     },
20764     
20765     validateRadio : function()
20766     {
20767         if(this.getVisibilityEl().hasClass('hidden')){
20768             return true;
20769         }
20770         
20771         if(this.allowBlank){
20772             return true;
20773         }
20774         
20775         var valid = false;
20776         
20777         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20778             if(!e.dom.checked){
20779                 return;
20780             }
20781             
20782             valid = true;
20783             
20784             return false;
20785         });
20786         
20787         return valid;
20788     },
20789     
20790     validateCheckbox : function()
20791     {
20792         if(!this.groupId){
20793             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20794             //return (this.getValue() == this.inputValue) ? true : false;
20795         }
20796         
20797         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20798         
20799         if(!group){
20800             return false;
20801         }
20802         
20803         var r = false;
20804         
20805         for(var i in group){
20806             if(group[i].el.isVisible(true)){
20807                 r = false;
20808                 break;
20809             }
20810             
20811             r = true;
20812         }
20813         
20814         for(var i in group){
20815             if(r){
20816                 break;
20817             }
20818             
20819             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20820         }
20821         
20822         return r;
20823     },
20824     
20825     /**
20826      * Mark this field as valid
20827      */
20828     markValid : function()
20829     {
20830         var _this = this;
20831         
20832         this.fireEvent('valid', this);
20833         
20834         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20835         
20836         if(this.groupId){
20837             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20838         }
20839         
20840         if(label){
20841             label.markValid();
20842         }
20843
20844         if(this.inputType == 'radio'){
20845             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20846                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20847                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20848             });
20849             
20850             return;
20851         }
20852
20853         if(!this.groupId){
20854             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20855             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20856             return;
20857         }
20858         
20859         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20860         
20861         if(!group){
20862             return;
20863         }
20864         
20865         for(var i in group){
20866             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20867             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20868         }
20869     },
20870     
20871      /**
20872      * Mark this field as invalid
20873      * @param {String} msg The validation message
20874      */
20875     markInvalid : function(msg)
20876     {
20877         if(this.allowBlank){
20878             return;
20879         }
20880         
20881         var _this = this;
20882         
20883         this.fireEvent('invalid', this, msg);
20884         
20885         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20886         
20887         if(this.groupId){
20888             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20889         }
20890         
20891         if(label){
20892             label.markInvalid();
20893         }
20894             
20895         if(this.inputType == 'radio'){
20896             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20897                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20898                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20899             });
20900             
20901             return;
20902         }
20903         
20904         if(!this.groupId){
20905             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20906             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20907             return;
20908         }
20909         
20910         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20911         
20912         if(!group){
20913             return;
20914         }
20915         
20916         for(var i in group){
20917             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20918             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20919         }
20920         
20921     },
20922     
20923     clearInvalid : function()
20924     {
20925         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20926         
20927         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20928         
20929         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20930         
20931         if (label && label.iconEl) {
20932             label.iconEl.removeClass(label.validClass);
20933             label.iconEl.removeClass(label.invalidClass);
20934         }
20935     },
20936     
20937     disable : function()
20938     {
20939         if(this.inputType != 'radio'){
20940             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20941             return;
20942         }
20943         
20944         var _this = this;
20945         
20946         if(this.rendered){
20947             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20948                 _this.getActionEl().addClass(this.disabledClass);
20949                 e.dom.disabled = true;
20950             });
20951         }
20952         
20953         this.disabled = true;
20954         this.fireEvent("disable", this);
20955         return this;
20956     },
20957
20958     enable : function()
20959     {
20960         if(this.inputType != 'radio'){
20961             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20962             return;
20963         }
20964         
20965         var _this = this;
20966         
20967         if(this.rendered){
20968             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20969                 _this.getActionEl().removeClass(this.disabledClass);
20970                 e.dom.disabled = false;
20971             });
20972         }
20973         
20974         this.disabled = false;
20975         this.fireEvent("enable", this);
20976         return this;
20977     },
20978     
20979     setBoxLabel : function(v)
20980     {
20981         this.boxLabel = v;
20982         
20983         if(this.rendered){
20984             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20985         }
20986     }
20987
20988 });
20989
20990 Roo.apply(Roo.bootstrap.CheckBox, {
20991     
20992     groups: {},
20993     
20994      /**
20995     * register a CheckBox Group
20996     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20997     */
20998     register : function(checkbox)
20999     {
21000         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21001             this.groups[checkbox.groupId] = {};
21002         }
21003         
21004         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21005             return;
21006         }
21007         
21008         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21009         
21010     },
21011     /**
21012     * fetch a CheckBox Group based on the group ID
21013     * @param {string} the group ID
21014     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21015     */
21016     get: function(groupId) {
21017         if (typeof(this.groups[groupId]) == 'undefined') {
21018             return false;
21019         }
21020         
21021         return this.groups[groupId] ;
21022     }
21023     
21024     
21025 });
21026 /*
21027  * - LGPL
21028  *
21029  * RadioItem
21030  * 
21031  */
21032
21033 /**
21034  * @class Roo.bootstrap.Radio
21035  * @extends Roo.bootstrap.Component
21036  * Bootstrap Radio class
21037  * @cfg {String} boxLabel - the label associated
21038  * @cfg {String} value - the value of radio
21039  * 
21040  * @constructor
21041  * Create a new Radio
21042  * @param {Object} config The config object
21043  */
21044 Roo.bootstrap.Radio = function(config){
21045     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21046     
21047 };
21048
21049 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21050     
21051     boxLabel : '',
21052     
21053     value : '',
21054     
21055     getAutoCreate : function()
21056     {
21057         var cfg = {
21058             tag : 'div',
21059             cls : 'form-group radio',
21060             cn : [
21061                 {
21062                     tag : 'label',
21063                     cls : 'box-label',
21064                     html : this.boxLabel
21065                 }
21066             ]
21067         };
21068         
21069         return cfg;
21070     },
21071     
21072     initEvents : function() 
21073     {
21074         this.parent().register(this);
21075         
21076         this.el.on('click', this.onClick, this);
21077         
21078     },
21079     
21080     onClick : function(e)
21081     {
21082         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21083             this.setChecked(true);
21084         }
21085     },
21086     
21087     setChecked : function(state, suppressEvent)
21088     {
21089         this.parent().setValue(this.value, suppressEvent);
21090         
21091     },
21092     
21093     setBoxLabel : function(v)
21094     {
21095         this.boxLabel = v;
21096         
21097         if(this.rendered){
21098             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21099         }
21100     }
21101     
21102 });
21103  
21104
21105  /*
21106  * - LGPL
21107  *
21108  * Input
21109  * 
21110  */
21111
21112 /**
21113  * @class Roo.bootstrap.SecurePass
21114  * @extends Roo.bootstrap.Input
21115  * Bootstrap SecurePass class
21116  *
21117  * 
21118  * @constructor
21119  * Create a new SecurePass
21120  * @param {Object} config The config object
21121  */
21122  
21123 Roo.bootstrap.SecurePass = function (config) {
21124     // these go here, so the translation tool can replace them..
21125     this.errors = {
21126         PwdEmpty: "Please type a password, and then retype it to confirm.",
21127         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21128         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21129         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21130         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21131         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21132         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21133         TooWeak: "Your password is Too Weak."
21134     },
21135     this.meterLabel = "Password strength:";
21136     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21137     this.meterClass = [
21138         "roo-password-meter-tooweak", 
21139         "roo-password-meter-weak", 
21140         "roo-password-meter-medium", 
21141         "roo-password-meter-strong", 
21142         "roo-password-meter-grey"
21143     ];
21144     
21145     this.errors = {};
21146     
21147     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21148 }
21149
21150 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21151     /**
21152      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21153      * {
21154      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21155      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21156      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21157      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21158      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21159      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21160      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21161      * })
21162      */
21163     // private
21164     
21165     meterWidth: 300,
21166     errorMsg :'',    
21167     errors: false,
21168     imageRoot: '/',
21169     /**
21170      * @cfg {String/Object} Label for the strength meter (defaults to
21171      * 'Password strength:')
21172      */
21173     // private
21174     meterLabel: '',
21175     /**
21176      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21177      * ['Weak', 'Medium', 'Strong'])
21178      */
21179     // private    
21180     pwdStrengths: false,    
21181     // private
21182     strength: 0,
21183     // private
21184     _lastPwd: null,
21185     // private
21186     kCapitalLetter: 0,
21187     kSmallLetter: 1,
21188     kDigit: 2,
21189     kPunctuation: 3,
21190     
21191     insecure: false,
21192     // private
21193     initEvents: function ()
21194     {
21195         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21196
21197         if (this.el.is('input[type=password]') && Roo.isSafari) {
21198             this.el.on('keydown', this.SafariOnKeyDown, this);
21199         }
21200
21201         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21202     },
21203     // private
21204     onRender: function (ct, position)
21205     {
21206         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21207         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21208         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21209
21210         this.trigger.createChild({
21211                    cn: [
21212                     {
21213                     //id: 'PwdMeter',
21214                     tag: 'div',
21215                     cls: 'roo-password-meter-grey col-xs-12',
21216                     style: {
21217                         //width: 0,
21218                         //width: this.meterWidth + 'px'                                                
21219                         }
21220                     },
21221                     {                            
21222                          cls: 'roo-password-meter-text'                          
21223                     }
21224                 ]            
21225         });
21226
21227          
21228         if (this.hideTrigger) {
21229             this.trigger.setDisplayed(false);
21230         }
21231         this.setSize(this.width || '', this.height || '');
21232     },
21233     // private
21234     onDestroy: function ()
21235     {
21236         if (this.trigger) {
21237             this.trigger.removeAllListeners();
21238             this.trigger.remove();
21239         }
21240         if (this.wrap) {
21241             this.wrap.remove();
21242         }
21243         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21244     },
21245     // private
21246     checkStrength: function ()
21247     {
21248         var pwd = this.inputEl().getValue();
21249         if (pwd == this._lastPwd) {
21250             return;
21251         }
21252
21253         var strength;
21254         if (this.ClientSideStrongPassword(pwd)) {
21255             strength = 3;
21256         } else if (this.ClientSideMediumPassword(pwd)) {
21257             strength = 2;
21258         } else if (this.ClientSideWeakPassword(pwd)) {
21259             strength = 1;
21260         } else {
21261             strength = 0;
21262         }
21263         
21264         Roo.log('strength1: ' + strength);
21265         
21266         //var pm = this.trigger.child('div/div/div').dom;
21267         var pm = this.trigger.child('div/div');
21268         pm.removeClass(this.meterClass);
21269         pm.addClass(this.meterClass[strength]);
21270                 
21271         
21272         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21273                 
21274         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21275         
21276         this._lastPwd = pwd;
21277     },
21278     reset: function ()
21279     {
21280         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21281         
21282         this._lastPwd = '';
21283         
21284         var pm = this.trigger.child('div/div');
21285         pm.removeClass(this.meterClass);
21286         pm.addClass('roo-password-meter-grey');        
21287         
21288         
21289         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21290         
21291         pt.innerHTML = '';
21292         this.inputEl().dom.type='password';
21293     },
21294     // private
21295     validateValue: function (value)
21296     {
21297         
21298         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21299             return false;
21300         }
21301         if (value.length == 0) {
21302             if (this.allowBlank) {
21303                 this.clearInvalid();
21304                 return true;
21305             }
21306
21307             this.markInvalid(this.errors.PwdEmpty);
21308             this.errorMsg = this.errors.PwdEmpty;
21309             return false;
21310         }
21311         
21312         if(this.insecure){
21313             return true;
21314         }
21315         
21316         if ('[\x21-\x7e]*'.match(value)) {
21317             this.markInvalid(this.errors.PwdBadChar);
21318             this.errorMsg = this.errors.PwdBadChar;
21319             return false;
21320         }
21321         if (value.length < 6) {
21322             this.markInvalid(this.errors.PwdShort);
21323             this.errorMsg = this.errors.PwdShort;
21324             return false;
21325         }
21326         if (value.length > 16) {
21327             this.markInvalid(this.errors.PwdLong);
21328             this.errorMsg = this.errors.PwdLong;
21329             return false;
21330         }
21331         var strength;
21332         if (this.ClientSideStrongPassword(value)) {
21333             strength = 3;
21334         } else if (this.ClientSideMediumPassword(value)) {
21335             strength = 2;
21336         } else if (this.ClientSideWeakPassword(value)) {
21337             strength = 1;
21338         } else {
21339             strength = 0;
21340         }
21341
21342         
21343         if (strength < 2) {
21344             //this.markInvalid(this.errors.TooWeak);
21345             this.errorMsg = this.errors.TooWeak;
21346             //return false;
21347         }
21348         
21349         
21350         console.log('strength2: ' + strength);
21351         
21352         //var pm = this.trigger.child('div/div/div').dom;
21353         
21354         var pm = this.trigger.child('div/div');
21355         pm.removeClass(this.meterClass);
21356         pm.addClass(this.meterClass[strength]);
21357                 
21358         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21359                 
21360         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21361         
21362         this.errorMsg = ''; 
21363         return true;
21364     },
21365     // private
21366     CharacterSetChecks: function (type)
21367     {
21368         this.type = type;
21369         this.fResult = false;
21370     },
21371     // private
21372     isctype: function (character, type)
21373     {
21374         switch (type) {  
21375             case this.kCapitalLetter:
21376                 if (character >= 'A' && character <= 'Z') {
21377                     return true;
21378                 }
21379                 break;
21380             
21381             case this.kSmallLetter:
21382                 if (character >= 'a' && character <= 'z') {
21383                     return true;
21384                 }
21385                 break;
21386             
21387             case this.kDigit:
21388                 if (character >= '0' && character <= '9') {
21389                     return true;
21390                 }
21391                 break;
21392             
21393             case this.kPunctuation:
21394                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21395                     return true;
21396                 }
21397                 break;
21398             
21399             default:
21400                 return false;
21401         }
21402
21403     },
21404     // private
21405     IsLongEnough: function (pwd, size)
21406     {
21407         return !(pwd == null || isNaN(size) || pwd.length < size);
21408     },
21409     // private
21410     SpansEnoughCharacterSets: function (word, nb)
21411     {
21412         if (!this.IsLongEnough(word, nb))
21413         {
21414             return false;
21415         }
21416
21417         var characterSetChecks = new Array(
21418             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21419             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21420         );
21421         
21422         for (var index = 0; index < word.length; ++index) {
21423             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21424                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21425                     characterSetChecks[nCharSet].fResult = true;
21426                     break;
21427                 }
21428             }
21429         }
21430
21431         var nCharSets = 0;
21432         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21433             if (characterSetChecks[nCharSet].fResult) {
21434                 ++nCharSets;
21435             }
21436         }
21437
21438         if (nCharSets < nb) {
21439             return false;
21440         }
21441         return true;
21442     },
21443     // private
21444     ClientSideStrongPassword: function (pwd)
21445     {
21446         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21447     },
21448     // private
21449     ClientSideMediumPassword: function (pwd)
21450     {
21451         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21452     },
21453     // private
21454     ClientSideWeakPassword: function (pwd)
21455     {
21456         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21457     }
21458           
21459 })//<script type="text/javascript">
21460
21461 /*
21462  * Based  Ext JS Library 1.1.1
21463  * Copyright(c) 2006-2007, Ext JS, LLC.
21464  * LGPL
21465  *
21466  */
21467  
21468 /**
21469  * @class Roo.HtmlEditorCore
21470  * @extends Roo.Component
21471  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21472  *
21473  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21474  */
21475
21476 Roo.HtmlEditorCore = function(config){
21477     
21478     
21479     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21480     
21481     
21482     this.addEvents({
21483         /**
21484          * @event initialize
21485          * Fires when the editor is fully initialized (including the iframe)
21486          * @param {Roo.HtmlEditorCore} this
21487          */
21488         initialize: true,
21489         /**
21490          * @event activate
21491          * Fires when the editor is first receives the focus. Any insertion must wait
21492          * until after this event.
21493          * @param {Roo.HtmlEditorCore} this
21494          */
21495         activate: true,
21496          /**
21497          * @event beforesync
21498          * Fires before the textarea is updated with content from the editor iframe. Return false
21499          * to cancel the sync.
21500          * @param {Roo.HtmlEditorCore} this
21501          * @param {String} html
21502          */
21503         beforesync: true,
21504          /**
21505          * @event beforepush
21506          * Fires before the iframe editor is updated with content from the textarea. Return false
21507          * to cancel the push.
21508          * @param {Roo.HtmlEditorCore} this
21509          * @param {String} html
21510          */
21511         beforepush: true,
21512          /**
21513          * @event sync
21514          * Fires when the textarea is updated with content from the editor iframe.
21515          * @param {Roo.HtmlEditorCore} this
21516          * @param {String} html
21517          */
21518         sync: true,
21519          /**
21520          * @event push
21521          * Fires when the iframe editor is updated with content from the textarea.
21522          * @param {Roo.HtmlEditorCore} this
21523          * @param {String} html
21524          */
21525         push: true,
21526         
21527         /**
21528          * @event editorevent
21529          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21530          * @param {Roo.HtmlEditorCore} this
21531          */
21532         editorevent: true
21533         
21534     });
21535     
21536     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21537     
21538     // defaults : white / black...
21539     this.applyBlacklists();
21540     
21541     
21542     
21543 };
21544
21545
21546 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21547
21548
21549      /**
21550      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21551      */
21552     
21553     owner : false,
21554     
21555      /**
21556      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21557      *                        Roo.resizable.
21558      */
21559     resizable : false,
21560      /**
21561      * @cfg {Number} height (in pixels)
21562      */   
21563     height: 300,
21564    /**
21565      * @cfg {Number} width (in pixels)
21566      */   
21567     width: 500,
21568     
21569     /**
21570      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21571      * 
21572      */
21573     stylesheets: false,
21574     
21575     // id of frame..
21576     frameId: false,
21577     
21578     // private properties
21579     validationEvent : false,
21580     deferHeight: true,
21581     initialized : false,
21582     activated : false,
21583     sourceEditMode : false,
21584     onFocus : Roo.emptyFn,
21585     iframePad:3,
21586     hideMode:'offsets',
21587     
21588     clearUp: true,
21589     
21590     // blacklist + whitelisted elements..
21591     black: false,
21592     white: false,
21593      
21594     bodyCls : '',
21595
21596     /**
21597      * Protected method that will not generally be called directly. It
21598      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21599      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21600      */
21601     getDocMarkup : function(){
21602         // body styles..
21603         var st = '';
21604         
21605         // inherit styels from page...?? 
21606         if (this.stylesheets === false) {
21607             
21608             Roo.get(document.head).select('style').each(function(node) {
21609                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21610             });
21611             
21612             Roo.get(document.head).select('link').each(function(node) { 
21613                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21614             });
21615             
21616         } else if (!this.stylesheets.length) {
21617                 // simple..
21618                 st = '<style type="text/css">' +
21619                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21620                    '</style>';
21621         } else { 
21622             st = '<style type="text/css">' +
21623                     this.stylesheets +
21624                 '</style>';
21625         }
21626         
21627         st +=  '<style type="text/css">' +
21628             'IMG { cursor: pointer } ' +
21629         '</style>';
21630
21631         var cls = 'roo-htmleditor-body';
21632         
21633         if(this.bodyCls.length){
21634             cls += ' ' + this.bodyCls;
21635         }
21636         
21637         return '<html><head>' + st  +
21638             //<style type="text/css">' +
21639             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21640             //'</style>' +
21641             ' </head><body class="' +  cls + '"></body></html>';
21642     },
21643
21644     // private
21645     onRender : function(ct, position)
21646     {
21647         var _t = this;
21648         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21649         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21650         
21651         
21652         this.el.dom.style.border = '0 none';
21653         this.el.dom.setAttribute('tabIndex', -1);
21654         this.el.addClass('x-hidden hide');
21655         
21656         
21657         
21658         if(Roo.isIE){ // fix IE 1px bogus margin
21659             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21660         }
21661        
21662         
21663         this.frameId = Roo.id();
21664         
21665          
21666         
21667         var iframe = this.owner.wrap.createChild({
21668             tag: 'iframe',
21669             cls: 'form-control', // bootstrap..
21670             id: this.frameId,
21671             name: this.frameId,
21672             frameBorder : 'no',
21673             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21674         }, this.el
21675         );
21676         
21677         
21678         this.iframe = iframe.dom;
21679
21680          this.assignDocWin();
21681         
21682         this.doc.designMode = 'on';
21683        
21684         this.doc.open();
21685         this.doc.write(this.getDocMarkup());
21686         this.doc.close();
21687
21688         
21689         var task = { // must defer to wait for browser to be ready
21690             run : function(){
21691                 //console.log("run task?" + this.doc.readyState);
21692                 this.assignDocWin();
21693                 if(this.doc.body || this.doc.readyState == 'complete'){
21694                     try {
21695                         this.doc.designMode="on";
21696                     } catch (e) {
21697                         return;
21698                     }
21699                     Roo.TaskMgr.stop(task);
21700                     this.initEditor.defer(10, this);
21701                 }
21702             },
21703             interval : 10,
21704             duration: 10000,
21705             scope: this
21706         };
21707         Roo.TaskMgr.start(task);
21708
21709     },
21710
21711     // private
21712     onResize : function(w, h)
21713     {
21714          Roo.log('resize: ' +w + ',' + h );
21715         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21716         if(!this.iframe){
21717             return;
21718         }
21719         if(typeof w == 'number'){
21720             
21721             this.iframe.style.width = w + 'px';
21722         }
21723         if(typeof h == 'number'){
21724             
21725             this.iframe.style.height = h + 'px';
21726             if(this.doc){
21727                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21728             }
21729         }
21730         
21731     },
21732
21733     /**
21734      * Toggles the editor between standard and source edit mode.
21735      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21736      */
21737     toggleSourceEdit : function(sourceEditMode){
21738         
21739         this.sourceEditMode = sourceEditMode === true;
21740         
21741         if(this.sourceEditMode){
21742  
21743             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21744             
21745         }else{
21746             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21747             //this.iframe.className = '';
21748             this.deferFocus();
21749         }
21750         //this.setSize(this.owner.wrap.getSize());
21751         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21752     },
21753
21754     
21755   
21756
21757     /**
21758      * Protected method that will not generally be called directly. If you need/want
21759      * custom HTML cleanup, this is the method you should override.
21760      * @param {String} html The HTML to be cleaned
21761      * return {String} The cleaned HTML
21762      */
21763     cleanHtml : function(html){
21764         html = String(html);
21765         if(html.length > 5){
21766             if(Roo.isSafari){ // strip safari nonsense
21767                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21768             }
21769         }
21770         if(html == '&nbsp;'){
21771             html = '';
21772         }
21773         return html;
21774     },
21775
21776     /**
21777      * HTML Editor -> Textarea
21778      * Protected method that will not generally be called directly. Syncs the contents
21779      * of the editor iframe with the textarea.
21780      */
21781     syncValue : function(){
21782         if(this.initialized){
21783             var bd = (this.doc.body || this.doc.documentElement);
21784             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21785             var html = bd.innerHTML;
21786             if(Roo.isSafari){
21787                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21788                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21789                 if(m && m[1]){
21790                     html = '<div style="'+m[0]+'">' + html + '</div>';
21791                 }
21792             }
21793             html = this.cleanHtml(html);
21794             // fix up the special chars.. normaly like back quotes in word...
21795             // however we do not want to do this with chinese..
21796             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21797                 var cc = b.charCodeAt();
21798                 if (
21799                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21800                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21801                     (cc >= 0xf900 && cc < 0xfb00 )
21802                 ) {
21803                         return b;
21804                 }
21805                 return "&#"+cc+";" 
21806             });
21807             if(this.owner.fireEvent('beforesync', this, html) !== false){
21808                 this.el.dom.value = html;
21809                 this.owner.fireEvent('sync', this, html);
21810             }
21811         }
21812     },
21813
21814     /**
21815      * Protected method that will not generally be called directly. Pushes the value of the textarea
21816      * into the iframe editor.
21817      */
21818     pushValue : function(){
21819         if(this.initialized){
21820             var v = this.el.dom.value.trim();
21821             
21822 //            if(v.length < 1){
21823 //                v = '&#160;';
21824 //            }
21825             
21826             if(this.owner.fireEvent('beforepush', this, v) !== false){
21827                 var d = (this.doc.body || this.doc.documentElement);
21828                 d.innerHTML = v;
21829                 this.cleanUpPaste();
21830                 this.el.dom.value = d.innerHTML;
21831                 this.owner.fireEvent('push', this, v);
21832             }
21833         }
21834     },
21835
21836     // private
21837     deferFocus : function(){
21838         this.focus.defer(10, this);
21839     },
21840
21841     // doc'ed in Field
21842     focus : function(){
21843         if(this.win && !this.sourceEditMode){
21844             this.win.focus();
21845         }else{
21846             this.el.focus();
21847         }
21848     },
21849     
21850     assignDocWin: function()
21851     {
21852         var iframe = this.iframe;
21853         
21854          if(Roo.isIE){
21855             this.doc = iframe.contentWindow.document;
21856             this.win = iframe.contentWindow;
21857         } else {
21858 //            if (!Roo.get(this.frameId)) {
21859 //                return;
21860 //            }
21861 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21862 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21863             
21864             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21865                 return;
21866             }
21867             
21868             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21869             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21870         }
21871     },
21872     
21873     // private
21874     initEditor : function(){
21875         //console.log("INIT EDITOR");
21876         this.assignDocWin();
21877         
21878         
21879         
21880         this.doc.designMode="on";
21881         this.doc.open();
21882         this.doc.write(this.getDocMarkup());
21883         this.doc.close();
21884         
21885         var dbody = (this.doc.body || this.doc.documentElement);
21886         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21887         // this copies styles from the containing element into thsi one..
21888         // not sure why we need all of this..
21889         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21890         
21891         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21892         //ss['background-attachment'] = 'fixed'; // w3c
21893         dbody.bgProperties = 'fixed'; // ie
21894         //Roo.DomHelper.applyStyles(dbody, ss);
21895         Roo.EventManager.on(this.doc, {
21896             //'mousedown': this.onEditorEvent,
21897             'mouseup': this.onEditorEvent,
21898             'dblclick': this.onEditorEvent,
21899             'click': this.onEditorEvent,
21900             'keyup': this.onEditorEvent,
21901             buffer:100,
21902             scope: this
21903         });
21904         if(Roo.isGecko){
21905             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21906         }
21907         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21908             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21909         }
21910         this.initialized = true;
21911
21912         this.owner.fireEvent('initialize', this);
21913         this.pushValue();
21914     },
21915
21916     // private
21917     onDestroy : function(){
21918         
21919         
21920         
21921         if(this.rendered){
21922             
21923             //for (var i =0; i < this.toolbars.length;i++) {
21924             //    // fixme - ask toolbars for heights?
21925             //    this.toolbars[i].onDestroy();
21926            // }
21927             
21928             //this.wrap.dom.innerHTML = '';
21929             //this.wrap.remove();
21930         }
21931     },
21932
21933     // private
21934     onFirstFocus : function(){
21935         
21936         this.assignDocWin();
21937         
21938         
21939         this.activated = true;
21940          
21941     
21942         if(Roo.isGecko){ // prevent silly gecko errors
21943             this.win.focus();
21944             var s = this.win.getSelection();
21945             if(!s.focusNode || s.focusNode.nodeType != 3){
21946                 var r = s.getRangeAt(0);
21947                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21948                 r.collapse(true);
21949                 this.deferFocus();
21950             }
21951             try{
21952                 this.execCmd('useCSS', true);
21953                 this.execCmd('styleWithCSS', false);
21954             }catch(e){}
21955         }
21956         this.owner.fireEvent('activate', this);
21957     },
21958
21959     // private
21960     adjustFont: function(btn){
21961         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21962         //if(Roo.isSafari){ // safari
21963         //    adjust *= 2;
21964        // }
21965         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21966         if(Roo.isSafari){ // safari
21967             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21968             v =  (v < 10) ? 10 : v;
21969             v =  (v > 48) ? 48 : v;
21970             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21971             
21972         }
21973         
21974         
21975         v = Math.max(1, v+adjust);
21976         
21977         this.execCmd('FontSize', v  );
21978     },
21979
21980     onEditorEvent : function(e)
21981     {
21982         this.owner.fireEvent('editorevent', this, e);
21983       //  this.updateToolbar();
21984         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21985     },
21986
21987     insertTag : function(tg)
21988     {
21989         // could be a bit smarter... -> wrap the current selected tRoo..
21990         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21991             
21992             range = this.createRange(this.getSelection());
21993             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21994             wrappingNode.appendChild(range.extractContents());
21995             range.insertNode(wrappingNode);
21996
21997             return;
21998             
21999             
22000             
22001         }
22002         this.execCmd("formatblock",   tg);
22003         
22004     },
22005     
22006     insertText : function(txt)
22007     {
22008         
22009         
22010         var range = this.createRange();
22011         range.deleteContents();
22012                //alert(Sender.getAttribute('label'));
22013                
22014         range.insertNode(this.doc.createTextNode(txt));
22015     } ,
22016     
22017      
22018
22019     /**
22020      * Executes a Midas editor command on the editor document and performs necessary focus and
22021      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22022      * @param {String} cmd The Midas command
22023      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22024      */
22025     relayCmd : function(cmd, value){
22026         this.win.focus();
22027         this.execCmd(cmd, value);
22028         this.owner.fireEvent('editorevent', this);
22029         //this.updateToolbar();
22030         this.owner.deferFocus();
22031     },
22032
22033     /**
22034      * Executes a Midas editor command directly on the editor document.
22035      * For visual commands, you should use {@link #relayCmd} instead.
22036      * <b>This should only be called after the editor is initialized.</b>
22037      * @param {String} cmd The Midas command
22038      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22039      */
22040     execCmd : function(cmd, value){
22041         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22042         this.syncValue();
22043     },
22044  
22045  
22046    
22047     /**
22048      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22049      * to insert tRoo.
22050      * @param {String} text | dom node.. 
22051      */
22052     insertAtCursor : function(text)
22053     {
22054         
22055         if(!this.activated){
22056             return;
22057         }
22058         /*
22059         if(Roo.isIE){
22060             this.win.focus();
22061             var r = this.doc.selection.createRange();
22062             if(r){
22063                 r.collapse(true);
22064                 r.pasteHTML(text);
22065                 this.syncValue();
22066                 this.deferFocus();
22067             
22068             }
22069             return;
22070         }
22071         */
22072         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22073             this.win.focus();
22074             
22075             
22076             // from jquery ui (MIT licenced)
22077             var range, node;
22078             var win = this.win;
22079             
22080             if (win.getSelection && win.getSelection().getRangeAt) {
22081                 range = win.getSelection().getRangeAt(0);
22082                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22083                 range.insertNode(node);
22084             } else if (win.document.selection && win.document.selection.createRange) {
22085                 // no firefox support
22086                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22087                 win.document.selection.createRange().pasteHTML(txt);
22088             } else {
22089                 // no firefox support
22090                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22091                 this.execCmd('InsertHTML', txt);
22092             } 
22093             
22094             this.syncValue();
22095             
22096             this.deferFocus();
22097         }
22098     },
22099  // private
22100     mozKeyPress : function(e){
22101         if(e.ctrlKey){
22102             var c = e.getCharCode(), cmd;
22103           
22104             if(c > 0){
22105                 c = String.fromCharCode(c).toLowerCase();
22106                 switch(c){
22107                     case 'b':
22108                         cmd = 'bold';
22109                         break;
22110                     case 'i':
22111                         cmd = 'italic';
22112                         break;
22113                     
22114                     case 'u':
22115                         cmd = 'underline';
22116                         break;
22117                     
22118                     case 'v':
22119                         this.cleanUpPaste.defer(100, this);
22120                         return;
22121                         
22122                 }
22123                 if(cmd){
22124                     this.win.focus();
22125                     this.execCmd(cmd);
22126                     this.deferFocus();
22127                     e.preventDefault();
22128                 }
22129                 
22130             }
22131         }
22132     },
22133
22134     // private
22135     fixKeys : function(){ // load time branching for fastest keydown performance
22136         if(Roo.isIE){
22137             return function(e){
22138                 var k = e.getKey(), r;
22139                 if(k == e.TAB){
22140                     e.stopEvent();
22141                     r = this.doc.selection.createRange();
22142                     if(r){
22143                         r.collapse(true);
22144                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22145                         this.deferFocus();
22146                     }
22147                     return;
22148                 }
22149                 
22150                 if(k == e.ENTER){
22151                     r = this.doc.selection.createRange();
22152                     if(r){
22153                         var target = r.parentElement();
22154                         if(!target || target.tagName.toLowerCase() != 'li'){
22155                             e.stopEvent();
22156                             r.pasteHTML('<br />');
22157                             r.collapse(false);
22158                             r.select();
22159                         }
22160                     }
22161                 }
22162                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22163                     this.cleanUpPaste.defer(100, this);
22164                     return;
22165                 }
22166                 
22167                 
22168             };
22169         }else if(Roo.isOpera){
22170             return function(e){
22171                 var k = e.getKey();
22172                 if(k == e.TAB){
22173                     e.stopEvent();
22174                     this.win.focus();
22175                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22176                     this.deferFocus();
22177                 }
22178                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22179                     this.cleanUpPaste.defer(100, this);
22180                     return;
22181                 }
22182                 
22183             };
22184         }else if(Roo.isSafari){
22185             return function(e){
22186                 var k = e.getKey();
22187                 
22188                 if(k == e.TAB){
22189                     e.stopEvent();
22190                     this.execCmd('InsertText','\t');
22191                     this.deferFocus();
22192                     return;
22193                 }
22194                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22195                     this.cleanUpPaste.defer(100, this);
22196                     return;
22197                 }
22198                 
22199              };
22200         }
22201     }(),
22202     
22203     getAllAncestors: function()
22204     {
22205         var p = this.getSelectedNode();
22206         var a = [];
22207         if (!p) {
22208             a.push(p); // push blank onto stack..
22209             p = this.getParentElement();
22210         }
22211         
22212         
22213         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22214             a.push(p);
22215             p = p.parentNode;
22216         }
22217         a.push(this.doc.body);
22218         return a;
22219     },
22220     lastSel : false,
22221     lastSelNode : false,
22222     
22223     
22224     getSelection : function() 
22225     {
22226         this.assignDocWin();
22227         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22228     },
22229     
22230     getSelectedNode: function() 
22231     {
22232         // this may only work on Gecko!!!
22233         
22234         // should we cache this!!!!
22235         
22236         
22237         
22238          
22239         var range = this.createRange(this.getSelection()).cloneRange();
22240         
22241         if (Roo.isIE) {
22242             var parent = range.parentElement();
22243             while (true) {
22244                 var testRange = range.duplicate();
22245                 testRange.moveToElementText(parent);
22246                 if (testRange.inRange(range)) {
22247                     break;
22248                 }
22249                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22250                     break;
22251                 }
22252                 parent = parent.parentElement;
22253             }
22254             return parent;
22255         }
22256         
22257         // is ancestor a text element.
22258         var ac =  range.commonAncestorContainer;
22259         if (ac.nodeType == 3) {
22260             ac = ac.parentNode;
22261         }
22262         
22263         var ar = ac.childNodes;
22264          
22265         var nodes = [];
22266         var other_nodes = [];
22267         var has_other_nodes = false;
22268         for (var i=0;i<ar.length;i++) {
22269             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22270                 continue;
22271             }
22272             // fullly contained node.
22273             
22274             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22275                 nodes.push(ar[i]);
22276                 continue;
22277             }
22278             
22279             // probably selected..
22280             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22281                 other_nodes.push(ar[i]);
22282                 continue;
22283             }
22284             // outer..
22285             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22286                 continue;
22287             }
22288             
22289             
22290             has_other_nodes = true;
22291         }
22292         if (!nodes.length && other_nodes.length) {
22293             nodes= other_nodes;
22294         }
22295         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22296             return false;
22297         }
22298         
22299         return nodes[0];
22300     },
22301     createRange: function(sel)
22302     {
22303         // this has strange effects when using with 
22304         // top toolbar - not sure if it's a great idea.
22305         //this.editor.contentWindow.focus();
22306         if (typeof sel != "undefined") {
22307             try {
22308                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22309             } catch(e) {
22310                 return this.doc.createRange();
22311             }
22312         } else {
22313             return this.doc.createRange();
22314         }
22315     },
22316     getParentElement: function()
22317     {
22318         
22319         this.assignDocWin();
22320         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22321         
22322         var range = this.createRange(sel);
22323          
22324         try {
22325             var p = range.commonAncestorContainer;
22326             while (p.nodeType == 3) { // text node
22327                 p = p.parentNode;
22328             }
22329             return p;
22330         } catch (e) {
22331             return null;
22332         }
22333     
22334     },
22335     /***
22336      *
22337      * Range intersection.. the hard stuff...
22338      *  '-1' = before
22339      *  '0' = hits..
22340      *  '1' = after.
22341      *         [ -- selected range --- ]
22342      *   [fail]                        [fail]
22343      *
22344      *    basically..
22345      *      if end is before start or  hits it. fail.
22346      *      if start is after end or hits it fail.
22347      *
22348      *   if either hits (but other is outside. - then it's not 
22349      *   
22350      *    
22351      **/
22352     
22353     
22354     // @see http://www.thismuchiknow.co.uk/?p=64.
22355     rangeIntersectsNode : function(range, node)
22356     {
22357         var nodeRange = node.ownerDocument.createRange();
22358         try {
22359             nodeRange.selectNode(node);
22360         } catch (e) {
22361             nodeRange.selectNodeContents(node);
22362         }
22363     
22364         var rangeStartRange = range.cloneRange();
22365         rangeStartRange.collapse(true);
22366     
22367         var rangeEndRange = range.cloneRange();
22368         rangeEndRange.collapse(false);
22369     
22370         var nodeStartRange = nodeRange.cloneRange();
22371         nodeStartRange.collapse(true);
22372     
22373         var nodeEndRange = nodeRange.cloneRange();
22374         nodeEndRange.collapse(false);
22375     
22376         return rangeStartRange.compareBoundaryPoints(
22377                  Range.START_TO_START, nodeEndRange) == -1 &&
22378                rangeEndRange.compareBoundaryPoints(
22379                  Range.START_TO_START, nodeStartRange) == 1;
22380         
22381          
22382     },
22383     rangeCompareNode : function(range, node)
22384     {
22385         var nodeRange = node.ownerDocument.createRange();
22386         try {
22387             nodeRange.selectNode(node);
22388         } catch (e) {
22389             nodeRange.selectNodeContents(node);
22390         }
22391         
22392         
22393         range.collapse(true);
22394     
22395         nodeRange.collapse(true);
22396      
22397         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22398         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22399          
22400         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22401         
22402         var nodeIsBefore   =  ss == 1;
22403         var nodeIsAfter    = ee == -1;
22404         
22405         if (nodeIsBefore && nodeIsAfter) {
22406             return 0; // outer
22407         }
22408         if (!nodeIsBefore && nodeIsAfter) {
22409             return 1; //right trailed.
22410         }
22411         
22412         if (nodeIsBefore && !nodeIsAfter) {
22413             return 2;  // left trailed.
22414         }
22415         // fully contined.
22416         return 3;
22417     },
22418
22419     // private? - in a new class?
22420     cleanUpPaste :  function()
22421     {
22422         // cleans up the whole document..
22423         Roo.log('cleanuppaste');
22424         
22425         this.cleanUpChildren(this.doc.body);
22426         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22427         if (clean != this.doc.body.innerHTML) {
22428             this.doc.body.innerHTML = clean;
22429         }
22430         
22431     },
22432     
22433     cleanWordChars : function(input) {// change the chars to hex code
22434         var he = Roo.HtmlEditorCore;
22435         
22436         var output = input;
22437         Roo.each(he.swapCodes, function(sw) { 
22438             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22439             
22440             output = output.replace(swapper, sw[1]);
22441         });
22442         
22443         return output;
22444     },
22445     
22446     
22447     cleanUpChildren : function (n)
22448     {
22449         if (!n.childNodes.length) {
22450             return;
22451         }
22452         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22453            this.cleanUpChild(n.childNodes[i]);
22454         }
22455     },
22456     
22457     
22458         
22459     
22460     cleanUpChild : function (node)
22461     {
22462         var ed = this;
22463         //console.log(node);
22464         if (node.nodeName == "#text") {
22465             // clean up silly Windows -- stuff?
22466             return; 
22467         }
22468         if (node.nodeName == "#comment") {
22469             node.parentNode.removeChild(node);
22470             // clean up silly Windows -- stuff?
22471             return; 
22472         }
22473         var lcname = node.tagName.toLowerCase();
22474         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22475         // whitelist of tags..
22476         
22477         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22478             // remove node.
22479             node.parentNode.removeChild(node);
22480             return;
22481             
22482         }
22483         
22484         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22485         
22486         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22487         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22488         
22489         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22490         //    remove_keep_children = true;
22491         //}
22492         
22493         if (remove_keep_children) {
22494             this.cleanUpChildren(node);
22495             // inserts everything just before this node...
22496             while (node.childNodes.length) {
22497                 var cn = node.childNodes[0];
22498                 node.removeChild(cn);
22499                 node.parentNode.insertBefore(cn, node);
22500             }
22501             node.parentNode.removeChild(node);
22502             return;
22503         }
22504         
22505         if (!node.attributes || !node.attributes.length) {
22506             this.cleanUpChildren(node);
22507             return;
22508         }
22509         
22510         function cleanAttr(n,v)
22511         {
22512             
22513             if (v.match(/^\./) || v.match(/^\//)) {
22514                 return;
22515             }
22516             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22517                 return;
22518             }
22519             if (v.match(/^#/)) {
22520                 return;
22521             }
22522 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22523             node.removeAttribute(n);
22524             
22525         }
22526         
22527         var cwhite = this.cwhite;
22528         var cblack = this.cblack;
22529             
22530         function cleanStyle(n,v)
22531         {
22532             if (v.match(/expression/)) { //XSS?? should we even bother..
22533                 node.removeAttribute(n);
22534                 return;
22535             }
22536             
22537             var parts = v.split(/;/);
22538             var clean = [];
22539             
22540             Roo.each(parts, function(p) {
22541                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22542                 if (!p.length) {
22543                     return true;
22544                 }
22545                 var l = p.split(':').shift().replace(/\s+/g,'');
22546                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22547                 
22548                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22549 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22550                     //node.removeAttribute(n);
22551                     return true;
22552                 }
22553                 //Roo.log()
22554                 // only allow 'c whitelisted system attributes'
22555                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22556 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22557                     //node.removeAttribute(n);
22558                     return true;
22559                 }
22560                 
22561                 
22562                  
22563                 
22564                 clean.push(p);
22565                 return true;
22566             });
22567             if (clean.length) { 
22568                 node.setAttribute(n, clean.join(';'));
22569             } else {
22570                 node.removeAttribute(n);
22571             }
22572             
22573         }
22574         
22575         
22576         for (var i = node.attributes.length-1; i > -1 ; i--) {
22577             var a = node.attributes[i];
22578             //console.log(a);
22579             
22580             if (a.name.toLowerCase().substr(0,2)=='on')  {
22581                 node.removeAttribute(a.name);
22582                 continue;
22583             }
22584             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22585                 node.removeAttribute(a.name);
22586                 continue;
22587             }
22588             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22589                 cleanAttr(a.name,a.value); // fixme..
22590                 continue;
22591             }
22592             if (a.name == 'style') {
22593                 cleanStyle(a.name,a.value);
22594                 continue;
22595             }
22596             /// clean up MS crap..
22597             // tecnically this should be a list of valid class'es..
22598             
22599             
22600             if (a.name == 'class') {
22601                 if (a.value.match(/^Mso/)) {
22602                     node.className = '';
22603                 }
22604                 
22605                 if (a.value.match(/^body$/)) {
22606                     node.className = '';
22607                 }
22608                 continue;
22609             }
22610             
22611             // style cleanup!?
22612             // class cleanup?
22613             
22614         }
22615         
22616         
22617         this.cleanUpChildren(node);
22618         
22619         
22620     },
22621     
22622     /**
22623      * Clean up MS wordisms...
22624      */
22625     cleanWord : function(node)
22626     {
22627         
22628         
22629         if (!node) {
22630             this.cleanWord(this.doc.body);
22631             return;
22632         }
22633         if (node.nodeName == "#text") {
22634             // clean up silly Windows -- stuff?
22635             return; 
22636         }
22637         if (node.nodeName == "#comment") {
22638             node.parentNode.removeChild(node);
22639             // clean up silly Windows -- stuff?
22640             return; 
22641         }
22642         
22643         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22644             node.parentNode.removeChild(node);
22645             return;
22646         }
22647         
22648         // remove - but keep children..
22649         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22650             while (node.childNodes.length) {
22651                 var cn = node.childNodes[0];
22652                 node.removeChild(cn);
22653                 node.parentNode.insertBefore(cn, node);
22654             }
22655             node.parentNode.removeChild(node);
22656             this.iterateChildren(node, this.cleanWord);
22657             return;
22658         }
22659         // clean styles
22660         if (node.className.length) {
22661             
22662             var cn = node.className.split(/\W+/);
22663             var cna = [];
22664             Roo.each(cn, function(cls) {
22665                 if (cls.match(/Mso[a-zA-Z]+/)) {
22666                     return;
22667                 }
22668                 cna.push(cls);
22669             });
22670             node.className = cna.length ? cna.join(' ') : '';
22671             if (!cna.length) {
22672                 node.removeAttribute("class");
22673             }
22674         }
22675         
22676         if (node.hasAttribute("lang")) {
22677             node.removeAttribute("lang");
22678         }
22679         
22680         if (node.hasAttribute("style")) {
22681             
22682             var styles = node.getAttribute("style").split(";");
22683             var nstyle = [];
22684             Roo.each(styles, function(s) {
22685                 if (!s.match(/:/)) {
22686                     return;
22687                 }
22688                 var kv = s.split(":");
22689                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22690                     return;
22691                 }
22692                 // what ever is left... we allow.
22693                 nstyle.push(s);
22694             });
22695             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22696             if (!nstyle.length) {
22697                 node.removeAttribute('style');
22698             }
22699         }
22700         this.iterateChildren(node, this.cleanWord);
22701         
22702         
22703         
22704     },
22705     /**
22706      * iterateChildren of a Node, calling fn each time, using this as the scole..
22707      * @param {DomNode} node node to iterate children of.
22708      * @param {Function} fn method of this class to call on each item.
22709      */
22710     iterateChildren : function(node, fn)
22711     {
22712         if (!node.childNodes.length) {
22713                 return;
22714         }
22715         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22716            fn.call(this, node.childNodes[i])
22717         }
22718     },
22719     
22720     
22721     /**
22722      * cleanTableWidths.
22723      *
22724      * Quite often pasting from word etc.. results in tables with column and widths.
22725      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22726      *
22727      */
22728     cleanTableWidths : function(node)
22729     {
22730          
22731          
22732         if (!node) {
22733             this.cleanTableWidths(this.doc.body);
22734             return;
22735         }
22736         
22737         // ignore list...
22738         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22739             return; 
22740         }
22741         Roo.log(node.tagName);
22742         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22743             this.iterateChildren(node, this.cleanTableWidths);
22744             return;
22745         }
22746         if (node.hasAttribute('width')) {
22747             node.removeAttribute('width');
22748         }
22749         
22750          
22751         if (node.hasAttribute("style")) {
22752             // pretty basic...
22753             
22754             var styles = node.getAttribute("style").split(";");
22755             var nstyle = [];
22756             Roo.each(styles, function(s) {
22757                 if (!s.match(/:/)) {
22758                     return;
22759                 }
22760                 var kv = s.split(":");
22761                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22762                     return;
22763                 }
22764                 // what ever is left... we allow.
22765                 nstyle.push(s);
22766             });
22767             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22768             if (!nstyle.length) {
22769                 node.removeAttribute('style');
22770             }
22771         }
22772         
22773         this.iterateChildren(node, this.cleanTableWidths);
22774         
22775         
22776     },
22777     
22778     
22779     
22780     
22781     domToHTML : function(currentElement, depth, nopadtext) {
22782         
22783         depth = depth || 0;
22784         nopadtext = nopadtext || false;
22785     
22786         if (!currentElement) {
22787             return this.domToHTML(this.doc.body);
22788         }
22789         
22790         //Roo.log(currentElement);
22791         var j;
22792         var allText = false;
22793         var nodeName = currentElement.nodeName;
22794         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22795         
22796         if  (nodeName == '#text') {
22797             
22798             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22799         }
22800         
22801         
22802         var ret = '';
22803         if (nodeName != 'BODY') {
22804              
22805             var i = 0;
22806             // Prints the node tagName, such as <A>, <IMG>, etc
22807             if (tagName) {
22808                 var attr = [];
22809                 for(i = 0; i < currentElement.attributes.length;i++) {
22810                     // quoting?
22811                     var aname = currentElement.attributes.item(i).name;
22812                     if (!currentElement.attributes.item(i).value.length) {
22813                         continue;
22814                     }
22815                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22816                 }
22817                 
22818                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22819             } 
22820             else {
22821                 
22822                 // eack
22823             }
22824         } else {
22825             tagName = false;
22826         }
22827         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22828             return ret;
22829         }
22830         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22831             nopadtext = true;
22832         }
22833         
22834         
22835         // Traverse the tree
22836         i = 0;
22837         var currentElementChild = currentElement.childNodes.item(i);
22838         var allText = true;
22839         var innerHTML  = '';
22840         lastnode = '';
22841         while (currentElementChild) {
22842             // Formatting code (indent the tree so it looks nice on the screen)
22843             var nopad = nopadtext;
22844             if (lastnode == 'SPAN') {
22845                 nopad  = true;
22846             }
22847             // text
22848             if  (currentElementChild.nodeName == '#text') {
22849                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22850                 toadd = nopadtext ? toadd : toadd.trim();
22851                 if (!nopad && toadd.length > 80) {
22852                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22853                 }
22854                 innerHTML  += toadd;
22855                 
22856                 i++;
22857                 currentElementChild = currentElement.childNodes.item(i);
22858                 lastNode = '';
22859                 continue;
22860             }
22861             allText = false;
22862             
22863             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22864                 
22865             // Recursively traverse the tree structure of the child node
22866             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22867             lastnode = currentElementChild.nodeName;
22868             i++;
22869             currentElementChild=currentElement.childNodes.item(i);
22870         }
22871         
22872         ret += innerHTML;
22873         
22874         if (!allText) {
22875                 // The remaining code is mostly for formatting the tree
22876             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22877         }
22878         
22879         
22880         if (tagName) {
22881             ret+= "</"+tagName+">";
22882         }
22883         return ret;
22884         
22885     },
22886         
22887     applyBlacklists : function()
22888     {
22889         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22890         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22891         
22892         this.white = [];
22893         this.black = [];
22894         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22895             if (b.indexOf(tag) > -1) {
22896                 return;
22897             }
22898             this.white.push(tag);
22899             
22900         }, this);
22901         
22902         Roo.each(w, function(tag) {
22903             if (b.indexOf(tag) > -1) {
22904                 return;
22905             }
22906             if (this.white.indexOf(tag) > -1) {
22907                 return;
22908             }
22909             this.white.push(tag);
22910             
22911         }, this);
22912         
22913         
22914         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22915             if (w.indexOf(tag) > -1) {
22916                 return;
22917             }
22918             this.black.push(tag);
22919             
22920         }, this);
22921         
22922         Roo.each(b, function(tag) {
22923             if (w.indexOf(tag) > -1) {
22924                 return;
22925             }
22926             if (this.black.indexOf(tag) > -1) {
22927                 return;
22928             }
22929             this.black.push(tag);
22930             
22931         }, this);
22932         
22933         
22934         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22935         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22936         
22937         this.cwhite = [];
22938         this.cblack = [];
22939         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22940             if (b.indexOf(tag) > -1) {
22941                 return;
22942             }
22943             this.cwhite.push(tag);
22944             
22945         }, this);
22946         
22947         Roo.each(w, function(tag) {
22948             if (b.indexOf(tag) > -1) {
22949                 return;
22950             }
22951             if (this.cwhite.indexOf(tag) > -1) {
22952                 return;
22953             }
22954             this.cwhite.push(tag);
22955             
22956         }, this);
22957         
22958         
22959         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22960             if (w.indexOf(tag) > -1) {
22961                 return;
22962             }
22963             this.cblack.push(tag);
22964             
22965         }, this);
22966         
22967         Roo.each(b, function(tag) {
22968             if (w.indexOf(tag) > -1) {
22969                 return;
22970             }
22971             if (this.cblack.indexOf(tag) > -1) {
22972                 return;
22973             }
22974             this.cblack.push(tag);
22975             
22976         }, this);
22977     },
22978     
22979     setStylesheets : function(stylesheets)
22980     {
22981         if(typeof(stylesheets) == 'string'){
22982             Roo.get(this.iframe.contentDocument.head).createChild({
22983                 tag : 'link',
22984                 rel : 'stylesheet',
22985                 type : 'text/css',
22986                 href : stylesheets
22987             });
22988             
22989             return;
22990         }
22991         var _this = this;
22992      
22993         Roo.each(stylesheets, function(s) {
22994             if(!s.length){
22995                 return;
22996             }
22997             
22998             Roo.get(_this.iframe.contentDocument.head).createChild({
22999                 tag : 'link',
23000                 rel : 'stylesheet',
23001                 type : 'text/css',
23002                 href : s
23003             });
23004         });
23005
23006         
23007     },
23008     
23009     removeStylesheets : function()
23010     {
23011         var _this = this;
23012         
23013         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23014             s.remove();
23015         });
23016     },
23017     
23018     setStyle : function(style)
23019     {
23020         Roo.get(this.iframe.contentDocument.head).createChild({
23021             tag : 'style',
23022             type : 'text/css',
23023             html : style
23024         });
23025
23026         return;
23027     }
23028     
23029     // hide stuff that is not compatible
23030     /**
23031      * @event blur
23032      * @hide
23033      */
23034     /**
23035      * @event change
23036      * @hide
23037      */
23038     /**
23039      * @event focus
23040      * @hide
23041      */
23042     /**
23043      * @event specialkey
23044      * @hide
23045      */
23046     /**
23047      * @cfg {String} fieldClass @hide
23048      */
23049     /**
23050      * @cfg {String} focusClass @hide
23051      */
23052     /**
23053      * @cfg {String} autoCreate @hide
23054      */
23055     /**
23056      * @cfg {String} inputType @hide
23057      */
23058     /**
23059      * @cfg {String} invalidClass @hide
23060      */
23061     /**
23062      * @cfg {String} invalidText @hide
23063      */
23064     /**
23065      * @cfg {String} msgFx @hide
23066      */
23067     /**
23068      * @cfg {String} validateOnBlur @hide
23069      */
23070 });
23071
23072 Roo.HtmlEditorCore.white = [
23073         'area', 'br', 'img', 'input', 'hr', 'wbr',
23074         
23075        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23076        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23077        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23078        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23079        'table',   'ul',         'xmp', 
23080        
23081        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23082       'thead',   'tr', 
23083      
23084       'dir', 'menu', 'ol', 'ul', 'dl',
23085        
23086       'embed',  'object'
23087 ];
23088
23089
23090 Roo.HtmlEditorCore.black = [
23091     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23092         'applet', // 
23093         'base',   'basefont', 'bgsound', 'blink',  'body', 
23094         'frame',  'frameset', 'head',    'html',   'ilayer', 
23095         'iframe', 'layer',  'link',     'meta',    'object',   
23096         'script', 'style' ,'title',  'xml' // clean later..
23097 ];
23098 Roo.HtmlEditorCore.clean = [
23099     'script', 'style', 'title', 'xml'
23100 ];
23101 Roo.HtmlEditorCore.remove = [
23102     'font'
23103 ];
23104 // attributes..
23105
23106 Roo.HtmlEditorCore.ablack = [
23107     'on'
23108 ];
23109     
23110 Roo.HtmlEditorCore.aclean = [ 
23111     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23112 ];
23113
23114 // protocols..
23115 Roo.HtmlEditorCore.pwhite= [
23116         'http',  'https',  'mailto'
23117 ];
23118
23119 // white listed style attributes.
23120 Roo.HtmlEditorCore.cwhite= [
23121       //  'text-align', /// default is to allow most things..
23122       
23123          
23124 //        'font-size'//??
23125 ];
23126
23127 // black listed style attributes.
23128 Roo.HtmlEditorCore.cblack= [
23129       //  'font-size' -- this can be set by the project 
23130 ];
23131
23132
23133 Roo.HtmlEditorCore.swapCodes   =[ 
23134     [    8211, "--" ], 
23135     [    8212, "--" ], 
23136     [    8216,  "'" ],  
23137     [    8217, "'" ],  
23138     [    8220, '"' ],  
23139     [    8221, '"' ],  
23140     [    8226, "*" ],  
23141     [    8230, "..." ]
23142 ]; 
23143
23144     /*
23145  * - LGPL
23146  *
23147  * HtmlEditor
23148  * 
23149  */
23150
23151 /**
23152  * @class Roo.bootstrap.HtmlEditor
23153  * @extends Roo.bootstrap.TextArea
23154  * Bootstrap HtmlEditor class
23155
23156  * @constructor
23157  * Create a new HtmlEditor
23158  * @param {Object} config The config object
23159  */
23160
23161 Roo.bootstrap.HtmlEditor = function(config){
23162     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23163     if (!this.toolbars) {
23164         this.toolbars = [];
23165     }
23166     
23167     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23168     this.addEvents({
23169             /**
23170              * @event initialize
23171              * Fires when the editor is fully initialized (including the iframe)
23172              * @param {HtmlEditor} this
23173              */
23174             initialize: true,
23175             /**
23176              * @event activate
23177              * Fires when the editor is first receives the focus. Any insertion must wait
23178              * until after this event.
23179              * @param {HtmlEditor} this
23180              */
23181             activate: true,
23182              /**
23183              * @event beforesync
23184              * Fires before the textarea is updated with content from the editor iframe. Return false
23185              * to cancel the sync.
23186              * @param {HtmlEditor} this
23187              * @param {String} html
23188              */
23189             beforesync: true,
23190              /**
23191              * @event beforepush
23192              * Fires before the iframe editor is updated with content from the textarea. Return false
23193              * to cancel the push.
23194              * @param {HtmlEditor} this
23195              * @param {String} html
23196              */
23197             beforepush: true,
23198              /**
23199              * @event sync
23200              * Fires when the textarea is updated with content from the editor iframe.
23201              * @param {HtmlEditor} this
23202              * @param {String} html
23203              */
23204             sync: true,
23205              /**
23206              * @event push
23207              * Fires when the iframe editor is updated with content from the textarea.
23208              * @param {HtmlEditor} this
23209              * @param {String} html
23210              */
23211             push: true,
23212              /**
23213              * @event editmodechange
23214              * Fires when the editor switches edit modes
23215              * @param {HtmlEditor} this
23216              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23217              */
23218             editmodechange: true,
23219             /**
23220              * @event editorevent
23221              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23222              * @param {HtmlEditor} this
23223              */
23224             editorevent: true,
23225             /**
23226              * @event firstfocus
23227              * Fires when on first focus - needed by toolbars..
23228              * @param {HtmlEditor} this
23229              */
23230             firstfocus: true,
23231             /**
23232              * @event autosave
23233              * Auto save the htmlEditor value as a file into Events
23234              * @param {HtmlEditor} this
23235              */
23236             autosave: true,
23237             /**
23238              * @event savedpreview
23239              * preview the saved version of htmlEditor
23240              * @param {HtmlEditor} this
23241              */
23242             savedpreview: true
23243         });
23244 };
23245
23246
23247 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23248     
23249     
23250       /**
23251      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23252      */
23253     toolbars : false,
23254     
23255      /**
23256     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23257     */
23258     btns : [],
23259    
23260      /**
23261      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23262      *                        Roo.resizable.
23263      */
23264     resizable : false,
23265      /**
23266      * @cfg {Number} height (in pixels)
23267      */   
23268     height: 300,
23269    /**
23270      * @cfg {Number} width (in pixels)
23271      */   
23272     width: false,
23273     
23274     /**
23275      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23276      * 
23277      */
23278     stylesheets: false,
23279     
23280     // id of frame..
23281     frameId: false,
23282     
23283     // private properties
23284     validationEvent : false,
23285     deferHeight: true,
23286     initialized : false,
23287     activated : false,
23288     
23289     onFocus : Roo.emptyFn,
23290     iframePad:3,
23291     hideMode:'offsets',
23292     
23293     tbContainer : false,
23294     
23295     bodyCls : '',
23296     
23297     toolbarContainer :function() {
23298         return this.wrap.select('.x-html-editor-tb',true).first();
23299     },
23300
23301     /**
23302      * Protected method that will not generally be called directly. It
23303      * is called when the editor creates its toolbar. Override this method if you need to
23304      * add custom toolbar buttons.
23305      * @param {HtmlEditor} editor
23306      */
23307     createToolbar : function(){
23308         Roo.log('renewing');
23309         Roo.log("create toolbars");
23310         
23311         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23312         this.toolbars[0].render(this.toolbarContainer());
23313         
23314         return;
23315         
23316 //        if (!editor.toolbars || !editor.toolbars.length) {
23317 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23318 //        }
23319 //        
23320 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23321 //            editor.toolbars[i] = Roo.factory(
23322 //                    typeof(editor.toolbars[i]) == 'string' ?
23323 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23324 //                Roo.bootstrap.HtmlEditor);
23325 //            editor.toolbars[i].init(editor);
23326 //        }
23327     },
23328
23329      
23330     // private
23331     onRender : function(ct, position)
23332     {
23333        // Roo.log("Call onRender: " + this.xtype);
23334         var _t = this;
23335         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23336       
23337         this.wrap = this.inputEl().wrap({
23338             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23339         });
23340         
23341         this.editorcore.onRender(ct, position);
23342          
23343         if (this.resizable) {
23344             this.resizeEl = new Roo.Resizable(this.wrap, {
23345                 pinned : true,
23346                 wrap: true,
23347                 dynamic : true,
23348                 minHeight : this.height,
23349                 height: this.height,
23350                 handles : this.resizable,
23351                 width: this.width,
23352                 listeners : {
23353                     resize : function(r, w, h) {
23354                         _t.onResize(w,h); // -something
23355                     }
23356                 }
23357             });
23358             
23359         }
23360         this.createToolbar(this);
23361        
23362         
23363         if(!this.width && this.resizable){
23364             this.setSize(this.wrap.getSize());
23365         }
23366         if (this.resizeEl) {
23367             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23368             // should trigger onReize..
23369         }
23370         
23371     },
23372
23373     // private
23374     onResize : function(w, h)
23375     {
23376         Roo.log('resize: ' +w + ',' + h );
23377         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23378         var ew = false;
23379         var eh = false;
23380         
23381         if(this.inputEl() ){
23382             if(typeof w == 'number'){
23383                 var aw = w - this.wrap.getFrameWidth('lr');
23384                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23385                 ew = aw;
23386             }
23387             if(typeof h == 'number'){
23388                  var tbh = -11;  // fixme it needs to tool bar size!
23389                 for (var i =0; i < this.toolbars.length;i++) {
23390                     // fixme - ask toolbars for heights?
23391                     tbh += this.toolbars[i].el.getHeight();
23392                     //if (this.toolbars[i].footer) {
23393                     //    tbh += this.toolbars[i].footer.el.getHeight();
23394                     //}
23395                 }
23396               
23397                 
23398                 
23399                 
23400                 
23401                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23402                 ah -= 5; // knock a few pixes off for look..
23403                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23404                 var eh = ah;
23405             }
23406         }
23407         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23408         this.editorcore.onResize(ew,eh);
23409         
23410     },
23411
23412     /**
23413      * Toggles the editor between standard and source edit mode.
23414      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23415      */
23416     toggleSourceEdit : function(sourceEditMode)
23417     {
23418         this.editorcore.toggleSourceEdit(sourceEditMode);
23419         
23420         if(this.editorcore.sourceEditMode){
23421             Roo.log('editor - showing textarea');
23422             
23423 //            Roo.log('in');
23424 //            Roo.log(this.syncValue());
23425             this.syncValue();
23426             this.inputEl().removeClass(['hide', 'x-hidden']);
23427             this.inputEl().dom.removeAttribute('tabIndex');
23428             this.inputEl().focus();
23429         }else{
23430             Roo.log('editor - hiding textarea');
23431 //            Roo.log('out')
23432 //            Roo.log(this.pushValue()); 
23433             this.pushValue();
23434             
23435             this.inputEl().addClass(['hide', 'x-hidden']);
23436             this.inputEl().dom.setAttribute('tabIndex', -1);
23437             //this.deferFocus();
23438         }
23439          
23440         if(this.resizable){
23441             this.setSize(this.wrap.getSize());
23442         }
23443         
23444         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23445     },
23446  
23447     // private (for BoxComponent)
23448     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23449
23450     // private (for BoxComponent)
23451     getResizeEl : function(){
23452         return this.wrap;
23453     },
23454
23455     // private (for BoxComponent)
23456     getPositionEl : function(){
23457         return this.wrap;
23458     },
23459
23460     // private
23461     initEvents : function(){
23462         this.originalValue = this.getValue();
23463     },
23464
23465 //    /**
23466 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23467 //     * @method
23468 //     */
23469 //    markInvalid : Roo.emptyFn,
23470 //    /**
23471 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23472 //     * @method
23473 //     */
23474 //    clearInvalid : Roo.emptyFn,
23475
23476     setValue : function(v){
23477         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23478         this.editorcore.pushValue();
23479     },
23480
23481      
23482     // private
23483     deferFocus : function(){
23484         this.focus.defer(10, this);
23485     },
23486
23487     // doc'ed in Field
23488     focus : function(){
23489         this.editorcore.focus();
23490         
23491     },
23492       
23493
23494     // private
23495     onDestroy : function(){
23496         
23497         
23498         
23499         if(this.rendered){
23500             
23501             for (var i =0; i < this.toolbars.length;i++) {
23502                 // fixme - ask toolbars for heights?
23503                 this.toolbars[i].onDestroy();
23504             }
23505             
23506             this.wrap.dom.innerHTML = '';
23507             this.wrap.remove();
23508         }
23509     },
23510
23511     // private
23512     onFirstFocus : function(){
23513         //Roo.log("onFirstFocus");
23514         this.editorcore.onFirstFocus();
23515          for (var i =0; i < this.toolbars.length;i++) {
23516             this.toolbars[i].onFirstFocus();
23517         }
23518         
23519     },
23520     
23521     // private
23522     syncValue : function()
23523     {   
23524         this.editorcore.syncValue();
23525     },
23526     
23527     pushValue : function()
23528     {   
23529         this.editorcore.pushValue();
23530     }
23531      
23532     
23533     // hide stuff that is not compatible
23534     /**
23535      * @event blur
23536      * @hide
23537      */
23538     /**
23539      * @event change
23540      * @hide
23541      */
23542     /**
23543      * @event focus
23544      * @hide
23545      */
23546     /**
23547      * @event specialkey
23548      * @hide
23549      */
23550     /**
23551      * @cfg {String} fieldClass @hide
23552      */
23553     /**
23554      * @cfg {String} focusClass @hide
23555      */
23556     /**
23557      * @cfg {String} autoCreate @hide
23558      */
23559     /**
23560      * @cfg {String} inputType @hide
23561      */
23562     /**
23563      * @cfg {String} invalidClass @hide
23564      */
23565     /**
23566      * @cfg {String} invalidText @hide
23567      */
23568     /**
23569      * @cfg {String} msgFx @hide
23570      */
23571     /**
23572      * @cfg {String} validateOnBlur @hide
23573      */
23574 });
23575  
23576     
23577    
23578    
23579    
23580       
23581 Roo.namespace('Roo.bootstrap.htmleditor');
23582 /**
23583  * @class Roo.bootstrap.HtmlEditorToolbar1
23584  * Basic Toolbar
23585  * 
23586  * Usage:
23587  *
23588  new Roo.bootstrap.HtmlEditor({
23589     ....
23590     toolbars : [
23591         new Roo.bootstrap.HtmlEditorToolbar1({
23592             disable : { fonts: 1 , format: 1, ..., ... , ...],
23593             btns : [ .... ]
23594         })
23595     }
23596      
23597  * 
23598  * @cfg {Object} disable List of elements to disable..
23599  * @cfg {Array} btns List of additional buttons.
23600  * 
23601  * 
23602  * NEEDS Extra CSS? 
23603  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23604  */
23605  
23606 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23607 {
23608     
23609     Roo.apply(this, config);
23610     
23611     // default disabled, based on 'good practice'..
23612     this.disable = this.disable || {};
23613     Roo.applyIf(this.disable, {
23614         fontSize : true,
23615         colors : true,
23616         specialElements : true
23617     });
23618     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23619     
23620     this.editor = config.editor;
23621     this.editorcore = config.editor.editorcore;
23622     
23623     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23624     
23625     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23626     // dont call parent... till later.
23627 }
23628 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23629      
23630     bar : true,
23631     
23632     editor : false,
23633     editorcore : false,
23634     
23635     
23636     formats : [
23637         "p" ,  
23638         "h1","h2","h3","h4","h5","h6", 
23639         "pre", "code", 
23640         "abbr", "acronym", "address", "cite", "samp", "var",
23641         'div','span'
23642     ],
23643     
23644     onRender : function(ct, position)
23645     {
23646        // Roo.log("Call onRender: " + this.xtype);
23647         
23648        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23649        Roo.log(this.el);
23650        this.el.dom.style.marginBottom = '0';
23651        var _this = this;
23652        var editorcore = this.editorcore;
23653        var editor= this.editor;
23654        
23655        var children = [];
23656        var btn = function(id,cmd , toggle, handler, html){
23657        
23658             var  event = toggle ? 'toggle' : 'click';
23659        
23660             var a = {
23661                 size : 'sm',
23662                 xtype: 'Button',
23663                 xns: Roo.bootstrap,
23664                 glyphicon : id,
23665                 cmd : id || cmd,
23666                 enableToggle:toggle !== false,
23667                 html : html || '',
23668                 pressed : toggle ? false : null,
23669                 listeners : {}
23670             };
23671             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23672                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23673             };
23674             children.push(a);
23675             return a;
23676        }
23677        
23678     //    var cb_box = function...
23679         
23680         var style = {
23681                 xtype: 'Button',
23682                 size : 'sm',
23683                 xns: Roo.bootstrap,
23684                 glyphicon : 'font',
23685                 //html : 'submit'
23686                 menu : {
23687                     xtype: 'Menu',
23688                     xns: Roo.bootstrap,
23689                     items:  []
23690                 }
23691         };
23692         Roo.each(this.formats, function(f) {
23693             style.menu.items.push({
23694                 xtype :'MenuItem',
23695                 xns: Roo.bootstrap,
23696                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23697                 tagname : f,
23698                 listeners : {
23699                     click : function()
23700                     {
23701                         editorcore.insertTag(this.tagname);
23702                         editor.focus();
23703                     }
23704                 }
23705                 
23706             });
23707         });
23708         children.push(style);   
23709         
23710         btn('bold',false,true);
23711         btn('italic',false,true);
23712         btn('align-left', 'justifyleft',true);
23713         btn('align-center', 'justifycenter',true);
23714         btn('align-right' , 'justifyright',true);
23715         btn('link', false, false, function(btn) {
23716             //Roo.log("create link?");
23717             var url = prompt(this.createLinkText, this.defaultLinkValue);
23718             if(url && url != 'http:/'+'/'){
23719                 this.editorcore.relayCmd('createlink', url);
23720             }
23721         }),
23722         btn('list','insertunorderedlist',true);
23723         btn('pencil', false,true, function(btn){
23724                 Roo.log(this);
23725                 this.toggleSourceEdit(btn.pressed);
23726         });
23727         
23728         if (this.editor.btns.length > 0) {
23729             for (var i = 0; i<this.editor.btns.length; i++) {
23730                 children.push(this.editor.btns[i]);
23731             }
23732         }
23733         
23734         /*
23735         var cog = {
23736                 xtype: 'Button',
23737                 size : 'sm',
23738                 xns: Roo.bootstrap,
23739                 glyphicon : 'cog',
23740                 //html : 'submit'
23741                 menu : {
23742                     xtype: 'Menu',
23743                     xns: Roo.bootstrap,
23744                     items:  []
23745                 }
23746         };
23747         
23748         cog.menu.items.push({
23749             xtype :'MenuItem',
23750             xns: Roo.bootstrap,
23751             html : Clean styles,
23752             tagname : f,
23753             listeners : {
23754                 click : function()
23755                 {
23756                     editorcore.insertTag(this.tagname);
23757                     editor.focus();
23758                 }
23759             }
23760             
23761         });
23762        */
23763         
23764          
23765        this.xtype = 'NavSimplebar';
23766         
23767         for(var i=0;i< children.length;i++) {
23768             
23769             this.buttons.add(this.addxtypeChild(children[i]));
23770             
23771         }
23772         
23773         editor.on('editorevent', this.updateToolbar, this);
23774     },
23775     onBtnClick : function(id)
23776     {
23777        this.editorcore.relayCmd(id);
23778        this.editorcore.focus();
23779     },
23780     
23781     /**
23782      * Protected method that will not generally be called directly. It triggers
23783      * a toolbar update by reading the markup state of the current selection in the editor.
23784      */
23785     updateToolbar: function(){
23786
23787         if(!this.editorcore.activated){
23788             this.editor.onFirstFocus(); // is this neeed?
23789             return;
23790         }
23791
23792         var btns = this.buttons; 
23793         var doc = this.editorcore.doc;
23794         btns.get('bold').setActive(doc.queryCommandState('bold'));
23795         btns.get('italic').setActive(doc.queryCommandState('italic'));
23796         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23797         
23798         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23799         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23800         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23801         
23802         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23803         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23804          /*
23805         
23806         var ans = this.editorcore.getAllAncestors();
23807         if (this.formatCombo) {
23808             
23809             
23810             var store = this.formatCombo.store;
23811             this.formatCombo.setValue("");
23812             for (var i =0; i < ans.length;i++) {
23813                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23814                     // select it..
23815                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23816                     break;
23817                 }
23818             }
23819         }
23820         
23821         
23822         
23823         // hides menus... - so this cant be on a menu...
23824         Roo.bootstrap.MenuMgr.hideAll();
23825         */
23826         Roo.bootstrap.MenuMgr.hideAll();
23827         //this.editorsyncValue();
23828     },
23829     onFirstFocus: function() {
23830         this.buttons.each(function(item){
23831            item.enable();
23832         });
23833     },
23834     toggleSourceEdit : function(sourceEditMode){
23835         
23836           
23837         if(sourceEditMode){
23838             Roo.log("disabling buttons");
23839            this.buttons.each( function(item){
23840                 if(item.cmd != 'pencil'){
23841                     item.disable();
23842                 }
23843             });
23844           
23845         }else{
23846             Roo.log("enabling buttons");
23847             if(this.editorcore.initialized){
23848                 this.buttons.each( function(item){
23849                     item.enable();
23850                 });
23851             }
23852             
23853         }
23854         Roo.log("calling toggole on editor");
23855         // tell the editor that it's been pressed..
23856         this.editor.toggleSourceEdit(sourceEditMode);
23857        
23858     }
23859 });
23860
23861
23862
23863
23864
23865 /**
23866  * @class Roo.bootstrap.Table.AbstractSelectionModel
23867  * @extends Roo.util.Observable
23868  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23869  * implemented by descendant classes.  This class should not be directly instantiated.
23870  * @constructor
23871  */
23872 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23873     this.locked = false;
23874     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23875 };
23876
23877
23878 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23879     /** @ignore Called by the grid automatically. Do not call directly. */
23880     init : function(grid){
23881         this.grid = grid;
23882         this.initEvents();
23883     },
23884
23885     /**
23886      * Locks the selections.
23887      */
23888     lock : function(){
23889         this.locked = true;
23890     },
23891
23892     /**
23893      * Unlocks the selections.
23894      */
23895     unlock : function(){
23896         this.locked = false;
23897     },
23898
23899     /**
23900      * Returns true if the selections are locked.
23901      * @return {Boolean}
23902      */
23903     isLocked : function(){
23904         return this.locked;
23905     }
23906 });
23907 /**
23908  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23909  * @class Roo.bootstrap.Table.RowSelectionModel
23910  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23911  * It supports multiple selections and keyboard selection/navigation. 
23912  * @constructor
23913  * @param {Object} config
23914  */
23915
23916 Roo.bootstrap.Table.RowSelectionModel = function(config){
23917     Roo.apply(this, config);
23918     this.selections = new Roo.util.MixedCollection(false, function(o){
23919         return o.id;
23920     });
23921
23922     this.last = false;
23923     this.lastActive = false;
23924
23925     this.addEvents({
23926         /**
23927              * @event selectionchange
23928              * Fires when the selection changes
23929              * @param {SelectionModel} this
23930              */
23931             "selectionchange" : true,
23932         /**
23933              * @event afterselectionchange
23934              * Fires after the selection changes (eg. by key press or clicking)
23935              * @param {SelectionModel} this
23936              */
23937             "afterselectionchange" : true,
23938         /**
23939              * @event beforerowselect
23940              * Fires when a row is selected being selected, return false to cancel.
23941              * @param {SelectionModel} this
23942              * @param {Number} rowIndex The selected index
23943              * @param {Boolean} keepExisting False if other selections will be cleared
23944              */
23945             "beforerowselect" : true,
23946         /**
23947              * @event rowselect
23948              * Fires when a row is selected.
23949              * @param {SelectionModel} this
23950              * @param {Number} rowIndex The selected index
23951              * @param {Roo.data.Record} r The record
23952              */
23953             "rowselect" : true,
23954         /**
23955              * @event rowdeselect
23956              * Fires when a row is deselected.
23957              * @param {SelectionModel} this
23958              * @param {Number} rowIndex The selected index
23959              */
23960         "rowdeselect" : true
23961     });
23962     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23963     this.locked = false;
23964  };
23965
23966 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23967     /**
23968      * @cfg {Boolean} singleSelect
23969      * True to allow selection of only one row at a time (defaults to false)
23970      */
23971     singleSelect : false,
23972
23973     // private
23974     initEvents : function()
23975     {
23976
23977         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23978         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23979         //}else{ // allow click to work like normal
23980          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23981         //}
23982         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23983         this.grid.on("rowclick", this.handleMouseDown, this);
23984         
23985         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23986             "up" : function(e){
23987                 if(!e.shiftKey){
23988                     this.selectPrevious(e.shiftKey);
23989                 }else if(this.last !== false && this.lastActive !== false){
23990                     var last = this.last;
23991                     this.selectRange(this.last,  this.lastActive-1);
23992                     this.grid.getView().focusRow(this.lastActive);
23993                     if(last !== false){
23994                         this.last = last;
23995                     }
23996                 }else{
23997                     this.selectFirstRow();
23998                 }
23999                 this.fireEvent("afterselectionchange", this);
24000             },
24001             "down" : function(e){
24002                 if(!e.shiftKey){
24003                     this.selectNext(e.shiftKey);
24004                 }else if(this.last !== false && this.lastActive !== false){
24005                     var last = this.last;
24006                     this.selectRange(this.last,  this.lastActive+1);
24007                     this.grid.getView().focusRow(this.lastActive);
24008                     if(last !== false){
24009                         this.last = last;
24010                     }
24011                 }else{
24012                     this.selectFirstRow();
24013                 }
24014                 this.fireEvent("afterselectionchange", this);
24015             },
24016             scope: this
24017         });
24018         this.grid.store.on('load', function(){
24019             this.selections.clear();
24020         },this);
24021         /*
24022         var view = this.grid.view;
24023         view.on("refresh", this.onRefresh, this);
24024         view.on("rowupdated", this.onRowUpdated, this);
24025         view.on("rowremoved", this.onRemove, this);
24026         */
24027     },
24028
24029     // private
24030     onRefresh : function()
24031     {
24032         var ds = this.grid.store, i, v = this.grid.view;
24033         var s = this.selections;
24034         s.each(function(r){
24035             if((i = ds.indexOfId(r.id)) != -1){
24036                 v.onRowSelect(i);
24037             }else{
24038                 s.remove(r);
24039             }
24040         });
24041     },
24042
24043     // private
24044     onRemove : function(v, index, r){
24045         this.selections.remove(r);
24046     },
24047
24048     // private
24049     onRowUpdated : function(v, index, r){
24050         if(this.isSelected(r)){
24051             v.onRowSelect(index);
24052         }
24053     },
24054
24055     /**
24056      * Select records.
24057      * @param {Array} records The records to select
24058      * @param {Boolean} keepExisting (optional) True to keep existing selections
24059      */
24060     selectRecords : function(records, keepExisting)
24061     {
24062         if(!keepExisting){
24063             this.clearSelections();
24064         }
24065             var ds = this.grid.store;
24066         for(var i = 0, len = records.length; i < len; i++){
24067             this.selectRow(ds.indexOf(records[i]), true);
24068         }
24069     },
24070
24071     /**
24072      * Gets the number of selected rows.
24073      * @return {Number}
24074      */
24075     getCount : function(){
24076         return this.selections.length;
24077     },
24078
24079     /**
24080      * Selects the first row in the grid.
24081      */
24082     selectFirstRow : function(){
24083         this.selectRow(0);
24084     },
24085
24086     /**
24087      * Select the last row.
24088      * @param {Boolean} keepExisting (optional) True to keep existing selections
24089      */
24090     selectLastRow : function(keepExisting){
24091         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24092         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24093     },
24094
24095     /**
24096      * Selects the row immediately following the last selected row.
24097      * @param {Boolean} keepExisting (optional) True to keep existing selections
24098      */
24099     selectNext : function(keepExisting)
24100     {
24101             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24102             this.selectRow(this.last+1, keepExisting);
24103             this.grid.getView().focusRow(this.last);
24104         }
24105     },
24106
24107     /**
24108      * Selects the row that precedes the last selected row.
24109      * @param {Boolean} keepExisting (optional) True to keep existing selections
24110      */
24111     selectPrevious : function(keepExisting){
24112         if(this.last){
24113             this.selectRow(this.last-1, keepExisting);
24114             this.grid.getView().focusRow(this.last);
24115         }
24116     },
24117
24118     /**
24119      * Returns the selected records
24120      * @return {Array} Array of selected records
24121      */
24122     getSelections : function(){
24123         return [].concat(this.selections.items);
24124     },
24125
24126     /**
24127      * Returns the first selected record.
24128      * @return {Record}
24129      */
24130     getSelected : function(){
24131         return this.selections.itemAt(0);
24132     },
24133
24134
24135     /**
24136      * Clears all selections.
24137      */
24138     clearSelections : function(fast)
24139     {
24140         if(this.locked) {
24141             return;
24142         }
24143         if(fast !== true){
24144                 var ds = this.grid.store;
24145             var s = this.selections;
24146             s.each(function(r){
24147                 this.deselectRow(ds.indexOfId(r.id));
24148             }, this);
24149             s.clear();
24150         }else{
24151             this.selections.clear();
24152         }
24153         this.last = false;
24154     },
24155
24156
24157     /**
24158      * Selects all rows.
24159      */
24160     selectAll : function(){
24161         if(this.locked) {
24162             return;
24163         }
24164         this.selections.clear();
24165         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24166             this.selectRow(i, true);
24167         }
24168     },
24169
24170     /**
24171      * Returns True if there is a selection.
24172      * @return {Boolean}
24173      */
24174     hasSelection : function(){
24175         return this.selections.length > 0;
24176     },
24177
24178     /**
24179      * Returns True if the specified row is selected.
24180      * @param {Number/Record} record The record or index of the record to check
24181      * @return {Boolean}
24182      */
24183     isSelected : function(index){
24184             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24185         return (r && this.selections.key(r.id) ? true : false);
24186     },
24187
24188     /**
24189      * Returns True if the specified record id is selected.
24190      * @param {String} id The id of record to check
24191      * @return {Boolean}
24192      */
24193     isIdSelected : function(id){
24194         return (this.selections.key(id) ? true : false);
24195     },
24196
24197
24198     // private
24199     handleMouseDBClick : function(e, t){
24200         
24201     },
24202     // private
24203     handleMouseDown : function(e, t)
24204     {
24205             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24206         if(this.isLocked() || rowIndex < 0 ){
24207             return;
24208         };
24209         if(e.shiftKey && this.last !== false){
24210             var last = this.last;
24211             this.selectRange(last, rowIndex, e.ctrlKey);
24212             this.last = last; // reset the last
24213             t.focus();
24214     
24215         }else{
24216             var isSelected = this.isSelected(rowIndex);
24217             //Roo.log("select row:" + rowIndex);
24218             if(isSelected){
24219                 this.deselectRow(rowIndex);
24220             } else {
24221                         this.selectRow(rowIndex, true);
24222             }
24223     
24224             /*
24225                 if(e.button !== 0 && isSelected){
24226                 alert('rowIndex 2: ' + rowIndex);
24227                     view.focusRow(rowIndex);
24228                 }else if(e.ctrlKey && isSelected){
24229                     this.deselectRow(rowIndex);
24230                 }else if(!isSelected){
24231                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24232                     view.focusRow(rowIndex);
24233                 }
24234             */
24235         }
24236         this.fireEvent("afterselectionchange", this);
24237     },
24238     // private
24239     handleDragableRowClick :  function(grid, rowIndex, e) 
24240     {
24241         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24242             this.selectRow(rowIndex, false);
24243             grid.view.focusRow(rowIndex);
24244              this.fireEvent("afterselectionchange", this);
24245         }
24246     },
24247     
24248     /**
24249      * Selects multiple rows.
24250      * @param {Array} rows Array of the indexes of the row to select
24251      * @param {Boolean} keepExisting (optional) True to keep existing selections
24252      */
24253     selectRows : function(rows, keepExisting){
24254         if(!keepExisting){
24255             this.clearSelections();
24256         }
24257         for(var i = 0, len = rows.length; i < len; i++){
24258             this.selectRow(rows[i], true);
24259         }
24260     },
24261
24262     /**
24263      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24264      * @param {Number} startRow The index of the first row in the range
24265      * @param {Number} endRow The index of the last row in the range
24266      * @param {Boolean} keepExisting (optional) True to retain existing selections
24267      */
24268     selectRange : function(startRow, endRow, keepExisting){
24269         if(this.locked) {
24270             return;
24271         }
24272         if(!keepExisting){
24273             this.clearSelections();
24274         }
24275         if(startRow <= endRow){
24276             for(var i = startRow; i <= endRow; i++){
24277                 this.selectRow(i, true);
24278             }
24279         }else{
24280             for(var i = startRow; i >= endRow; i--){
24281                 this.selectRow(i, true);
24282             }
24283         }
24284     },
24285
24286     /**
24287      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24288      * @param {Number} startRow The index of the first row in the range
24289      * @param {Number} endRow The index of the last row in the range
24290      */
24291     deselectRange : function(startRow, endRow, preventViewNotify){
24292         if(this.locked) {
24293             return;
24294         }
24295         for(var i = startRow; i <= endRow; i++){
24296             this.deselectRow(i, preventViewNotify);
24297         }
24298     },
24299
24300     /**
24301      * Selects a row.
24302      * @param {Number} row The index of the row to select
24303      * @param {Boolean} keepExisting (optional) True to keep existing selections
24304      */
24305     selectRow : function(index, keepExisting, preventViewNotify)
24306     {
24307             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24308             return;
24309         }
24310         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24311             if(!keepExisting || this.singleSelect){
24312                 this.clearSelections();
24313             }
24314             
24315             var r = this.grid.store.getAt(index);
24316             //console.log('selectRow - record id :' + r.id);
24317             
24318             this.selections.add(r);
24319             this.last = this.lastActive = index;
24320             if(!preventViewNotify){
24321                 var proxy = new Roo.Element(
24322                                 this.grid.getRowDom(index)
24323                 );
24324                 proxy.addClass('bg-info info');
24325             }
24326             this.fireEvent("rowselect", this, index, r);
24327             this.fireEvent("selectionchange", this);
24328         }
24329     },
24330
24331     /**
24332      * Deselects a row.
24333      * @param {Number} row The index of the row to deselect
24334      */
24335     deselectRow : function(index, preventViewNotify)
24336     {
24337         if(this.locked) {
24338             return;
24339         }
24340         if(this.last == index){
24341             this.last = false;
24342         }
24343         if(this.lastActive == index){
24344             this.lastActive = false;
24345         }
24346         
24347         var r = this.grid.store.getAt(index);
24348         if (!r) {
24349             return;
24350         }
24351         
24352         this.selections.remove(r);
24353         //.console.log('deselectRow - record id :' + r.id);
24354         if(!preventViewNotify){
24355         
24356             var proxy = new Roo.Element(
24357                 this.grid.getRowDom(index)
24358             );
24359             proxy.removeClass('bg-info info');
24360         }
24361         this.fireEvent("rowdeselect", this, index);
24362         this.fireEvent("selectionchange", this);
24363     },
24364
24365     // private
24366     restoreLast : function(){
24367         if(this._last){
24368             this.last = this._last;
24369         }
24370     },
24371
24372     // private
24373     acceptsNav : function(row, col, cm){
24374         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24375     },
24376
24377     // private
24378     onEditorKey : function(field, e){
24379         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24380         if(k == e.TAB){
24381             e.stopEvent();
24382             ed.completeEdit();
24383             if(e.shiftKey){
24384                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24385             }else{
24386                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24387             }
24388         }else if(k == e.ENTER && !e.ctrlKey){
24389             e.stopEvent();
24390             ed.completeEdit();
24391             if(e.shiftKey){
24392                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24393             }else{
24394                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24395             }
24396         }else if(k == e.ESC){
24397             ed.cancelEdit();
24398         }
24399         if(newCell){
24400             g.startEditing(newCell[0], newCell[1]);
24401         }
24402     }
24403 });
24404 /*
24405  * Based on:
24406  * Ext JS Library 1.1.1
24407  * Copyright(c) 2006-2007, Ext JS, LLC.
24408  *
24409  * Originally Released Under LGPL - original licence link has changed is not relivant.
24410  *
24411  * Fork - LGPL
24412  * <script type="text/javascript">
24413  */
24414  
24415 /**
24416  * @class Roo.bootstrap.PagingToolbar
24417  * @extends Roo.bootstrap.NavSimplebar
24418  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24419  * @constructor
24420  * Create a new PagingToolbar
24421  * @param {Object} config The config object
24422  * @param {Roo.data.Store} store
24423  */
24424 Roo.bootstrap.PagingToolbar = function(config)
24425 {
24426     // old args format still supported... - xtype is prefered..
24427         // created from xtype...
24428     
24429     this.ds = config.dataSource;
24430     
24431     if (config.store && !this.ds) {
24432         this.store= Roo.factory(config.store, Roo.data);
24433         this.ds = this.store;
24434         this.ds.xmodule = this.xmodule || false;
24435     }
24436     
24437     this.toolbarItems = [];
24438     if (config.items) {
24439         this.toolbarItems = config.items;
24440     }
24441     
24442     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24443     
24444     this.cursor = 0;
24445     
24446     if (this.ds) { 
24447         this.bind(this.ds);
24448     }
24449     
24450     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24451     
24452 };
24453
24454 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24455     /**
24456      * @cfg {Roo.data.Store} dataSource
24457      * The underlying data store providing the paged data
24458      */
24459     /**
24460      * @cfg {String/HTMLElement/Element} container
24461      * container The id or element that will contain the toolbar
24462      */
24463     /**
24464      * @cfg {Boolean} displayInfo
24465      * True to display the displayMsg (defaults to false)
24466      */
24467     /**
24468      * @cfg {Number} pageSize
24469      * The number of records to display per page (defaults to 20)
24470      */
24471     pageSize: 20,
24472     /**
24473      * @cfg {String} displayMsg
24474      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24475      */
24476     displayMsg : 'Displaying {0} - {1} of {2}',
24477     /**
24478      * @cfg {String} emptyMsg
24479      * The message to display when no records are found (defaults to "No data to display")
24480      */
24481     emptyMsg : 'No data to display',
24482     /**
24483      * Customizable piece of the default paging text (defaults to "Page")
24484      * @type String
24485      */
24486     beforePageText : "Page",
24487     /**
24488      * Customizable piece of the default paging text (defaults to "of %0")
24489      * @type String
24490      */
24491     afterPageText : "of {0}",
24492     /**
24493      * Customizable piece of the default paging text (defaults to "First Page")
24494      * @type String
24495      */
24496     firstText : "First Page",
24497     /**
24498      * Customizable piece of the default paging text (defaults to "Previous Page")
24499      * @type String
24500      */
24501     prevText : "Previous Page",
24502     /**
24503      * Customizable piece of the default paging text (defaults to "Next Page")
24504      * @type String
24505      */
24506     nextText : "Next Page",
24507     /**
24508      * Customizable piece of the default paging text (defaults to "Last Page")
24509      * @type String
24510      */
24511     lastText : "Last Page",
24512     /**
24513      * Customizable piece of the default paging text (defaults to "Refresh")
24514      * @type String
24515      */
24516     refreshText : "Refresh",
24517
24518     buttons : false,
24519     // private
24520     onRender : function(ct, position) 
24521     {
24522         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24523         this.navgroup.parentId = this.id;
24524         this.navgroup.onRender(this.el, null);
24525         // add the buttons to the navgroup
24526         
24527         if(this.displayInfo){
24528             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24529             this.displayEl = this.el.select('.x-paging-info', true).first();
24530 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24531 //            this.displayEl = navel.el.select('span',true).first();
24532         }
24533         
24534         var _this = this;
24535         
24536         if(this.buttons){
24537             Roo.each(_this.buttons, function(e){ // this might need to use render????
24538                Roo.factory(e).render(_this.el);
24539             });
24540         }
24541             
24542         Roo.each(_this.toolbarItems, function(e) {
24543             _this.navgroup.addItem(e);
24544         });
24545         
24546         
24547         this.first = this.navgroup.addItem({
24548             tooltip: this.firstText,
24549             cls: "prev",
24550             icon : 'fa fa-backward',
24551             disabled: true,
24552             preventDefault: true,
24553             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24554         });
24555         
24556         this.prev =  this.navgroup.addItem({
24557             tooltip: this.prevText,
24558             cls: "prev",
24559             icon : 'fa fa-step-backward',
24560             disabled: true,
24561             preventDefault: true,
24562             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24563         });
24564     //this.addSeparator();
24565         
24566         
24567         var field = this.navgroup.addItem( {
24568             tagtype : 'span',
24569             cls : 'x-paging-position',
24570             
24571             html : this.beforePageText  +
24572                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24573                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24574          } ); //?? escaped?
24575         
24576         this.field = field.el.select('input', true).first();
24577         this.field.on("keydown", this.onPagingKeydown, this);
24578         this.field.on("focus", function(){this.dom.select();});
24579     
24580     
24581         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24582         //this.field.setHeight(18);
24583         //this.addSeparator();
24584         this.next = this.navgroup.addItem({
24585             tooltip: this.nextText,
24586             cls: "next",
24587             html : ' <i class="fa fa-step-forward">',
24588             disabled: true,
24589             preventDefault: true,
24590             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24591         });
24592         this.last = this.navgroup.addItem({
24593             tooltip: this.lastText,
24594             icon : 'fa fa-forward',
24595             cls: "next",
24596             disabled: true,
24597             preventDefault: true,
24598             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24599         });
24600     //this.addSeparator();
24601         this.loading = this.navgroup.addItem({
24602             tooltip: this.refreshText,
24603             icon: 'fa fa-refresh',
24604             preventDefault: true,
24605             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24606         });
24607         
24608     },
24609
24610     // private
24611     updateInfo : function(){
24612         if(this.displayEl){
24613             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24614             var msg = count == 0 ?
24615                 this.emptyMsg :
24616                 String.format(
24617                     this.displayMsg,
24618                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24619                 );
24620             this.displayEl.update(msg);
24621         }
24622     },
24623
24624     // private
24625     onLoad : function(ds, r, o)
24626     {
24627         this.cursor = o.params.start ? o.params.start : 0;
24628         
24629         var d = this.getPageData(),
24630             ap = d.activePage,
24631             ps = d.pages;
24632         
24633         
24634         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24635         this.field.dom.value = ap;
24636         this.first.setDisabled(ap == 1);
24637         this.prev.setDisabled(ap == 1);
24638         this.next.setDisabled(ap == ps);
24639         this.last.setDisabled(ap == ps);
24640         this.loading.enable();
24641         this.updateInfo();
24642     },
24643
24644     // private
24645     getPageData : function(){
24646         var total = this.ds.getTotalCount();
24647         return {
24648             total : total,
24649             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24650             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24651         };
24652     },
24653
24654     // private
24655     onLoadError : function(){
24656         this.loading.enable();
24657     },
24658
24659     // private
24660     onPagingKeydown : function(e){
24661         var k = e.getKey();
24662         var d = this.getPageData();
24663         if(k == e.RETURN){
24664             var v = this.field.dom.value, pageNum;
24665             if(!v || isNaN(pageNum = parseInt(v, 10))){
24666                 this.field.dom.value = d.activePage;
24667                 return;
24668             }
24669             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24670             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24671             e.stopEvent();
24672         }
24673         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))
24674         {
24675           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24676           this.field.dom.value = pageNum;
24677           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24678           e.stopEvent();
24679         }
24680         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24681         {
24682           var v = this.field.dom.value, pageNum; 
24683           var increment = (e.shiftKey) ? 10 : 1;
24684           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24685                 increment *= -1;
24686           }
24687           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24688             this.field.dom.value = d.activePage;
24689             return;
24690           }
24691           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24692           {
24693             this.field.dom.value = parseInt(v, 10) + increment;
24694             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24695             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24696           }
24697           e.stopEvent();
24698         }
24699     },
24700
24701     // private
24702     beforeLoad : function(){
24703         if(this.loading){
24704             this.loading.disable();
24705         }
24706     },
24707
24708     // private
24709     onClick : function(which){
24710         
24711         var ds = this.ds;
24712         if (!ds) {
24713             return;
24714         }
24715         
24716         switch(which){
24717             case "first":
24718                 ds.load({params:{start: 0, limit: this.pageSize}});
24719             break;
24720             case "prev":
24721                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24722             break;
24723             case "next":
24724                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24725             break;
24726             case "last":
24727                 var total = ds.getTotalCount();
24728                 var extra = total % this.pageSize;
24729                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24730                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24731             break;
24732             case "refresh":
24733                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24734             break;
24735         }
24736     },
24737
24738     /**
24739      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24740      * @param {Roo.data.Store} store The data store to unbind
24741      */
24742     unbind : function(ds){
24743         ds.un("beforeload", this.beforeLoad, this);
24744         ds.un("load", this.onLoad, this);
24745         ds.un("loadexception", this.onLoadError, this);
24746         ds.un("remove", this.updateInfo, this);
24747         ds.un("add", this.updateInfo, this);
24748         this.ds = undefined;
24749     },
24750
24751     /**
24752      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24753      * @param {Roo.data.Store} store The data store to bind
24754      */
24755     bind : function(ds){
24756         ds.on("beforeload", this.beforeLoad, this);
24757         ds.on("load", this.onLoad, this);
24758         ds.on("loadexception", this.onLoadError, this);
24759         ds.on("remove", this.updateInfo, this);
24760         ds.on("add", this.updateInfo, this);
24761         this.ds = ds;
24762     }
24763 });/*
24764  * - LGPL
24765  *
24766  * element
24767  * 
24768  */
24769
24770 /**
24771  * @class Roo.bootstrap.MessageBar
24772  * @extends Roo.bootstrap.Component
24773  * Bootstrap MessageBar class
24774  * @cfg {String} html contents of the MessageBar
24775  * @cfg {String} weight (info | success | warning | danger) default info
24776  * @cfg {String} beforeClass insert the bar before the given class
24777  * @cfg {Boolean} closable (true | false) default false
24778  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24779  * 
24780  * @constructor
24781  * Create a new Element
24782  * @param {Object} config The config object
24783  */
24784
24785 Roo.bootstrap.MessageBar = function(config){
24786     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24787 };
24788
24789 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24790     
24791     html: '',
24792     weight: 'info',
24793     closable: false,
24794     fixed: false,
24795     beforeClass: 'bootstrap-sticky-wrap',
24796     
24797     getAutoCreate : function(){
24798         
24799         var cfg = {
24800             tag: 'div',
24801             cls: 'alert alert-dismissable alert-' + this.weight,
24802             cn: [
24803                 {
24804                     tag: 'span',
24805                     cls: 'message',
24806                     html: this.html || ''
24807                 }
24808             ]
24809         };
24810         
24811         if(this.fixed){
24812             cfg.cls += ' alert-messages-fixed';
24813         }
24814         
24815         if(this.closable){
24816             cfg.cn.push({
24817                 tag: 'button',
24818                 cls: 'close',
24819                 html: 'x'
24820             });
24821         }
24822         
24823         return cfg;
24824     },
24825     
24826     onRender : function(ct, position)
24827     {
24828         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24829         
24830         if(!this.el){
24831             var cfg = Roo.apply({},  this.getAutoCreate());
24832             cfg.id = Roo.id();
24833             
24834             if (this.cls) {
24835                 cfg.cls += ' ' + this.cls;
24836             }
24837             if (this.style) {
24838                 cfg.style = this.style;
24839             }
24840             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24841             
24842             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24843         }
24844         
24845         this.el.select('>button.close').on('click', this.hide, this);
24846         
24847     },
24848     
24849     show : function()
24850     {
24851         if (!this.rendered) {
24852             this.render();
24853         }
24854         
24855         this.el.show();
24856         
24857         this.fireEvent('show', this);
24858         
24859     },
24860     
24861     hide : function()
24862     {
24863         if (!this.rendered) {
24864             this.render();
24865         }
24866         
24867         this.el.hide();
24868         
24869         this.fireEvent('hide', this);
24870     },
24871     
24872     update : function()
24873     {
24874 //        var e = this.el.dom.firstChild;
24875 //        
24876 //        if(this.closable){
24877 //            e = e.nextSibling;
24878 //        }
24879 //        
24880 //        e.data = this.html || '';
24881
24882         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24883     }
24884    
24885 });
24886
24887  
24888
24889      /*
24890  * - LGPL
24891  *
24892  * Graph
24893  * 
24894  */
24895
24896
24897 /**
24898  * @class Roo.bootstrap.Graph
24899  * @extends Roo.bootstrap.Component
24900  * Bootstrap Graph class
24901 > Prameters
24902  -sm {number} sm 4
24903  -md {number} md 5
24904  @cfg {String} graphtype  bar | vbar | pie
24905  @cfg {number} g_x coodinator | centre x (pie)
24906  @cfg {number} g_y coodinator | centre y (pie)
24907  @cfg {number} g_r radius (pie)
24908  @cfg {number} g_height height of the chart (respected by all elements in the set)
24909  @cfg {number} g_width width of the chart (respected by all elements in the set)
24910  @cfg {Object} title The title of the chart
24911     
24912  -{Array}  values
24913  -opts (object) options for the chart 
24914      o {
24915      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24916      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24917      o vgutter (number)
24918      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.
24919      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24920      o to
24921      o stretch (boolean)
24922      o }
24923  -opts (object) options for the pie
24924      o{
24925      o cut
24926      o startAngle (number)
24927      o endAngle (number)
24928      } 
24929  *
24930  * @constructor
24931  * Create a new Input
24932  * @param {Object} config The config object
24933  */
24934
24935 Roo.bootstrap.Graph = function(config){
24936     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24937     
24938     this.addEvents({
24939         // img events
24940         /**
24941          * @event click
24942          * The img click event for the img.
24943          * @param {Roo.EventObject} e
24944          */
24945         "click" : true
24946     });
24947 };
24948
24949 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24950     
24951     sm: 4,
24952     md: 5,
24953     graphtype: 'bar',
24954     g_height: 250,
24955     g_width: 400,
24956     g_x: 50,
24957     g_y: 50,
24958     g_r: 30,
24959     opts:{
24960         //g_colors: this.colors,
24961         g_type: 'soft',
24962         g_gutter: '20%'
24963
24964     },
24965     title : false,
24966
24967     getAutoCreate : function(){
24968         
24969         var cfg = {
24970             tag: 'div',
24971             html : null
24972         };
24973         
24974         
24975         return  cfg;
24976     },
24977
24978     onRender : function(ct,position){
24979         
24980         
24981         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24982         
24983         if (typeof(Raphael) == 'undefined') {
24984             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24985             return;
24986         }
24987         
24988         this.raphael = Raphael(this.el.dom);
24989         
24990                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24991                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24992                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24993                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24994                 /*
24995                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24996                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24997                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24998                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24999                 
25000                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25001                 r.barchart(330, 10, 300, 220, data1);
25002                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25003                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25004                 */
25005                 
25006                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25007                 // r.barchart(30, 30, 560, 250,  xdata, {
25008                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25009                 //     axis : "0 0 1 1",
25010                 //     axisxlabels :  xdata
25011                 //     //yvalues : cols,
25012                    
25013                 // });
25014 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25015 //        
25016 //        this.load(null,xdata,{
25017 //                axis : "0 0 1 1",
25018 //                axisxlabels :  xdata
25019 //                });
25020
25021     },
25022
25023     load : function(graphtype,xdata,opts)
25024     {
25025         this.raphael.clear();
25026         if(!graphtype) {
25027             graphtype = this.graphtype;
25028         }
25029         if(!opts){
25030             opts = this.opts;
25031         }
25032         var r = this.raphael,
25033             fin = function () {
25034                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25035             },
25036             fout = function () {
25037                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25038             },
25039             pfin = function() {
25040                 this.sector.stop();
25041                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25042
25043                 if (this.label) {
25044                     this.label[0].stop();
25045                     this.label[0].attr({ r: 7.5 });
25046                     this.label[1].attr({ "font-weight": 800 });
25047                 }
25048             },
25049             pfout = function() {
25050                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25051
25052                 if (this.label) {
25053                     this.label[0].animate({ r: 5 }, 500, "bounce");
25054                     this.label[1].attr({ "font-weight": 400 });
25055                 }
25056             };
25057
25058         switch(graphtype){
25059             case 'bar':
25060                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25061                 break;
25062             case 'hbar':
25063                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25064                 break;
25065             case 'pie':
25066 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25067 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25068 //            
25069                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25070                 
25071                 break;
25072
25073         }
25074         
25075         if(this.title){
25076             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25077         }
25078         
25079     },
25080     
25081     setTitle: function(o)
25082     {
25083         this.title = o;
25084     },
25085     
25086     initEvents: function() {
25087         
25088         if(!this.href){
25089             this.el.on('click', this.onClick, this);
25090         }
25091     },
25092     
25093     onClick : function(e)
25094     {
25095         Roo.log('img onclick');
25096         this.fireEvent('click', this, e);
25097     }
25098    
25099 });
25100
25101  
25102 /*
25103  * - LGPL
25104  *
25105  * numberBox
25106  * 
25107  */
25108 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25109
25110 /**
25111  * @class Roo.bootstrap.dash.NumberBox
25112  * @extends Roo.bootstrap.Component
25113  * Bootstrap NumberBox class
25114  * @cfg {String} headline Box headline
25115  * @cfg {String} content Box content
25116  * @cfg {String} icon Box icon
25117  * @cfg {String} footer Footer text
25118  * @cfg {String} fhref Footer href
25119  * 
25120  * @constructor
25121  * Create a new NumberBox
25122  * @param {Object} config The config object
25123  */
25124
25125
25126 Roo.bootstrap.dash.NumberBox = function(config){
25127     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25128     
25129 };
25130
25131 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25132     
25133     headline : '',
25134     content : '',
25135     icon : '',
25136     footer : '',
25137     fhref : '',
25138     ficon : '',
25139     
25140     getAutoCreate : function(){
25141         
25142         var cfg = {
25143             tag : 'div',
25144             cls : 'small-box ',
25145             cn : [
25146                 {
25147                     tag : 'div',
25148                     cls : 'inner',
25149                     cn :[
25150                         {
25151                             tag : 'h3',
25152                             cls : 'roo-headline',
25153                             html : this.headline
25154                         },
25155                         {
25156                             tag : 'p',
25157                             cls : 'roo-content',
25158                             html : this.content
25159                         }
25160                     ]
25161                 }
25162             ]
25163         };
25164         
25165         if(this.icon){
25166             cfg.cn.push({
25167                 tag : 'div',
25168                 cls : 'icon',
25169                 cn :[
25170                     {
25171                         tag : 'i',
25172                         cls : 'ion ' + this.icon
25173                     }
25174                 ]
25175             });
25176         }
25177         
25178         if(this.footer){
25179             var footer = {
25180                 tag : 'a',
25181                 cls : 'small-box-footer',
25182                 href : this.fhref || '#',
25183                 html : this.footer
25184             };
25185             
25186             cfg.cn.push(footer);
25187             
25188         }
25189         
25190         return  cfg;
25191     },
25192
25193     onRender : function(ct,position){
25194         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25195
25196
25197        
25198                 
25199     },
25200
25201     setHeadline: function (value)
25202     {
25203         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25204     },
25205     
25206     setFooter: function (value, href)
25207     {
25208         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25209         
25210         if(href){
25211             this.el.select('a.small-box-footer',true).first().attr('href', href);
25212         }
25213         
25214     },
25215
25216     setContent: function (value)
25217     {
25218         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25219     },
25220
25221     initEvents: function() 
25222     {   
25223         
25224     }
25225     
25226 });
25227
25228  
25229 /*
25230  * - LGPL
25231  *
25232  * TabBox
25233  * 
25234  */
25235 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25236
25237 /**
25238  * @class Roo.bootstrap.dash.TabBox
25239  * @extends Roo.bootstrap.Component
25240  * Bootstrap TabBox class
25241  * @cfg {String} title Title of the TabBox
25242  * @cfg {String} icon Icon of the TabBox
25243  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25244  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25245  * 
25246  * @constructor
25247  * Create a new TabBox
25248  * @param {Object} config The config object
25249  */
25250
25251
25252 Roo.bootstrap.dash.TabBox = function(config){
25253     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25254     this.addEvents({
25255         // raw events
25256         /**
25257          * @event addpane
25258          * When a pane is added
25259          * @param {Roo.bootstrap.dash.TabPane} pane
25260          */
25261         "addpane" : true,
25262         /**
25263          * @event activatepane
25264          * When a pane is activated
25265          * @param {Roo.bootstrap.dash.TabPane} pane
25266          */
25267         "activatepane" : true
25268         
25269          
25270     });
25271     
25272     this.panes = [];
25273 };
25274
25275 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25276
25277     title : '',
25278     icon : false,
25279     showtabs : true,
25280     tabScrollable : false,
25281     
25282     getChildContainer : function()
25283     {
25284         return this.el.select('.tab-content', true).first();
25285     },
25286     
25287     getAutoCreate : function(){
25288         
25289         var header = {
25290             tag: 'li',
25291             cls: 'pull-left header',
25292             html: this.title,
25293             cn : []
25294         };
25295         
25296         if(this.icon){
25297             header.cn.push({
25298                 tag: 'i',
25299                 cls: 'fa ' + this.icon
25300             });
25301         }
25302         
25303         var h = {
25304             tag: 'ul',
25305             cls: 'nav nav-tabs pull-right',
25306             cn: [
25307                 header
25308             ]
25309         };
25310         
25311         if(this.tabScrollable){
25312             h = {
25313                 tag: 'div',
25314                 cls: 'tab-header',
25315                 cn: [
25316                     {
25317                         tag: 'ul',
25318                         cls: 'nav nav-tabs pull-right',
25319                         cn: [
25320                             header
25321                         ]
25322                     }
25323                 ]
25324             };
25325         }
25326         
25327         var cfg = {
25328             tag: 'div',
25329             cls: 'nav-tabs-custom',
25330             cn: [
25331                 h,
25332                 {
25333                     tag: 'div',
25334                     cls: 'tab-content no-padding',
25335                     cn: []
25336                 }
25337             ]
25338         };
25339
25340         return  cfg;
25341     },
25342     initEvents : function()
25343     {
25344         //Roo.log('add add pane handler');
25345         this.on('addpane', this.onAddPane, this);
25346     },
25347      /**
25348      * Updates the box title
25349      * @param {String} html to set the title to.
25350      */
25351     setTitle : function(value)
25352     {
25353         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25354     },
25355     onAddPane : function(pane)
25356     {
25357         this.panes.push(pane);
25358         //Roo.log('addpane');
25359         //Roo.log(pane);
25360         // tabs are rendere left to right..
25361         if(!this.showtabs){
25362             return;
25363         }
25364         
25365         var ctr = this.el.select('.nav-tabs', true).first();
25366          
25367          
25368         var existing = ctr.select('.nav-tab',true);
25369         var qty = existing.getCount();;
25370         
25371         
25372         var tab = ctr.createChild({
25373             tag : 'li',
25374             cls : 'nav-tab' + (qty ? '' : ' active'),
25375             cn : [
25376                 {
25377                     tag : 'a',
25378                     href:'#',
25379                     html : pane.title
25380                 }
25381             ]
25382         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25383         pane.tab = tab;
25384         
25385         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25386         if (!qty) {
25387             pane.el.addClass('active');
25388         }
25389         
25390                 
25391     },
25392     onTabClick : function(ev,un,ob,pane)
25393     {
25394         //Roo.log('tab - prev default');
25395         ev.preventDefault();
25396         
25397         
25398         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25399         pane.tab.addClass('active');
25400         //Roo.log(pane.title);
25401         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25402         // technically we should have a deactivate event.. but maybe add later.
25403         // and it should not de-activate the selected tab...
25404         this.fireEvent('activatepane', pane);
25405         pane.el.addClass('active');
25406         pane.fireEvent('activate');
25407         
25408         
25409     },
25410     
25411     getActivePane : function()
25412     {
25413         var r = false;
25414         Roo.each(this.panes, function(p) {
25415             if(p.el.hasClass('active')){
25416                 r = p;
25417                 return false;
25418             }
25419             
25420             return;
25421         });
25422         
25423         return r;
25424     }
25425     
25426     
25427 });
25428
25429  
25430 /*
25431  * - LGPL
25432  *
25433  * Tab pane
25434  * 
25435  */
25436 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25437 /**
25438  * @class Roo.bootstrap.TabPane
25439  * @extends Roo.bootstrap.Component
25440  * Bootstrap TabPane class
25441  * @cfg {Boolean} active (false | true) Default false
25442  * @cfg {String} title title of panel
25443
25444  * 
25445  * @constructor
25446  * Create a new TabPane
25447  * @param {Object} config The config object
25448  */
25449
25450 Roo.bootstrap.dash.TabPane = function(config){
25451     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25452     
25453     this.addEvents({
25454         // raw events
25455         /**
25456          * @event activate
25457          * When a pane is activated
25458          * @param {Roo.bootstrap.dash.TabPane} pane
25459          */
25460         "activate" : true
25461          
25462     });
25463 };
25464
25465 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25466     
25467     active : false,
25468     title : '',
25469     
25470     // the tabBox that this is attached to.
25471     tab : false,
25472      
25473     getAutoCreate : function() 
25474     {
25475         var cfg = {
25476             tag: 'div',
25477             cls: 'tab-pane'
25478         };
25479         
25480         if(this.active){
25481             cfg.cls += ' active';
25482         }
25483         
25484         return cfg;
25485     },
25486     initEvents  : function()
25487     {
25488         //Roo.log('trigger add pane handler');
25489         this.parent().fireEvent('addpane', this)
25490     },
25491     
25492      /**
25493      * Updates the tab title 
25494      * @param {String} html to set the title to.
25495      */
25496     setTitle: function(str)
25497     {
25498         if (!this.tab) {
25499             return;
25500         }
25501         this.title = str;
25502         this.tab.select('a', true).first().dom.innerHTML = str;
25503         
25504     }
25505     
25506     
25507     
25508 });
25509
25510  
25511
25512
25513  /*
25514  * - LGPL
25515  *
25516  * menu
25517  * 
25518  */
25519 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25520
25521 /**
25522  * @class Roo.bootstrap.menu.Menu
25523  * @extends Roo.bootstrap.Component
25524  * Bootstrap Menu class - container for Menu
25525  * @cfg {String} html Text of the menu
25526  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25527  * @cfg {String} icon Font awesome icon
25528  * @cfg {String} pos Menu align to (top | bottom) default bottom
25529  * 
25530  * 
25531  * @constructor
25532  * Create a new Menu
25533  * @param {Object} config The config object
25534  */
25535
25536
25537 Roo.bootstrap.menu.Menu = function(config){
25538     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25539     
25540     this.addEvents({
25541         /**
25542          * @event beforeshow
25543          * Fires before this menu is displayed
25544          * @param {Roo.bootstrap.menu.Menu} this
25545          */
25546         beforeshow : true,
25547         /**
25548          * @event beforehide
25549          * Fires before this menu is hidden
25550          * @param {Roo.bootstrap.menu.Menu} this
25551          */
25552         beforehide : true,
25553         /**
25554          * @event show
25555          * Fires after this menu is displayed
25556          * @param {Roo.bootstrap.menu.Menu} this
25557          */
25558         show : true,
25559         /**
25560          * @event hide
25561          * Fires after this menu is hidden
25562          * @param {Roo.bootstrap.menu.Menu} this
25563          */
25564         hide : true,
25565         /**
25566          * @event click
25567          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25568          * @param {Roo.bootstrap.menu.Menu} this
25569          * @param {Roo.EventObject} e
25570          */
25571         click : true
25572     });
25573     
25574 };
25575
25576 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25577     
25578     submenu : false,
25579     html : '',
25580     weight : 'default',
25581     icon : false,
25582     pos : 'bottom',
25583     
25584     
25585     getChildContainer : function() {
25586         if(this.isSubMenu){
25587             return this.el;
25588         }
25589         
25590         return this.el.select('ul.dropdown-menu', true).first();  
25591     },
25592     
25593     getAutoCreate : function()
25594     {
25595         var text = [
25596             {
25597                 tag : 'span',
25598                 cls : 'roo-menu-text',
25599                 html : this.html
25600             }
25601         ];
25602         
25603         if(this.icon){
25604             text.unshift({
25605                 tag : 'i',
25606                 cls : 'fa ' + this.icon
25607             })
25608         }
25609         
25610         
25611         var cfg = {
25612             tag : 'div',
25613             cls : 'btn-group',
25614             cn : [
25615                 {
25616                     tag : 'button',
25617                     cls : 'dropdown-button btn btn-' + this.weight,
25618                     cn : text
25619                 },
25620                 {
25621                     tag : 'button',
25622                     cls : 'dropdown-toggle btn btn-' + this.weight,
25623                     cn : [
25624                         {
25625                             tag : 'span',
25626                             cls : 'caret'
25627                         }
25628                     ]
25629                 },
25630                 {
25631                     tag : 'ul',
25632                     cls : 'dropdown-menu'
25633                 }
25634             ]
25635             
25636         };
25637         
25638         if(this.pos == 'top'){
25639             cfg.cls += ' dropup';
25640         }
25641         
25642         if(this.isSubMenu){
25643             cfg = {
25644                 tag : 'ul',
25645                 cls : 'dropdown-menu'
25646             }
25647         }
25648         
25649         return cfg;
25650     },
25651     
25652     onRender : function(ct, position)
25653     {
25654         this.isSubMenu = ct.hasClass('dropdown-submenu');
25655         
25656         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25657     },
25658     
25659     initEvents : function() 
25660     {
25661         if(this.isSubMenu){
25662             return;
25663         }
25664         
25665         this.hidden = true;
25666         
25667         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25668         this.triggerEl.on('click', this.onTriggerPress, this);
25669         
25670         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25671         this.buttonEl.on('click', this.onClick, this);
25672         
25673     },
25674     
25675     list : function()
25676     {
25677         if(this.isSubMenu){
25678             return this.el;
25679         }
25680         
25681         return this.el.select('ul.dropdown-menu', true).first();
25682     },
25683     
25684     onClick : function(e)
25685     {
25686         this.fireEvent("click", this, e);
25687     },
25688     
25689     onTriggerPress  : function(e)
25690     {   
25691         if (this.isVisible()) {
25692             this.hide();
25693         } else {
25694             this.show();
25695         }
25696     },
25697     
25698     isVisible : function(){
25699         return !this.hidden;
25700     },
25701     
25702     show : function()
25703     {
25704         this.fireEvent("beforeshow", this);
25705         
25706         this.hidden = false;
25707         this.el.addClass('open');
25708         
25709         Roo.get(document).on("mouseup", this.onMouseUp, this);
25710         
25711         this.fireEvent("show", this);
25712         
25713         
25714     },
25715     
25716     hide : function()
25717     {
25718         this.fireEvent("beforehide", this);
25719         
25720         this.hidden = true;
25721         this.el.removeClass('open');
25722         
25723         Roo.get(document).un("mouseup", this.onMouseUp);
25724         
25725         this.fireEvent("hide", this);
25726     },
25727     
25728     onMouseUp : function()
25729     {
25730         this.hide();
25731     }
25732     
25733 });
25734
25735  
25736  /*
25737  * - LGPL
25738  *
25739  * menu item
25740  * 
25741  */
25742 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25743
25744 /**
25745  * @class Roo.bootstrap.menu.Item
25746  * @extends Roo.bootstrap.Component
25747  * Bootstrap MenuItem class
25748  * @cfg {Boolean} submenu (true | false) default false
25749  * @cfg {String} html text of the item
25750  * @cfg {String} href the link
25751  * @cfg {Boolean} disable (true | false) default false
25752  * @cfg {Boolean} preventDefault (true | false) default true
25753  * @cfg {String} icon Font awesome icon
25754  * @cfg {String} pos Submenu align to (left | right) default right 
25755  * 
25756  * 
25757  * @constructor
25758  * Create a new Item
25759  * @param {Object} config The config object
25760  */
25761
25762
25763 Roo.bootstrap.menu.Item = function(config){
25764     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25765     this.addEvents({
25766         /**
25767          * @event mouseover
25768          * Fires when the mouse is hovering over this menu
25769          * @param {Roo.bootstrap.menu.Item} this
25770          * @param {Roo.EventObject} e
25771          */
25772         mouseover : true,
25773         /**
25774          * @event mouseout
25775          * Fires when the mouse exits this menu
25776          * @param {Roo.bootstrap.menu.Item} this
25777          * @param {Roo.EventObject} e
25778          */
25779         mouseout : true,
25780         // raw events
25781         /**
25782          * @event click
25783          * The raw click event for the entire grid.
25784          * @param {Roo.EventObject} e
25785          */
25786         click : true
25787     });
25788 };
25789
25790 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25791     
25792     submenu : false,
25793     href : '',
25794     html : '',
25795     preventDefault: true,
25796     disable : false,
25797     icon : false,
25798     pos : 'right',
25799     
25800     getAutoCreate : function()
25801     {
25802         var text = [
25803             {
25804                 tag : 'span',
25805                 cls : 'roo-menu-item-text',
25806                 html : this.html
25807             }
25808         ];
25809         
25810         if(this.icon){
25811             text.unshift({
25812                 tag : 'i',
25813                 cls : 'fa ' + this.icon
25814             })
25815         }
25816         
25817         var cfg = {
25818             tag : 'li',
25819             cn : [
25820                 {
25821                     tag : 'a',
25822                     href : this.href || '#',
25823                     cn : text
25824                 }
25825             ]
25826         };
25827         
25828         if(this.disable){
25829             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25830         }
25831         
25832         if(this.submenu){
25833             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25834             
25835             if(this.pos == 'left'){
25836                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25837             }
25838         }
25839         
25840         return cfg;
25841     },
25842     
25843     initEvents : function() 
25844     {
25845         this.el.on('mouseover', this.onMouseOver, this);
25846         this.el.on('mouseout', this.onMouseOut, this);
25847         
25848         this.el.select('a', true).first().on('click', this.onClick, this);
25849         
25850     },
25851     
25852     onClick : function(e)
25853     {
25854         if(this.preventDefault){
25855             e.preventDefault();
25856         }
25857         
25858         this.fireEvent("click", this, e);
25859     },
25860     
25861     onMouseOver : function(e)
25862     {
25863         if(this.submenu && this.pos == 'left'){
25864             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25865         }
25866         
25867         this.fireEvent("mouseover", this, e);
25868     },
25869     
25870     onMouseOut : function(e)
25871     {
25872         this.fireEvent("mouseout", this, e);
25873     }
25874 });
25875
25876  
25877
25878  /*
25879  * - LGPL
25880  *
25881  * menu separator
25882  * 
25883  */
25884 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25885
25886 /**
25887  * @class Roo.bootstrap.menu.Separator
25888  * @extends Roo.bootstrap.Component
25889  * Bootstrap Separator class
25890  * 
25891  * @constructor
25892  * Create a new Separator
25893  * @param {Object} config The config object
25894  */
25895
25896
25897 Roo.bootstrap.menu.Separator = function(config){
25898     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25899 };
25900
25901 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25902     
25903     getAutoCreate : function(){
25904         var cfg = {
25905             tag : 'li',
25906             cls: 'divider'
25907         };
25908         
25909         return cfg;
25910     }
25911    
25912 });
25913
25914  
25915
25916  /*
25917  * - LGPL
25918  *
25919  * Tooltip
25920  * 
25921  */
25922
25923 /**
25924  * @class Roo.bootstrap.Tooltip
25925  * Bootstrap Tooltip class
25926  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25927  * to determine which dom element triggers the tooltip.
25928  * 
25929  * It needs to add support for additional attributes like tooltip-position
25930  * 
25931  * @constructor
25932  * Create a new Toolti
25933  * @param {Object} config The config object
25934  */
25935
25936 Roo.bootstrap.Tooltip = function(config){
25937     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25938     
25939     this.alignment = Roo.bootstrap.Tooltip.alignment;
25940     
25941     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25942         this.alignment = config.alignment;
25943     }
25944     
25945 };
25946
25947 Roo.apply(Roo.bootstrap.Tooltip, {
25948     /**
25949      * @function init initialize tooltip monitoring.
25950      * @static
25951      */
25952     currentEl : false,
25953     currentTip : false,
25954     currentRegion : false,
25955     
25956     //  init : delay?
25957     
25958     init : function()
25959     {
25960         Roo.get(document).on('mouseover', this.enter ,this);
25961         Roo.get(document).on('mouseout', this.leave, this);
25962          
25963         
25964         this.currentTip = new Roo.bootstrap.Tooltip();
25965     },
25966     
25967     enter : function(ev)
25968     {
25969         var dom = ev.getTarget();
25970         
25971         //Roo.log(['enter',dom]);
25972         var el = Roo.fly(dom);
25973         if (this.currentEl) {
25974             //Roo.log(dom);
25975             //Roo.log(this.currentEl);
25976             //Roo.log(this.currentEl.contains(dom));
25977             if (this.currentEl == el) {
25978                 return;
25979             }
25980             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25981                 return;
25982             }
25983
25984         }
25985         
25986         if (this.currentTip.el) {
25987             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25988         }    
25989         //Roo.log(ev);
25990         
25991         if(!el || el.dom == document){
25992             return;
25993         }
25994         
25995         var bindEl = el;
25996         
25997         // you can not look for children, as if el is the body.. then everythign is the child..
25998         if (!el.attr('tooltip')) { //
25999             if (!el.select("[tooltip]").elements.length) {
26000                 return;
26001             }
26002             // is the mouse over this child...?
26003             bindEl = el.select("[tooltip]").first();
26004             var xy = ev.getXY();
26005             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26006                 //Roo.log("not in region.");
26007                 return;
26008             }
26009             //Roo.log("child element over..");
26010             
26011         }
26012         this.currentEl = bindEl;
26013         this.currentTip.bind(bindEl);
26014         this.currentRegion = Roo.lib.Region.getRegion(dom);
26015         this.currentTip.enter();
26016         
26017     },
26018     leave : function(ev)
26019     {
26020         var dom = ev.getTarget();
26021         //Roo.log(['leave',dom]);
26022         if (!this.currentEl) {
26023             return;
26024         }
26025         
26026         
26027         if (dom != this.currentEl.dom) {
26028             return;
26029         }
26030         var xy = ev.getXY();
26031         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26032             return;
26033         }
26034         // only activate leave if mouse cursor is outside... bounding box..
26035         
26036         
26037         
26038         
26039         if (this.currentTip) {
26040             this.currentTip.leave();
26041         }
26042         //Roo.log('clear currentEl');
26043         this.currentEl = false;
26044         
26045         
26046     },
26047     alignment : {
26048         'left' : ['r-l', [-2,0], 'right'],
26049         'right' : ['l-r', [2,0], 'left'],
26050         'bottom' : ['t-b', [0,2], 'top'],
26051         'top' : [ 'b-t', [0,-2], 'bottom']
26052     }
26053     
26054 });
26055
26056
26057 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26058     
26059     
26060     bindEl : false,
26061     
26062     delay : null, // can be { show : 300 , hide: 500}
26063     
26064     timeout : null,
26065     
26066     hoverState : null, //???
26067     
26068     placement : 'bottom', 
26069     
26070     alignment : false,
26071     
26072     getAutoCreate : function(){
26073     
26074         var cfg = {
26075            cls : 'tooltip',
26076            role : 'tooltip',
26077            cn : [
26078                 {
26079                     cls : 'tooltip-arrow'
26080                 },
26081                 {
26082                     cls : 'tooltip-inner'
26083                 }
26084            ]
26085         };
26086         
26087         return cfg;
26088     },
26089     bind : function(el)
26090     {
26091         this.bindEl = el;
26092     },
26093       
26094     
26095     enter : function () {
26096        
26097         if (this.timeout != null) {
26098             clearTimeout(this.timeout);
26099         }
26100         
26101         this.hoverState = 'in';
26102          //Roo.log("enter - show");
26103         if (!this.delay || !this.delay.show) {
26104             this.show();
26105             return;
26106         }
26107         var _t = this;
26108         this.timeout = setTimeout(function () {
26109             if (_t.hoverState == 'in') {
26110                 _t.show();
26111             }
26112         }, this.delay.show);
26113     },
26114     leave : function()
26115     {
26116         clearTimeout(this.timeout);
26117     
26118         this.hoverState = 'out';
26119          if (!this.delay || !this.delay.hide) {
26120             this.hide();
26121             return;
26122         }
26123        
26124         var _t = this;
26125         this.timeout = setTimeout(function () {
26126             //Roo.log("leave - timeout");
26127             
26128             if (_t.hoverState == 'out') {
26129                 _t.hide();
26130                 Roo.bootstrap.Tooltip.currentEl = false;
26131             }
26132         }, delay);
26133     },
26134     
26135     show : function (msg)
26136     {
26137         if (!this.el) {
26138             this.render(document.body);
26139         }
26140         // set content.
26141         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26142         
26143         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26144         
26145         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26146         
26147         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26148         
26149         var placement = typeof this.placement == 'function' ?
26150             this.placement.call(this, this.el, on_el) :
26151             this.placement;
26152             
26153         var autoToken = /\s?auto?\s?/i;
26154         var autoPlace = autoToken.test(placement);
26155         if (autoPlace) {
26156             placement = placement.replace(autoToken, '') || 'top';
26157         }
26158         
26159         //this.el.detach()
26160         //this.el.setXY([0,0]);
26161         this.el.show();
26162         //this.el.dom.style.display='block';
26163         
26164         //this.el.appendTo(on_el);
26165         
26166         var p = this.getPosition();
26167         var box = this.el.getBox();
26168         
26169         if (autoPlace) {
26170             // fixme..
26171         }
26172         
26173         var align = this.alignment[placement];
26174         
26175         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26176         
26177         if(placement == 'top' || placement == 'bottom'){
26178             if(xy[0] < 0){
26179                 placement = 'right';
26180             }
26181             
26182             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26183                 placement = 'left';
26184             }
26185             
26186             var scroll = Roo.select('body', true).first().getScroll();
26187             
26188             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26189                 placement = 'top';
26190             }
26191             
26192         }
26193         
26194         this.el.alignTo(this.bindEl, align[0],align[1]);
26195         //var arrow = this.el.select('.arrow',true).first();
26196         //arrow.set(align[2], 
26197         
26198         this.el.addClass(placement);
26199         
26200         this.el.addClass('in fade');
26201         
26202         this.hoverState = null;
26203         
26204         if (this.el.hasClass('fade')) {
26205             // fade it?
26206         }
26207         
26208     },
26209     hide : function()
26210     {
26211          
26212         if (!this.el) {
26213             return;
26214         }
26215         //this.el.setXY([0,0]);
26216         this.el.removeClass('in');
26217         //this.el.hide();
26218         
26219     }
26220     
26221 });
26222  
26223
26224  /*
26225  * - LGPL
26226  *
26227  * Location Picker
26228  * 
26229  */
26230
26231 /**
26232  * @class Roo.bootstrap.LocationPicker
26233  * @extends Roo.bootstrap.Component
26234  * Bootstrap LocationPicker class
26235  * @cfg {Number} latitude Position when init default 0
26236  * @cfg {Number} longitude Position when init default 0
26237  * @cfg {Number} zoom default 15
26238  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26239  * @cfg {Boolean} mapTypeControl default false
26240  * @cfg {Boolean} disableDoubleClickZoom default false
26241  * @cfg {Boolean} scrollwheel default true
26242  * @cfg {Boolean} streetViewControl default false
26243  * @cfg {Number} radius default 0
26244  * @cfg {String} locationName
26245  * @cfg {Boolean} draggable default true
26246  * @cfg {Boolean} enableAutocomplete default false
26247  * @cfg {Boolean} enableReverseGeocode default true
26248  * @cfg {String} markerTitle
26249  * 
26250  * @constructor
26251  * Create a new LocationPicker
26252  * @param {Object} config The config object
26253  */
26254
26255
26256 Roo.bootstrap.LocationPicker = function(config){
26257     
26258     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26259     
26260     this.addEvents({
26261         /**
26262          * @event initial
26263          * Fires when the picker initialized.
26264          * @param {Roo.bootstrap.LocationPicker} this
26265          * @param {Google Location} location
26266          */
26267         initial : true,
26268         /**
26269          * @event positionchanged
26270          * Fires when the picker position changed.
26271          * @param {Roo.bootstrap.LocationPicker} this
26272          * @param {Google Location} location
26273          */
26274         positionchanged : true,
26275         /**
26276          * @event resize
26277          * Fires when the map resize.
26278          * @param {Roo.bootstrap.LocationPicker} this
26279          */
26280         resize : true,
26281         /**
26282          * @event show
26283          * Fires when the map show.
26284          * @param {Roo.bootstrap.LocationPicker} this
26285          */
26286         show : true,
26287         /**
26288          * @event hide
26289          * Fires when the map hide.
26290          * @param {Roo.bootstrap.LocationPicker} this
26291          */
26292         hide : true,
26293         /**
26294          * @event mapClick
26295          * Fires when click the map.
26296          * @param {Roo.bootstrap.LocationPicker} this
26297          * @param {Map event} e
26298          */
26299         mapClick : true,
26300         /**
26301          * @event mapRightClick
26302          * Fires when right click the map.
26303          * @param {Roo.bootstrap.LocationPicker} this
26304          * @param {Map event} e
26305          */
26306         mapRightClick : true,
26307         /**
26308          * @event markerClick
26309          * Fires when click the marker.
26310          * @param {Roo.bootstrap.LocationPicker} this
26311          * @param {Map event} e
26312          */
26313         markerClick : true,
26314         /**
26315          * @event markerRightClick
26316          * Fires when right click the marker.
26317          * @param {Roo.bootstrap.LocationPicker} this
26318          * @param {Map event} e
26319          */
26320         markerRightClick : true,
26321         /**
26322          * @event OverlayViewDraw
26323          * Fires when OverlayView Draw
26324          * @param {Roo.bootstrap.LocationPicker} this
26325          */
26326         OverlayViewDraw : true,
26327         /**
26328          * @event OverlayViewOnAdd
26329          * Fires when OverlayView Draw
26330          * @param {Roo.bootstrap.LocationPicker} this
26331          */
26332         OverlayViewOnAdd : true,
26333         /**
26334          * @event OverlayViewOnRemove
26335          * Fires when OverlayView Draw
26336          * @param {Roo.bootstrap.LocationPicker} this
26337          */
26338         OverlayViewOnRemove : true,
26339         /**
26340          * @event OverlayViewShow
26341          * Fires when OverlayView Draw
26342          * @param {Roo.bootstrap.LocationPicker} this
26343          * @param {Pixel} cpx
26344          */
26345         OverlayViewShow : true,
26346         /**
26347          * @event OverlayViewHide
26348          * Fires when OverlayView Draw
26349          * @param {Roo.bootstrap.LocationPicker} this
26350          */
26351         OverlayViewHide : true,
26352         /**
26353          * @event loadexception
26354          * Fires when load google lib failed.
26355          * @param {Roo.bootstrap.LocationPicker} this
26356          */
26357         loadexception : true
26358     });
26359         
26360 };
26361
26362 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26363     
26364     gMapContext: false,
26365     
26366     latitude: 0,
26367     longitude: 0,
26368     zoom: 15,
26369     mapTypeId: false,
26370     mapTypeControl: false,
26371     disableDoubleClickZoom: false,
26372     scrollwheel: true,
26373     streetViewControl: false,
26374     radius: 0,
26375     locationName: '',
26376     draggable: true,
26377     enableAutocomplete: false,
26378     enableReverseGeocode: true,
26379     markerTitle: '',
26380     
26381     getAutoCreate: function()
26382     {
26383
26384         var cfg = {
26385             tag: 'div',
26386             cls: 'roo-location-picker'
26387         };
26388         
26389         return cfg
26390     },
26391     
26392     initEvents: function(ct, position)
26393     {       
26394         if(!this.el.getWidth() || this.isApplied()){
26395             return;
26396         }
26397         
26398         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26399         
26400         this.initial();
26401     },
26402     
26403     initial: function()
26404     {
26405         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26406             this.fireEvent('loadexception', this);
26407             return;
26408         }
26409         
26410         if(!this.mapTypeId){
26411             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26412         }
26413         
26414         this.gMapContext = this.GMapContext();
26415         
26416         this.initOverlayView();
26417         
26418         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26419         
26420         var _this = this;
26421                 
26422         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26423             _this.setPosition(_this.gMapContext.marker.position);
26424         });
26425         
26426         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26427             _this.fireEvent('mapClick', this, event);
26428             
26429         });
26430
26431         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26432             _this.fireEvent('mapRightClick', this, event);
26433             
26434         });
26435         
26436         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26437             _this.fireEvent('markerClick', this, event);
26438             
26439         });
26440
26441         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26442             _this.fireEvent('markerRightClick', this, event);
26443             
26444         });
26445         
26446         this.setPosition(this.gMapContext.location);
26447         
26448         this.fireEvent('initial', this, this.gMapContext.location);
26449     },
26450     
26451     initOverlayView: function()
26452     {
26453         var _this = this;
26454         
26455         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26456             
26457             draw: function()
26458             {
26459                 _this.fireEvent('OverlayViewDraw', _this);
26460             },
26461             
26462             onAdd: function()
26463             {
26464                 _this.fireEvent('OverlayViewOnAdd', _this);
26465             },
26466             
26467             onRemove: function()
26468             {
26469                 _this.fireEvent('OverlayViewOnRemove', _this);
26470             },
26471             
26472             show: function(cpx)
26473             {
26474                 _this.fireEvent('OverlayViewShow', _this, cpx);
26475             },
26476             
26477             hide: function()
26478             {
26479                 _this.fireEvent('OverlayViewHide', _this);
26480             }
26481             
26482         });
26483     },
26484     
26485     fromLatLngToContainerPixel: function(event)
26486     {
26487         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26488     },
26489     
26490     isApplied: function() 
26491     {
26492         return this.getGmapContext() == false ? false : true;
26493     },
26494     
26495     getGmapContext: function() 
26496     {
26497         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26498     },
26499     
26500     GMapContext: function() 
26501     {
26502         var position = new google.maps.LatLng(this.latitude, this.longitude);
26503         
26504         var _map = new google.maps.Map(this.el.dom, {
26505             center: position,
26506             zoom: this.zoom,
26507             mapTypeId: this.mapTypeId,
26508             mapTypeControl: this.mapTypeControl,
26509             disableDoubleClickZoom: this.disableDoubleClickZoom,
26510             scrollwheel: this.scrollwheel,
26511             streetViewControl: this.streetViewControl,
26512             locationName: this.locationName,
26513             draggable: this.draggable,
26514             enableAutocomplete: this.enableAutocomplete,
26515             enableReverseGeocode: this.enableReverseGeocode
26516         });
26517         
26518         var _marker = new google.maps.Marker({
26519             position: position,
26520             map: _map,
26521             title: this.markerTitle,
26522             draggable: this.draggable
26523         });
26524         
26525         return {
26526             map: _map,
26527             marker: _marker,
26528             circle: null,
26529             location: position,
26530             radius: this.radius,
26531             locationName: this.locationName,
26532             addressComponents: {
26533                 formatted_address: null,
26534                 addressLine1: null,
26535                 addressLine2: null,
26536                 streetName: null,
26537                 streetNumber: null,
26538                 city: null,
26539                 district: null,
26540                 state: null,
26541                 stateOrProvince: null
26542             },
26543             settings: this,
26544             domContainer: this.el.dom,
26545             geodecoder: new google.maps.Geocoder()
26546         };
26547     },
26548     
26549     drawCircle: function(center, radius, options) 
26550     {
26551         if (this.gMapContext.circle != null) {
26552             this.gMapContext.circle.setMap(null);
26553         }
26554         if (radius > 0) {
26555             radius *= 1;
26556             options = Roo.apply({}, options, {
26557                 strokeColor: "#0000FF",
26558                 strokeOpacity: .35,
26559                 strokeWeight: 2,
26560                 fillColor: "#0000FF",
26561                 fillOpacity: .2
26562             });
26563             
26564             options.map = this.gMapContext.map;
26565             options.radius = radius;
26566             options.center = center;
26567             this.gMapContext.circle = new google.maps.Circle(options);
26568             return this.gMapContext.circle;
26569         }
26570         
26571         return null;
26572     },
26573     
26574     setPosition: function(location) 
26575     {
26576         this.gMapContext.location = location;
26577         this.gMapContext.marker.setPosition(location);
26578         this.gMapContext.map.panTo(location);
26579         this.drawCircle(location, this.gMapContext.radius, {});
26580         
26581         var _this = this;
26582         
26583         if (this.gMapContext.settings.enableReverseGeocode) {
26584             this.gMapContext.geodecoder.geocode({
26585                 latLng: this.gMapContext.location
26586             }, function(results, status) {
26587                 
26588                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26589                     _this.gMapContext.locationName = results[0].formatted_address;
26590                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26591                     
26592                     _this.fireEvent('positionchanged', this, location);
26593                 }
26594             });
26595             
26596             return;
26597         }
26598         
26599         this.fireEvent('positionchanged', this, location);
26600     },
26601     
26602     resize: function()
26603     {
26604         google.maps.event.trigger(this.gMapContext.map, "resize");
26605         
26606         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26607         
26608         this.fireEvent('resize', this);
26609     },
26610     
26611     setPositionByLatLng: function(latitude, longitude)
26612     {
26613         this.setPosition(new google.maps.LatLng(latitude, longitude));
26614     },
26615     
26616     getCurrentPosition: function() 
26617     {
26618         return {
26619             latitude: this.gMapContext.location.lat(),
26620             longitude: this.gMapContext.location.lng()
26621         };
26622     },
26623     
26624     getAddressName: function() 
26625     {
26626         return this.gMapContext.locationName;
26627     },
26628     
26629     getAddressComponents: function() 
26630     {
26631         return this.gMapContext.addressComponents;
26632     },
26633     
26634     address_component_from_google_geocode: function(address_components) 
26635     {
26636         var result = {};
26637         
26638         for (var i = 0; i < address_components.length; i++) {
26639             var component = address_components[i];
26640             if (component.types.indexOf("postal_code") >= 0) {
26641                 result.postalCode = component.short_name;
26642             } else if (component.types.indexOf("street_number") >= 0) {
26643                 result.streetNumber = component.short_name;
26644             } else if (component.types.indexOf("route") >= 0) {
26645                 result.streetName = component.short_name;
26646             } else if (component.types.indexOf("neighborhood") >= 0) {
26647                 result.city = component.short_name;
26648             } else if (component.types.indexOf("locality") >= 0) {
26649                 result.city = component.short_name;
26650             } else if (component.types.indexOf("sublocality") >= 0) {
26651                 result.district = component.short_name;
26652             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26653                 result.stateOrProvince = component.short_name;
26654             } else if (component.types.indexOf("country") >= 0) {
26655                 result.country = component.short_name;
26656             }
26657         }
26658         
26659         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26660         result.addressLine2 = "";
26661         return result;
26662     },
26663     
26664     setZoomLevel: function(zoom)
26665     {
26666         this.gMapContext.map.setZoom(zoom);
26667     },
26668     
26669     show: function()
26670     {
26671         if(!this.el){
26672             return;
26673         }
26674         
26675         this.el.show();
26676         
26677         this.resize();
26678         
26679         this.fireEvent('show', this);
26680     },
26681     
26682     hide: function()
26683     {
26684         if(!this.el){
26685             return;
26686         }
26687         
26688         this.el.hide();
26689         
26690         this.fireEvent('hide', this);
26691     }
26692     
26693 });
26694
26695 Roo.apply(Roo.bootstrap.LocationPicker, {
26696     
26697     OverlayView : function(map, options)
26698     {
26699         options = options || {};
26700         
26701         this.setMap(map);
26702     }
26703     
26704     
26705 });/*
26706  * - LGPL
26707  *
26708  * Alert
26709  * 
26710  */
26711
26712 /**
26713  * @class Roo.bootstrap.Alert
26714  * @extends Roo.bootstrap.Component
26715  * Bootstrap Alert class
26716  * @cfg {String} title The title of alert
26717  * @cfg {String} html The content of alert
26718  * @cfg {String} weight (  success | info | warning | danger )
26719  * @cfg {String} faicon font-awesomeicon
26720  * 
26721  * @constructor
26722  * Create a new alert
26723  * @param {Object} config The config object
26724  */
26725
26726
26727 Roo.bootstrap.Alert = function(config){
26728     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26729     
26730 };
26731
26732 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26733     
26734     title: '',
26735     html: '',
26736     weight: false,
26737     faicon: false,
26738     
26739     getAutoCreate : function()
26740     {
26741         
26742         var cfg = {
26743             tag : 'div',
26744             cls : 'alert',
26745             cn : [
26746                 {
26747                     tag : 'i',
26748                     cls : 'roo-alert-icon'
26749                     
26750                 },
26751                 {
26752                     tag : 'b',
26753                     cls : 'roo-alert-title',
26754                     html : this.title
26755                 },
26756                 {
26757                     tag : 'span',
26758                     cls : 'roo-alert-text',
26759                     html : this.html
26760                 }
26761             ]
26762         };
26763         
26764         if(this.faicon){
26765             cfg.cn[0].cls += ' fa ' + this.faicon;
26766         }
26767         
26768         if(this.weight){
26769             cfg.cls += ' alert-' + this.weight;
26770         }
26771         
26772         return cfg;
26773     },
26774     
26775     initEvents: function() 
26776     {
26777         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26778     },
26779     
26780     setTitle : function(str)
26781     {
26782         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26783     },
26784     
26785     setText : function(str)
26786     {
26787         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26788     },
26789     
26790     setWeight : function(weight)
26791     {
26792         if(this.weight){
26793             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26794         }
26795         
26796         this.weight = weight;
26797         
26798         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26799     },
26800     
26801     setIcon : function(icon)
26802     {
26803         if(this.faicon){
26804             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26805         }
26806         
26807         this.faicon = icon;
26808         
26809         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26810     },
26811     
26812     hide: function() 
26813     {
26814         this.el.hide();   
26815     },
26816     
26817     show: function() 
26818     {  
26819         this.el.show();   
26820     }
26821     
26822 });
26823
26824  
26825 /*
26826 * Licence: LGPL
26827 */
26828
26829 /**
26830  * @class Roo.bootstrap.UploadCropbox
26831  * @extends Roo.bootstrap.Component
26832  * Bootstrap UploadCropbox class
26833  * @cfg {String} emptyText show when image has been loaded
26834  * @cfg {String} rotateNotify show when image too small to rotate
26835  * @cfg {Number} errorTimeout default 3000
26836  * @cfg {Number} minWidth default 300
26837  * @cfg {Number} minHeight default 300
26838  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26839  * @cfg {Boolean} isDocument (true|false) default false
26840  * @cfg {String} url action url
26841  * @cfg {String} paramName default 'imageUpload'
26842  * @cfg {String} method default POST
26843  * @cfg {Boolean} loadMask (true|false) default true
26844  * @cfg {Boolean} loadingText default 'Loading...'
26845  * 
26846  * @constructor
26847  * Create a new UploadCropbox
26848  * @param {Object} config The config object
26849  */
26850
26851 Roo.bootstrap.UploadCropbox = function(config){
26852     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26853     
26854     this.addEvents({
26855         /**
26856          * @event beforeselectfile
26857          * Fire before select file
26858          * @param {Roo.bootstrap.UploadCropbox} this
26859          */
26860         "beforeselectfile" : true,
26861         /**
26862          * @event initial
26863          * Fire after initEvent
26864          * @param {Roo.bootstrap.UploadCropbox} this
26865          */
26866         "initial" : true,
26867         /**
26868          * @event crop
26869          * Fire after initEvent
26870          * @param {Roo.bootstrap.UploadCropbox} this
26871          * @param {String} data
26872          */
26873         "crop" : true,
26874         /**
26875          * @event prepare
26876          * Fire when preparing the file data
26877          * @param {Roo.bootstrap.UploadCropbox} this
26878          * @param {Object} file
26879          */
26880         "prepare" : true,
26881         /**
26882          * @event exception
26883          * Fire when get exception
26884          * @param {Roo.bootstrap.UploadCropbox} this
26885          * @param {XMLHttpRequest} xhr
26886          */
26887         "exception" : true,
26888         /**
26889          * @event beforeloadcanvas
26890          * Fire before load the canvas
26891          * @param {Roo.bootstrap.UploadCropbox} this
26892          * @param {String} src
26893          */
26894         "beforeloadcanvas" : true,
26895         /**
26896          * @event trash
26897          * Fire when trash image
26898          * @param {Roo.bootstrap.UploadCropbox} this
26899          */
26900         "trash" : true,
26901         /**
26902          * @event download
26903          * Fire when download the image
26904          * @param {Roo.bootstrap.UploadCropbox} this
26905          */
26906         "download" : true,
26907         /**
26908          * @event footerbuttonclick
26909          * Fire when footerbuttonclick
26910          * @param {Roo.bootstrap.UploadCropbox} this
26911          * @param {String} type
26912          */
26913         "footerbuttonclick" : true,
26914         /**
26915          * @event resize
26916          * Fire when resize
26917          * @param {Roo.bootstrap.UploadCropbox} this
26918          */
26919         "resize" : true,
26920         /**
26921          * @event rotate
26922          * Fire when rotate the image
26923          * @param {Roo.bootstrap.UploadCropbox} this
26924          * @param {String} pos
26925          */
26926         "rotate" : true,
26927         /**
26928          * @event inspect
26929          * Fire when inspect the file
26930          * @param {Roo.bootstrap.UploadCropbox} this
26931          * @param {Object} file
26932          */
26933         "inspect" : true,
26934         /**
26935          * @event upload
26936          * Fire when xhr upload the file
26937          * @param {Roo.bootstrap.UploadCropbox} this
26938          * @param {Object} data
26939          */
26940         "upload" : true,
26941         /**
26942          * @event arrange
26943          * Fire when arrange the file data
26944          * @param {Roo.bootstrap.UploadCropbox} this
26945          * @param {Object} formData
26946          */
26947         "arrange" : true
26948     });
26949     
26950     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26951 };
26952
26953 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26954     
26955     emptyText : 'Click to upload image',
26956     rotateNotify : 'Image is too small to rotate',
26957     errorTimeout : 3000,
26958     scale : 0,
26959     baseScale : 1,
26960     rotate : 0,
26961     dragable : false,
26962     pinching : false,
26963     mouseX : 0,
26964     mouseY : 0,
26965     cropData : false,
26966     minWidth : 300,
26967     minHeight : 300,
26968     file : false,
26969     exif : {},
26970     baseRotate : 1,
26971     cropType : 'image/jpeg',
26972     buttons : false,
26973     canvasLoaded : false,
26974     isDocument : false,
26975     method : 'POST',
26976     paramName : 'imageUpload',
26977     loadMask : true,
26978     loadingText : 'Loading...',
26979     maskEl : false,
26980     
26981     getAutoCreate : function()
26982     {
26983         var cfg = {
26984             tag : 'div',
26985             cls : 'roo-upload-cropbox',
26986             cn : [
26987                 {
26988                     tag : 'input',
26989                     cls : 'roo-upload-cropbox-selector',
26990                     type : 'file'
26991                 },
26992                 {
26993                     tag : 'div',
26994                     cls : 'roo-upload-cropbox-body',
26995                     style : 'cursor:pointer',
26996                     cn : [
26997                         {
26998                             tag : 'div',
26999                             cls : 'roo-upload-cropbox-preview'
27000                         },
27001                         {
27002                             tag : 'div',
27003                             cls : 'roo-upload-cropbox-thumb'
27004                         },
27005                         {
27006                             tag : 'div',
27007                             cls : 'roo-upload-cropbox-empty-notify',
27008                             html : this.emptyText
27009                         },
27010                         {
27011                             tag : 'div',
27012                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27013                             html : this.rotateNotify
27014                         }
27015                     ]
27016                 },
27017                 {
27018                     tag : 'div',
27019                     cls : 'roo-upload-cropbox-footer',
27020                     cn : {
27021                         tag : 'div',
27022                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27023                         cn : []
27024                     }
27025                 }
27026             ]
27027         };
27028         
27029         return cfg;
27030     },
27031     
27032     onRender : function(ct, position)
27033     {
27034         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27035         
27036         if (this.buttons.length) {
27037             
27038             Roo.each(this.buttons, function(bb) {
27039                 
27040                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27041                 
27042                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27043                 
27044             }, this);
27045         }
27046         
27047         if(this.loadMask){
27048             this.maskEl = this.el;
27049         }
27050     },
27051     
27052     initEvents : function()
27053     {
27054         this.urlAPI = (window.createObjectURL && window) || 
27055                                 (window.URL && URL.revokeObjectURL && URL) || 
27056                                 (window.webkitURL && webkitURL);
27057                         
27058         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27059         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27060         
27061         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27062         this.selectorEl.hide();
27063         
27064         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27065         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27066         
27067         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27068         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27069         this.thumbEl.hide();
27070         
27071         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27072         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27073         
27074         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27075         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27076         this.errorEl.hide();
27077         
27078         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27079         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27080         this.footerEl.hide();
27081         
27082         this.setThumbBoxSize();
27083         
27084         this.bind();
27085         
27086         this.resize();
27087         
27088         this.fireEvent('initial', this);
27089     },
27090
27091     bind : function()
27092     {
27093         var _this = this;
27094         
27095         window.addEventListener("resize", function() { _this.resize(); } );
27096         
27097         this.bodyEl.on('click', this.beforeSelectFile, this);
27098         
27099         if(Roo.isTouch){
27100             this.bodyEl.on('touchstart', this.onTouchStart, this);
27101             this.bodyEl.on('touchmove', this.onTouchMove, this);
27102             this.bodyEl.on('touchend', this.onTouchEnd, this);
27103         }
27104         
27105         if(!Roo.isTouch){
27106             this.bodyEl.on('mousedown', this.onMouseDown, this);
27107             this.bodyEl.on('mousemove', this.onMouseMove, this);
27108             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27109             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27110             Roo.get(document).on('mouseup', this.onMouseUp, this);
27111         }
27112         
27113         this.selectorEl.on('change', this.onFileSelected, this);
27114     },
27115     
27116     reset : function()
27117     {    
27118         this.scale = 0;
27119         this.baseScale = 1;
27120         this.rotate = 0;
27121         this.baseRotate = 1;
27122         this.dragable = false;
27123         this.pinching = false;
27124         this.mouseX = 0;
27125         this.mouseY = 0;
27126         this.cropData = false;
27127         this.notifyEl.dom.innerHTML = this.emptyText;
27128         
27129         this.selectorEl.dom.value = '';
27130         
27131     },
27132     
27133     resize : function()
27134     {
27135         if(this.fireEvent('resize', this) != false){
27136             this.setThumbBoxPosition();
27137             this.setCanvasPosition();
27138         }
27139     },
27140     
27141     onFooterButtonClick : function(e, el, o, type)
27142     {
27143         switch (type) {
27144             case 'rotate-left' :
27145                 this.onRotateLeft(e);
27146                 break;
27147             case 'rotate-right' :
27148                 this.onRotateRight(e);
27149                 break;
27150             case 'picture' :
27151                 this.beforeSelectFile(e);
27152                 break;
27153             case 'trash' :
27154                 this.trash(e);
27155                 break;
27156             case 'crop' :
27157                 this.crop(e);
27158                 break;
27159             case 'download' :
27160                 this.download(e);
27161                 break;
27162             default :
27163                 break;
27164         }
27165         
27166         this.fireEvent('footerbuttonclick', this, type);
27167     },
27168     
27169     beforeSelectFile : function(e)
27170     {
27171         e.preventDefault();
27172         
27173         if(this.fireEvent('beforeselectfile', this) != false){
27174             this.selectorEl.dom.click();
27175         }
27176     },
27177     
27178     onFileSelected : function(e)
27179     {
27180         e.preventDefault();
27181         
27182         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27183             return;
27184         }
27185         
27186         var file = this.selectorEl.dom.files[0];
27187         
27188         if(this.fireEvent('inspect', this, file) != false){
27189             this.prepare(file);
27190         }
27191         
27192     },
27193     
27194     trash : function(e)
27195     {
27196         this.fireEvent('trash', this);
27197     },
27198     
27199     download : function(e)
27200     {
27201         this.fireEvent('download', this);
27202     },
27203     
27204     loadCanvas : function(src)
27205     {   
27206         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27207             
27208             this.reset();
27209             
27210             this.imageEl = document.createElement('img');
27211             
27212             var _this = this;
27213             
27214             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27215             
27216             this.imageEl.src = src;
27217         }
27218     },
27219     
27220     onLoadCanvas : function()
27221     {   
27222         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27223         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27224         
27225         this.bodyEl.un('click', this.beforeSelectFile, this);
27226         
27227         this.notifyEl.hide();
27228         this.thumbEl.show();
27229         this.footerEl.show();
27230         
27231         this.baseRotateLevel();
27232         
27233         if(this.isDocument){
27234             this.setThumbBoxSize();
27235         }
27236         
27237         this.setThumbBoxPosition();
27238         
27239         this.baseScaleLevel();
27240         
27241         this.draw();
27242         
27243         this.resize();
27244         
27245         this.canvasLoaded = true;
27246         
27247         if(this.loadMask){
27248             this.maskEl.unmask();
27249         }
27250         
27251     },
27252     
27253     setCanvasPosition : function()
27254     {   
27255         if(!this.canvasEl){
27256             return;
27257         }
27258         
27259         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27260         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27261         
27262         this.previewEl.setLeft(pw);
27263         this.previewEl.setTop(ph);
27264         
27265     },
27266     
27267     onMouseDown : function(e)
27268     {   
27269         e.stopEvent();
27270         
27271         this.dragable = true;
27272         this.pinching = false;
27273         
27274         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27275             this.dragable = false;
27276             return;
27277         }
27278         
27279         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27280         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27281         
27282     },
27283     
27284     onMouseMove : function(e)
27285     {   
27286         e.stopEvent();
27287         
27288         if(!this.canvasLoaded){
27289             return;
27290         }
27291         
27292         if (!this.dragable){
27293             return;
27294         }
27295         
27296         var minX = Math.ceil(this.thumbEl.getLeft(true));
27297         var minY = Math.ceil(this.thumbEl.getTop(true));
27298         
27299         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27300         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27301         
27302         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27303         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27304         
27305         x = x - this.mouseX;
27306         y = y - this.mouseY;
27307         
27308         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27309         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27310         
27311         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27312         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27313         
27314         this.previewEl.setLeft(bgX);
27315         this.previewEl.setTop(bgY);
27316         
27317         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27318         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27319     },
27320     
27321     onMouseUp : function(e)
27322     {   
27323         e.stopEvent();
27324         
27325         this.dragable = false;
27326     },
27327     
27328     onMouseWheel : function(e)
27329     {   
27330         e.stopEvent();
27331         
27332         this.startScale = this.scale;
27333         
27334         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27335         
27336         if(!this.zoomable()){
27337             this.scale = this.startScale;
27338             return;
27339         }
27340         
27341         this.draw();
27342         
27343         return;
27344     },
27345     
27346     zoomable : function()
27347     {
27348         var minScale = this.thumbEl.getWidth() / this.minWidth;
27349         
27350         if(this.minWidth < this.minHeight){
27351             minScale = this.thumbEl.getHeight() / this.minHeight;
27352         }
27353         
27354         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27355         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27356         
27357         if(
27358                 this.isDocument &&
27359                 (this.rotate == 0 || this.rotate == 180) && 
27360                 (
27361                     width > this.imageEl.OriginWidth || 
27362                     height > this.imageEl.OriginHeight ||
27363                     (width < this.minWidth && height < this.minHeight)
27364                 )
27365         ){
27366             return false;
27367         }
27368         
27369         if(
27370                 this.isDocument &&
27371                 (this.rotate == 90 || this.rotate == 270) && 
27372                 (
27373                     width > this.imageEl.OriginWidth || 
27374                     height > this.imageEl.OriginHeight ||
27375                     (width < this.minHeight && height < this.minWidth)
27376                 )
27377         ){
27378             return false;
27379         }
27380         
27381         if(
27382                 !this.isDocument &&
27383                 (this.rotate == 0 || this.rotate == 180) && 
27384                 (
27385                     width < this.minWidth || 
27386                     width > this.imageEl.OriginWidth || 
27387                     height < this.minHeight || 
27388                     height > this.imageEl.OriginHeight
27389                 )
27390         ){
27391             return false;
27392         }
27393         
27394         if(
27395                 !this.isDocument &&
27396                 (this.rotate == 90 || this.rotate == 270) && 
27397                 (
27398                     width < this.minHeight || 
27399                     width > this.imageEl.OriginWidth || 
27400                     height < this.minWidth || 
27401                     height > this.imageEl.OriginHeight
27402                 )
27403         ){
27404             return false;
27405         }
27406         
27407         return true;
27408         
27409     },
27410     
27411     onRotateLeft : function(e)
27412     {   
27413         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27414             
27415             var minScale = this.thumbEl.getWidth() / this.minWidth;
27416             
27417             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27418             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27419             
27420             this.startScale = this.scale;
27421             
27422             while (this.getScaleLevel() < minScale){
27423             
27424                 this.scale = this.scale + 1;
27425                 
27426                 if(!this.zoomable()){
27427                     break;
27428                 }
27429                 
27430                 if(
27431                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27432                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27433                 ){
27434                     continue;
27435                 }
27436                 
27437                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27438
27439                 this.draw();
27440                 
27441                 return;
27442             }
27443             
27444             this.scale = this.startScale;
27445             
27446             this.onRotateFail();
27447             
27448             return false;
27449         }
27450         
27451         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27452
27453         if(this.isDocument){
27454             this.setThumbBoxSize();
27455             this.setThumbBoxPosition();
27456             this.setCanvasPosition();
27457         }
27458         
27459         this.draw();
27460         
27461         this.fireEvent('rotate', this, 'left');
27462         
27463     },
27464     
27465     onRotateRight : function(e)
27466     {
27467         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27468             
27469             var minScale = this.thumbEl.getWidth() / this.minWidth;
27470         
27471             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27472             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27473             
27474             this.startScale = this.scale;
27475             
27476             while (this.getScaleLevel() < minScale){
27477             
27478                 this.scale = this.scale + 1;
27479                 
27480                 if(!this.zoomable()){
27481                     break;
27482                 }
27483                 
27484                 if(
27485                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27486                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27487                 ){
27488                     continue;
27489                 }
27490                 
27491                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27492
27493                 this.draw();
27494                 
27495                 return;
27496             }
27497             
27498             this.scale = this.startScale;
27499             
27500             this.onRotateFail();
27501             
27502             return false;
27503         }
27504         
27505         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27506
27507         if(this.isDocument){
27508             this.setThumbBoxSize();
27509             this.setThumbBoxPosition();
27510             this.setCanvasPosition();
27511         }
27512         
27513         this.draw();
27514         
27515         this.fireEvent('rotate', this, 'right');
27516     },
27517     
27518     onRotateFail : function()
27519     {
27520         this.errorEl.show(true);
27521         
27522         var _this = this;
27523         
27524         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27525     },
27526     
27527     draw : function()
27528     {
27529         this.previewEl.dom.innerHTML = '';
27530         
27531         var canvasEl = document.createElement("canvas");
27532         
27533         var contextEl = canvasEl.getContext("2d");
27534         
27535         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27536         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27537         var center = this.imageEl.OriginWidth / 2;
27538         
27539         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27540             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27541             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27542             center = this.imageEl.OriginHeight / 2;
27543         }
27544         
27545         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27546         
27547         contextEl.translate(center, center);
27548         contextEl.rotate(this.rotate * Math.PI / 180);
27549
27550         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27551         
27552         this.canvasEl = document.createElement("canvas");
27553         
27554         this.contextEl = this.canvasEl.getContext("2d");
27555         
27556         switch (this.rotate) {
27557             case 0 :
27558                 
27559                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27560                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27561                 
27562                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27563                 
27564                 break;
27565             case 90 : 
27566                 
27567                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27568                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27569                 
27570                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27571                     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);
27572                     break;
27573                 }
27574                 
27575                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27576                 
27577                 break;
27578             case 180 :
27579                 
27580                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27581                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27582                 
27583                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27584                     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);
27585                     break;
27586                 }
27587                 
27588                 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);
27589                 
27590                 break;
27591             case 270 :
27592                 
27593                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27594                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27595         
27596                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27597                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27598                     break;
27599                 }
27600                 
27601                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27602                 
27603                 break;
27604             default : 
27605                 break;
27606         }
27607         
27608         this.previewEl.appendChild(this.canvasEl);
27609         
27610         this.setCanvasPosition();
27611     },
27612     
27613     crop : function()
27614     {
27615         if(!this.canvasLoaded){
27616             return;
27617         }
27618         
27619         var imageCanvas = document.createElement("canvas");
27620         
27621         var imageContext = imageCanvas.getContext("2d");
27622         
27623         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27624         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27625         
27626         var center = imageCanvas.width / 2;
27627         
27628         imageContext.translate(center, center);
27629         
27630         imageContext.rotate(this.rotate * Math.PI / 180);
27631         
27632         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27633         
27634         var canvas = document.createElement("canvas");
27635         
27636         var context = canvas.getContext("2d");
27637                 
27638         canvas.width = this.minWidth;
27639         canvas.height = this.minHeight;
27640
27641         switch (this.rotate) {
27642             case 0 :
27643                 
27644                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27645                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27646                 
27647                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27648                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27649                 
27650                 var targetWidth = this.minWidth - 2 * x;
27651                 var targetHeight = this.minHeight - 2 * y;
27652                 
27653                 var scale = 1;
27654                 
27655                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27656                     scale = targetWidth / width;
27657                 }
27658                 
27659                 if(x > 0 && y == 0){
27660                     scale = targetHeight / height;
27661                 }
27662                 
27663                 if(x > 0 && y > 0){
27664                     scale = targetWidth / width;
27665                     
27666                     if(width < height){
27667                         scale = targetHeight / height;
27668                     }
27669                 }
27670                 
27671                 context.scale(scale, scale);
27672                 
27673                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27674                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27675
27676                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27677                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27678
27679                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27680                 
27681                 break;
27682             case 90 : 
27683                 
27684                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27685                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27686                 
27687                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27688                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27689                 
27690                 var targetWidth = this.minWidth - 2 * x;
27691                 var targetHeight = this.minHeight - 2 * y;
27692                 
27693                 var scale = 1;
27694                 
27695                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27696                     scale = targetWidth / width;
27697                 }
27698                 
27699                 if(x > 0 && y == 0){
27700                     scale = targetHeight / height;
27701                 }
27702                 
27703                 if(x > 0 && y > 0){
27704                     scale = targetWidth / width;
27705                     
27706                     if(width < height){
27707                         scale = targetHeight / height;
27708                     }
27709                 }
27710                 
27711                 context.scale(scale, scale);
27712                 
27713                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27714                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27715
27716                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27717                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27718                 
27719                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27720                 
27721                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27722                 
27723                 break;
27724             case 180 :
27725                 
27726                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27727                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27728                 
27729                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27730                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27731                 
27732                 var targetWidth = this.minWidth - 2 * x;
27733                 var targetHeight = this.minHeight - 2 * y;
27734                 
27735                 var scale = 1;
27736                 
27737                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27738                     scale = targetWidth / width;
27739                 }
27740                 
27741                 if(x > 0 && y == 0){
27742                     scale = targetHeight / height;
27743                 }
27744                 
27745                 if(x > 0 && y > 0){
27746                     scale = targetWidth / width;
27747                     
27748                     if(width < height){
27749                         scale = targetHeight / height;
27750                     }
27751                 }
27752                 
27753                 context.scale(scale, scale);
27754                 
27755                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27756                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27757
27758                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27759                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27760
27761                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27762                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27763                 
27764                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27765                 
27766                 break;
27767             case 270 :
27768                 
27769                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27770                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27771                 
27772                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27773                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27774                 
27775                 var targetWidth = this.minWidth - 2 * x;
27776                 var targetHeight = this.minHeight - 2 * y;
27777                 
27778                 var scale = 1;
27779                 
27780                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27781                     scale = targetWidth / width;
27782                 }
27783                 
27784                 if(x > 0 && y == 0){
27785                     scale = targetHeight / height;
27786                 }
27787                 
27788                 if(x > 0 && y > 0){
27789                     scale = targetWidth / width;
27790                     
27791                     if(width < height){
27792                         scale = targetHeight / height;
27793                     }
27794                 }
27795                 
27796                 context.scale(scale, scale);
27797                 
27798                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27799                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27800
27801                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27802                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27803                 
27804                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27805                 
27806                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27807                 
27808                 break;
27809             default : 
27810                 break;
27811         }
27812         
27813         this.cropData = canvas.toDataURL(this.cropType);
27814         
27815         if(this.fireEvent('crop', this, this.cropData) !== false){
27816             this.process(this.file, this.cropData);
27817         }
27818         
27819         return;
27820         
27821     },
27822     
27823     setThumbBoxSize : function()
27824     {
27825         var width, height;
27826         
27827         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27828             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27829             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27830             
27831             this.minWidth = width;
27832             this.minHeight = height;
27833             
27834             if(this.rotate == 90 || this.rotate == 270){
27835                 this.minWidth = height;
27836                 this.minHeight = width;
27837             }
27838         }
27839         
27840         height = 300;
27841         width = Math.ceil(this.minWidth * height / this.minHeight);
27842         
27843         if(this.minWidth > this.minHeight){
27844             width = 300;
27845             height = Math.ceil(this.minHeight * width / this.minWidth);
27846         }
27847         
27848         this.thumbEl.setStyle({
27849             width : width + 'px',
27850             height : height + 'px'
27851         });
27852
27853         return;
27854             
27855     },
27856     
27857     setThumbBoxPosition : function()
27858     {
27859         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27860         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27861         
27862         this.thumbEl.setLeft(x);
27863         this.thumbEl.setTop(y);
27864         
27865     },
27866     
27867     baseRotateLevel : function()
27868     {
27869         this.baseRotate = 1;
27870         
27871         if(
27872                 typeof(this.exif) != 'undefined' &&
27873                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27874                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27875         ){
27876             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27877         }
27878         
27879         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27880         
27881     },
27882     
27883     baseScaleLevel : function()
27884     {
27885         var width, height;
27886         
27887         if(this.isDocument){
27888             
27889             if(this.baseRotate == 6 || this.baseRotate == 8){
27890             
27891                 height = this.thumbEl.getHeight();
27892                 this.baseScale = height / this.imageEl.OriginWidth;
27893
27894                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27895                     width = this.thumbEl.getWidth();
27896                     this.baseScale = width / this.imageEl.OriginHeight;
27897                 }
27898
27899                 return;
27900             }
27901
27902             height = this.thumbEl.getHeight();
27903             this.baseScale = height / this.imageEl.OriginHeight;
27904
27905             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27906                 width = this.thumbEl.getWidth();
27907                 this.baseScale = width / this.imageEl.OriginWidth;
27908             }
27909
27910             return;
27911         }
27912         
27913         if(this.baseRotate == 6 || this.baseRotate == 8){
27914             
27915             width = this.thumbEl.getHeight();
27916             this.baseScale = width / this.imageEl.OriginHeight;
27917             
27918             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27919                 height = this.thumbEl.getWidth();
27920                 this.baseScale = height / this.imageEl.OriginHeight;
27921             }
27922             
27923             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27924                 height = this.thumbEl.getWidth();
27925                 this.baseScale = height / this.imageEl.OriginHeight;
27926                 
27927                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27928                     width = this.thumbEl.getHeight();
27929                     this.baseScale = width / this.imageEl.OriginWidth;
27930                 }
27931             }
27932             
27933             return;
27934         }
27935         
27936         width = this.thumbEl.getWidth();
27937         this.baseScale = width / this.imageEl.OriginWidth;
27938         
27939         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27940             height = this.thumbEl.getHeight();
27941             this.baseScale = height / this.imageEl.OriginHeight;
27942         }
27943         
27944         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27945             
27946             height = this.thumbEl.getHeight();
27947             this.baseScale = height / this.imageEl.OriginHeight;
27948             
27949             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27950                 width = this.thumbEl.getWidth();
27951                 this.baseScale = width / this.imageEl.OriginWidth;
27952             }
27953             
27954         }
27955         
27956         return;
27957     },
27958     
27959     getScaleLevel : function()
27960     {
27961         return this.baseScale * Math.pow(1.1, this.scale);
27962     },
27963     
27964     onTouchStart : function(e)
27965     {
27966         if(!this.canvasLoaded){
27967             this.beforeSelectFile(e);
27968             return;
27969         }
27970         
27971         var touches = e.browserEvent.touches;
27972         
27973         if(!touches){
27974             return;
27975         }
27976         
27977         if(touches.length == 1){
27978             this.onMouseDown(e);
27979             return;
27980         }
27981         
27982         if(touches.length != 2){
27983             return;
27984         }
27985         
27986         var coords = [];
27987         
27988         for(var i = 0, finger; finger = touches[i]; i++){
27989             coords.push(finger.pageX, finger.pageY);
27990         }
27991         
27992         var x = Math.pow(coords[0] - coords[2], 2);
27993         var y = Math.pow(coords[1] - coords[3], 2);
27994         
27995         this.startDistance = Math.sqrt(x + y);
27996         
27997         this.startScale = this.scale;
27998         
27999         this.pinching = true;
28000         this.dragable = false;
28001         
28002     },
28003     
28004     onTouchMove : function(e)
28005     {
28006         if(!this.pinching && !this.dragable){
28007             return;
28008         }
28009         
28010         var touches = e.browserEvent.touches;
28011         
28012         if(!touches){
28013             return;
28014         }
28015         
28016         if(this.dragable){
28017             this.onMouseMove(e);
28018             return;
28019         }
28020         
28021         var coords = [];
28022         
28023         for(var i = 0, finger; finger = touches[i]; i++){
28024             coords.push(finger.pageX, finger.pageY);
28025         }
28026         
28027         var x = Math.pow(coords[0] - coords[2], 2);
28028         var y = Math.pow(coords[1] - coords[3], 2);
28029         
28030         this.endDistance = Math.sqrt(x + y);
28031         
28032         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28033         
28034         if(!this.zoomable()){
28035             this.scale = this.startScale;
28036             return;
28037         }
28038         
28039         this.draw();
28040         
28041     },
28042     
28043     onTouchEnd : function(e)
28044     {
28045         this.pinching = false;
28046         this.dragable = false;
28047         
28048     },
28049     
28050     process : function(file, crop)
28051     {
28052         if(this.loadMask){
28053             this.maskEl.mask(this.loadingText);
28054         }
28055         
28056         this.xhr = new XMLHttpRequest();
28057         
28058         file.xhr = this.xhr;
28059
28060         this.xhr.open(this.method, this.url, true);
28061         
28062         var headers = {
28063             "Accept": "application/json",
28064             "Cache-Control": "no-cache",
28065             "X-Requested-With": "XMLHttpRequest"
28066         };
28067         
28068         for (var headerName in headers) {
28069             var headerValue = headers[headerName];
28070             if (headerValue) {
28071                 this.xhr.setRequestHeader(headerName, headerValue);
28072             }
28073         }
28074         
28075         var _this = this;
28076         
28077         this.xhr.onload = function()
28078         {
28079             _this.xhrOnLoad(_this.xhr);
28080         }
28081         
28082         this.xhr.onerror = function()
28083         {
28084             _this.xhrOnError(_this.xhr);
28085         }
28086         
28087         var formData = new FormData();
28088
28089         formData.append('returnHTML', 'NO');
28090         
28091         if(crop){
28092             formData.append('crop', crop);
28093         }
28094         
28095         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28096             formData.append(this.paramName, file, file.name);
28097         }
28098         
28099         if(typeof(file.filename) != 'undefined'){
28100             formData.append('filename', file.filename);
28101         }
28102         
28103         if(typeof(file.mimetype) != 'undefined'){
28104             formData.append('mimetype', file.mimetype);
28105         }
28106         
28107         if(this.fireEvent('arrange', this, formData) != false){
28108             this.xhr.send(formData);
28109         };
28110     },
28111     
28112     xhrOnLoad : function(xhr)
28113     {
28114         if(this.loadMask){
28115             this.maskEl.unmask();
28116         }
28117         
28118         if (xhr.readyState !== 4) {
28119             this.fireEvent('exception', this, xhr);
28120             return;
28121         }
28122
28123         var response = Roo.decode(xhr.responseText);
28124         
28125         if(!response.success){
28126             this.fireEvent('exception', this, xhr);
28127             return;
28128         }
28129         
28130         var response = Roo.decode(xhr.responseText);
28131         
28132         this.fireEvent('upload', this, response);
28133         
28134     },
28135     
28136     xhrOnError : function()
28137     {
28138         if(this.loadMask){
28139             this.maskEl.unmask();
28140         }
28141         
28142         Roo.log('xhr on error');
28143         
28144         var response = Roo.decode(xhr.responseText);
28145           
28146         Roo.log(response);
28147         
28148     },
28149     
28150     prepare : function(file)
28151     {   
28152         if(this.loadMask){
28153             this.maskEl.mask(this.loadingText);
28154         }
28155         
28156         this.file = false;
28157         this.exif = {};
28158         
28159         if(typeof(file) === 'string'){
28160             this.loadCanvas(file);
28161             return;
28162         }
28163         
28164         if(!file || !this.urlAPI){
28165             return;
28166         }
28167         
28168         this.file = file;
28169         this.cropType = file.type;
28170         
28171         var _this = this;
28172         
28173         if(this.fireEvent('prepare', this, this.file) != false){
28174             
28175             var reader = new FileReader();
28176             
28177             reader.onload = function (e) {
28178                 if (e.target.error) {
28179                     Roo.log(e.target.error);
28180                     return;
28181                 }
28182                 
28183                 var buffer = e.target.result,
28184                     dataView = new DataView(buffer),
28185                     offset = 2,
28186                     maxOffset = dataView.byteLength - 4,
28187                     markerBytes,
28188                     markerLength;
28189                 
28190                 if (dataView.getUint16(0) === 0xffd8) {
28191                     while (offset < maxOffset) {
28192                         markerBytes = dataView.getUint16(offset);
28193                         
28194                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28195                             markerLength = dataView.getUint16(offset + 2) + 2;
28196                             if (offset + markerLength > dataView.byteLength) {
28197                                 Roo.log('Invalid meta data: Invalid segment size.');
28198                                 break;
28199                             }
28200                             
28201                             if(markerBytes == 0xffe1){
28202                                 _this.parseExifData(
28203                                     dataView,
28204                                     offset,
28205                                     markerLength
28206                                 );
28207                             }
28208                             
28209                             offset += markerLength;
28210                             
28211                             continue;
28212                         }
28213                         
28214                         break;
28215                     }
28216                     
28217                 }
28218                 
28219                 var url = _this.urlAPI.createObjectURL(_this.file);
28220                 
28221                 _this.loadCanvas(url);
28222                 
28223                 return;
28224             }
28225             
28226             reader.readAsArrayBuffer(this.file);
28227             
28228         }
28229         
28230     },
28231     
28232     parseExifData : function(dataView, offset, length)
28233     {
28234         var tiffOffset = offset + 10,
28235             littleEndian,
28236             dirOffset;
28237     
28238         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28239             // No Exif data, might be XMP data instead
28240             return;
28241         }
28242         
28243         // Check for the ASCII code for "Exif" (0x45786966):
28244         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28245             // No Exif data, might be XMP data instead
28246             return;
28247         }
28248         if (tiffOffset + 8 > dataView.byteLength) {
28249             Roo.log('Invalid Exif data: Invalid segment size.');
28250             return;
28251         }
28252         // Check for the two null bytes:
28253         if (dataView.getUint16(offset + 8) !== 0x0000) {
28254             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28255             return;
28256         }
28257         // Check the byte alignment:
28258         switch (dataView.getUint16(tiffOffset)) {
28259         case 0x4949:
28260             littleEndian = true;
28261             break;
28262         case 0x4D4D:
28263             littleEndian = false;
28264             break;
28265         default:
28266             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28267             return;
28268         }
28269         // Check for the TIFF tag marker (0x002A):
28270         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28271             Roo.log('Invalid Exif data: Missing TIFF marker.');
28272             return;
28273         }
28274         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28275         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28276         
28277         this.parseExifTags(
28278             dataView,
28279             tiffOffset,
28280             tiffOffset + dirOffset,
28281             littleEndian
28282         );
28283     },
28284     
28285     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28286     {
28287         var tagsNumber,
28288             dirEndOffset,
28289             i;
28290         if (dirOffset + 6 > dataView.byteLength) {
28291             Roo.log('Invalid Exif data: Invalid directory offset.');
28292             return;
28293         }
28294         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28295         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28296         if (dirEndOffset + 4 > dataView.byteLength) {
28297             Roo.log('Invalid Exif data: Invalid directory size.');
28298             return;
28299         }
28300         for (i = 0; i < tagsNumber; i += 1) {
28301             this.parseExifTag(
28302                 dataView,
28303                 tiffOffset,
28304                 dirOffset + 2 + 12 * i, // tag offset
28305                 littleEndian
28306             );
28307         }
28308         // Return the offset to the next directory:
28309         return dataView.getUint32(dirEndOffset, littleEndian);
28310     },
28311     
28312     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28313     {
28314         var tag = dataView.getUint16(offset, littleEndian);
28315         
28316         this.exif[tag] = this.getExifValue(
28317             dataView,
28318             tiffOffset,
28319             offset,
28320             dataView.getUint16(offset + 2, littleEndian), // tag type
28321             dataView.getUint32(offset + 4, littleEndian), // tag length
28322             littleEndian
28323         );
28324     },
28325     
28326     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28327     {
28328         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28329             tagSize,
28330             dataOffset,
28331             values,
28332             i,
28333             str,
28334             c;
28335     
28336         if (!tagType) {
28337             Roo.log('Invalid Exif data: Invalid tag type.');
28338             return;
28339         }
28340         
28341         tagSize = tagType.size * length;
28342         // Determine if the value is contained in the dataOffset bytes,
28343         // or if the value at the dataOffset is a pointer to the actual data:
28344         dataOffset = tagSize > 4 ?
28345                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28346         if (dataOffset + tagSize > dataView.byteLength) {
28347             Roo.log('Invalid Exif data: Invalid data offset.');
28348             return;
28349         }
28350         if (length === 1) {
28351             return tagType.getValue(dataView, dataOffset, littleEndian);
28352         }
28353         values = [];
28354         for (i = 0; i < length; i += 1) {
28355             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28356         }
28357         
28358         if (tagType.ascii) {
28359             str = '';
28360             // Concatenate the chars:
28361             for (i = 0; i < values.length; i += 1) {
28362                 c = values[i];
28363                 // Ignore the terminating NULL byte(s):
28364                 if (c === '\u0000') {
28365                     break;
28366                 }
28367                 str += c;
28368             }
28369             return str;
28370         }
28371         return values;
28372     }
28373     
28374 });
28375
28376 Roo.apply(Roo.bootstrap.UploadCropbox, {
28377     tags : {
28378         'Orientation': 0x0112
28379     },
28380     
28381     Orientation: {
28382             1: 0, //'top-left',
28383 //            2: 'top-right',
28384             3: 180, //'bottom-right',
28385 //            4: 'bottom-left',
28386 //            5: 'left-top',
28387             6: 90, //'right-top',
28388 //            7: 'right-bottom',
28389             8: 270 //'left-bottom'
28390     },
28391     
28392     exifTagTypes : {
28393         // byte, 8-bit unsigned int:
28394         1: {
28395             getValue: function (dataView, dataOffset) {
28396                 return dataView.getUint8(dataOffset);
28397             },
28398             size: 1
28399         },
28400         // ascii, 8-bit byte:
28401         2: {
28402             getValue: function (dataView, dataOffset) {
28403                 return String.fromCharCode(dataView.getUint8(dataOffset));
28404             },
28405             size: 1,
28406             ascii: true
28407         },
28408         // short, 16 bit int:
28409         3: {
28410             getValue: function (dataView, dataOffset, littleEndian) {
28411                 return dataView.getUint16(dataOffset, littleEndian);
28412             },
28413             size: 2
28414         },
28415         // long, 32 bit int:
28416         4: {
28417             getValue: function (dataView, dataOffset, littleEndian) {
28418                 return dataView.getUint32(dataOffset, littleEndian);
28419             },
28420             size: 4
28421         },
28422         // rational = two long values, first is numerator, second is denominator:
28423         5: {
28424             getValue: function (dataView, dataOffset, littleEndian) {
28425                 return dataView.getUint32(dataOffset, littleEndian) /
28426                     dataView.getUint32(dataOffset + 4, littleEndian);
28427             },
28428             size: 8
28429         },
28430         // slong, 32 bit signed int:
28431         9: {
28432             getValue: function (dataView, dataOffset, littleEndian) {
28433                 return dataView.getInt32(dataOffset, littleEndian);
28434             },
28435             size: 4
28436         },
28437         // srational, two slongs, first is numerator, second is denominator:
28438         10: {
28439             getValue: function (dataView, dataOffset, littleEndian) {
28440                 return dataView.getInt32(dataOffset, littleEndian) /
28441                     dataView.getInt32(dataOffset + 4, littleEndian);
28442             },
28443             size: 8
28444         }
28445     },
28446     
28447     footer : {
28448         STANDARD : [
28449             {
28450                 tag : 'div',
28451                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28452                 action : 'rotate-left',
28453                 cn : [
28454                     {
28455                         tag : 'button',
28456                         cls : 'btn btn-default',
28457                         html : '<i class="fa fa-undo"></i>'
28458                     }
28459                 ]
28460             },
28461             {
28462                 tag : 'div',
28463                 cls : 'btn-group roo-upload-cropbox-picture',
28464                 action : 'picture',
28465                 cn : [
28466                     {
28467                         tag : 'button',
28468                         cls : 'btn btn-default',
28469                         html : '<i class="fa fa-picture-o"></i>'
28470                     }
28471                 ]
28472             },
28473             {
28474                 tag : 'div',
28475                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28476                 action : 'rotate-right',
28477                 cn : [
28478                     {
28479                         tag : 'button',
28480                         cls : 'btn btn-default',
28481                         html : '<i class="fa fa-repeat"></i>'
28482                     }
28483                 ]
28484             }
28485         ],
28486         DOCUMENT : [
28487             {
28488                 tag : 'div',
28489                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28490                 action : 'rotate-left',
28491                 cn : [
28492                     {
28493                         tag : 'button',
28494                         cls : 'btn btn-default',
28495                         html : '<i class="fa fa-undo"></i>'
28496                     }
28497                 ]
28498             },
28499             {
28500                 tag : 'div',
28501                 cls : 'btn-group roo-upload-cropbox-download',
28502                 action : 'download',
28503                 cn : [
28504                     {
28505                         tag : 'button',
28506                         cls : 'btn btn-default',
28507                         html : '<i class="fa fa-download"></i>'
28508                     }
28509                 ]
28510             },
28511             {
28512                 tag : 'div',
28513                 cls : 'btn-group roo-upload-cropbox-crop',
28514                 action : 'crop',
28515                 cn : [
28516                     {
28517                         tag : 'button',
28518                         cls : 'btn btn-default',
28519                         html : '<i class="fa fa-crop"></i>'
28520                     }
28521                 ]
28522             },
28523             {
28524                 tag : 'div',
28525                 cls : 'btn-group roo-upload-cropbox-trash',
28526                 action : 'trash',
28527                 cn : [
28528                     {
28529                         tag : 'button',
28530                         cls : 'btn btn-default',
28531                         html : '<i class="fa fa-trash"></i>'
28532                     }
28533                 ]
28534             },
28535             {
28536                 tag : 'div',
28537                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28538                 action : 'rotate-right',
28539                 cn : [
28540                     {
28541                         tag : 'button',
28542                         cls : 'btn btn-default',
28543                         html : '<i class="fa fa-repeat"></i>'
28544                     }
28545                 ]
28546             }
28547         ],
28548         ROTATOR : [
28549             {
28550                 tag : 'div',
28551                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28552                 action : 'rotate-left',
28553                 cn : [
28554                     {
28555                         tag : 'button',
28556                         cls : 'btn btn-default',
28557                         html : '<i class="fa fa-undo"></i>'
28558                     }
28559                 ]
28560             },
28561             {
28562                 tag : 'div',
28563                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28564                 action : 'rotate-right',
28565                 cn : [
28566                     {
28567                         tag : 'button',
28568                         cls : 'btn btn-default',
28569                         html : '<i class="fa fa-repeat"></i>'
28570                     }
28571                 ]
28572             }
28573         ]
28574     }
28575 });
28576
28577 /*
28578 * Licence: LGPL
28579 */
28580
28581 /**
28582  * @class Roo.bootstrap.DocumentManager
28583  * @extends Roo.bootstrap.Component
28584  * Bootstrap DocumentManager class
28585  * @cfg {String} paramName default 'imageUpload'
28586  * @cfg {String} toolTipName default 'filename'
28587  * @cfg {String} method default POST
28588  * @cfg {String} url action url
28589  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28590  * @cfg {Boolean} multiple multiple upload default true
28591  * @cfg {Number} thumbSize default 300
28592  * @cfg {String} fieldLabel
28593  * @cfg {Number} labelWidth default 4
28594  * @cfg {String} labelAlign (left|top) default left
28595  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28596 * @cfg {Number} labellg set the width of label (1-12)
28597  * @cfg {Number} labelmd set the width of label (1-12)
28598  * @cfg {Number} labelsm set the width of label (1-12)
28599  * @cfg {Number} labelxs set the width of label (1-12)
28600  * 
28601  * @constructor
28602  * Create a new DocumentManager
28603  * @param {Object} config The config object
28604  */
28605
28606 Roo.bootstrap.DocumentManager = function(config){
28607     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28608     
28609     this.files = [];
28610     this.delegates = [];
28611     
28612     this.addEvents({
28613         /**
28614          * @event initial
28615          * Fire when initial the DocumentManager
28616          * @param {Roo.bootstrap.DocumentManager} this
28617          */
28618         "initial" : true,
28619         /**
28620          * @event inspect
28621          * inspect selected file
28622          * @param {Roo.bootstrap.DocumentManager} this
28623          * @param {File} file
28624          */
28625         "inspect" : true,
28626         /**
28627          * @event exception
28628          * Fire when xhr load exception
28629          * @param {Roo.bootstrap.DocumentManager} this
28630          * @param {XMLHttpRequest} xhr
28631          */
28632         "exception" : true,
28633         /**
28634          * @event afterupload
28635          * Fire when xhr load exception
28636          * @param {Roo.bootstrap.DocumentManager} this
28637          * @param {XMLHttpRequest} xhr
28638          */
28639         "afterupload" : true,
28640         /**
28641          * @event prepare
28642          * prepare the form data
28643          * @param {Roo.bootstrap.DocumentManager} this
28644          * @param {Object} formData
28645          */
28646         "prepare" : true,
28647         /**
28648          * @event remove
28649          * Fire when remove the file
28650          * @param {Roo.bootstrap.DocumentManager} this
28651          * @param {Object} file
28652          */
28653         "remove" : true,
28654         /**
28655          * @event refresh
28656          * Fire after refresh the file
28657          * @param {Roo.bootstrap.DocumentManager} this
28658          */
28659         "refresh" : true,
28660         /**
28661          * @event click
28662          * Fire after click the image
28663          * @param {Roo.bootstrap.DocumentManager} this
28664          * @param {Object} file
28665          */
28666         "click" : true,
28667         /**
28668          * @event edit
28669          * Fire when upload a image and editable set to true
28670          * @param {Roo.bootstrap.DocumentManager} this
28671          * @param {Object} file
28672          */
28673         "edit" : true,
28674         /**
28675          * @event beforeselectfile
28676          * Fire before select file
28677          * @param {Roo.bootstrap.DocumentManager} this
28678          */
28679         "beforeselectfile" : true,
28680         /**
28681          * @event process
28682          * Fire before process file
28683          * @param {Roo.bootstrap.DocumentManager} this
28684          * @param {Object} file
28685          */
28686         "process" : true,
28687         /**
28688          * @event previewrendered
28689          * Fire when preview rendered
28690          * @param {Roo.bootstrap.DocumentManager} this
28691          * @param {Object} file
28692          */
28693         "previewrendered" : true
28694         
28695     });
28696 };
28697
28698 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28699     
28700     boxes : 0,
28701     inputName : '',
28702     thumbSize : 300,
28703     multiple : true,
28704     files : false,
28705     method : 'POST',
28706     url : '',
28707     paramName : 'imageUpload',
28708     toolTipName : 'filename',
28709     fieldLabel : '',
28710     labelWidth : 4,
28711     labelAlign : 'left',
28712     editable : true,
28713     delegates : false,
28714     xhr : false, 
28715     
28716     labellg : 0,
28717     labelmd : 0,
28718     labelsm : 0,
28719     labelxs : 0,
28720     
28721     getAutoCreate : function()
28722     {   
28723         var managerWidget = {
28724             tag : 'div',
28725             cls : 'roo-document-manager',
28726             cn : [
28727                 {
28728                     tag : 'input',
28729                     cls : 'roo-document-manager-selector',
28730                     type : 'file'
28731                 },
28732                 {
28733                     tag : 'div',
28734                     cls : 'roo-document-manager-uploader',
28735                     cn : [
28736                         {
28737                             tag : 'div',
28738                             cls : 'roo-document-manager-upload-btn',
28739                             html : '<i class="fa fa-plus"></i>'
28740                         }
28741                     ]
28742                     
28743                 }
28744             ]
28745         };
28746         
28747         var content = [
28748             {
28749                 tag : 'div',
28750                 cls : 'column col-md-12',
28751                 cn : managerWidget
28752             }
28753         ];
28754         
28755         if(this.fieldLabel.length){
28756             
28757             content = [
28758                 {
28759                     tag : 'div',
28760                     cls : 'column col-md-12',
28761                     html : this.fieldLabel
28762                 },
28763                 {
28764                     tag : 'div',
28765                     cls : 'column col-md-12',
28766                     cn : managerWidget
28767                 }
28768             ];
28769
28770             if(this.labelAlign == 'left'){
28771                 content = [
28772                     {
28773                         tag : 'div',
28774                         cls : 'column',
28775                         html : this.fieldLabel
28776                     },
28777                     {
28778                         tag : 'div',
28779                         cls : 'column',
28780                         cn : managerWidget
28781                     }
28782                 ];
28783                 
28784                 if(this.labelWidth > 12){
28785                     content[0].style = "width: " + this.labelWidth + 'px';
28786                 }
28787
28788                 if(this.labelWidth < 13 && this.labelmd == 0){
28789                     this.labelmd = this.labelWidth;
28790                 }
28791
28792                 if(this.labellg > 0){
28793                     content[0].cls += ' col-lg-' + this.labellg;
28794                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28795                 }
28796
28797                 if(this.labelmd > 0){
28798                     content[0].cls += ' col-md-' + this.labelmd;
28799                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28800                 }
28801
28802                 if(this.labelsm > 0){
28803                     content[0].cls += ' col-sm-' + this.labelsm;
28804                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28805                 }
28806
28807                 if(this.labelxs > 0){
28808                     content[0].cls += ' col-xs-' + this.labelxs;
28809                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28810                 }
28811                 
28812             }
28813         }
28814         
28815         var cfg = {
28816             tag : 'div',
28817             cls : 'row clearfix',
28818             cn : content
28819         };
28820         
28821         return cfg;
28822         
28823     },
28824     
28825     initEvents : function()
28826     {
28827         this.managerEl = this.el.select('.roo-document-manager', true).first();
28828         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28829         
28830         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28831         this.selectorEl.hide();
28832         
28833         if(this.multiple){
28834             this.selectorEl.attr('multiple', 'multiple');
28835         }
28836         
28837         this.selectorEl.on('change', this.onFileSelected, this);
28838         
28839         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28840         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28841         
28842         this.uploader.on('click', this.onUploaderClick, this);
28843         
28844         this.renderProgressDialog();
28845         
28846         var _this = this;
28847         
28848         window.addEventListener("resize", function() { _this.refresh(); } );
28849         
28850         this.fireEvent('initial', this);
28851     },
28852     
28853     renderProgressDialog : function()
28854     {
28855         var _this = this;
28856         
28857         this.progressDialog = new Roo.bootstrap.Modal({
28858             cls : 'roo-document-manager-progress-dialog',
28859             allow_close : false,
28860             title : '',
28861             buttons : [
28862                 {
28863                     name  :'cancel',
28864                     weight : 'danger',
28865                     html : 'Cancel'
28866                 }
28867             ], 
28868             listeners : { 
28869                 btnclick : function() {
28870                     _this.uploadCancel();
28871                     this.hide();
28872                 }
28873             }
28874         });
28875          
28876         this.progressDialog.render(Roo.get(document.body));
28877          
28878         this.progress = new Roo.bootstrap.Progress({
28879             cls : 'roo-document-manager-progress',
28880             active : true,
28881             striped : true
28882         });
28883         
28884         this.progress.render(this.progressDialog.getChildContainer());
28885         
28886         this.progressBar = new Roo.bootstrap.ProgressBar({
28887             cls : 'roo-document-manager-progress-bar',
28888             aria_valuenow : 0,
28889             aria_valuemin : 0,
28890             aria_valuemax : 12,
28891             panel : 'success'
28892         });
28893         
28894         this.progressBar.render(this.progress.getChildContainer());
28895     },
28896     
28897     onUploaderClick : function(e)
28898     {
28899         e.preventDefault();
28900      
28901         if(this.fireEvent('beforeselectfile', this) != false){
28902             this.selectorEl.dom.click();
28903         }
28904         
28905     },
28906     
28907     onFileSelected : function(e)
28908     {
28909         e.preventDefault();
28910         
28911         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28912             return;
28913         }
28914         
28915         Roo.each(this.selectorEl.dom.files, function(file){
28916             if(this.fireEvent('inspect', this, file) != false){
28917                 this.files.push(file);
28918             }
28919         }, this);
28920         
28921         this.queue();
28922         
28923     },
28924     
28925     queue : function()
28926     {
28927         this.selectorEl.dom.value = '';
28928         
28929         if(!this.files || !this.files.length){
28930             return;
28931         }
28932         
28933         if(this.boxes > 0 && this.files.length > this.boxes){
28934             this.files = this.files.slice(0, this.boxes);
28935         }
28936         
28937         this.uploader.show();
28938         
28939         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28940             this.uploader.hide();
28941         }
28942         
28943         var _this = this;
28944         
28945         var files = [];
28946         
28947         var docs = [];
28948         
28949         Roo.each(this.files, function(file){
28950             
28951             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28952                 var f = this.renderPreview(file);
28953                 files.push(f);
28954                 return;
28955             }
28956             
28957             if(file.type.indexOf('image') != -1){
28958                 this.delegates.push(
28959                     (function(){
28960                         _this.process(file);
28961                     }).createDelegate(this)
28962                 );
28963         
28964                 return;
28965             }
28966             
28967             docs.push(
28968                 (function(){
28969                     _this.process(file);
28970                 }).createDelegate(this)
28971             );
28972             
28973         }, this);
28974         
28975         this.files = files;
28976         
28977         this.delegates = this.delegates.concat(docs);
28978         
28979         if(!this.delegates.length){
28980             this.refresh();
28981             return;
28982         }
28983         
28984         this.progressBar.aria_valuemax = this.delegates.length;
28985         
28986         this.arrange();
28987         
28988         return;
28989     },
28990     
28991     arrange : function()
28992     {
28993         if(!this.delegates.length){
28994             this.progressDialog.hide();
28995             this.refresh();
28996             return;
28997         }
28998         
28999         var delegate = this.delegates.shift();
29000         
29001         this.progressDialog.show();
29002         
29003         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29004         
29005         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29006         
29007         delegate();
29008     },
29009     
29010     refresh : function()
29011     {
29012         this.uploader.show();
29013         
29014         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29015             this.uploader.hide();
29016         }
29017         
29018         Roo.isTouch ? this.closable(false) : this.closable(true);
29019         
29020         this.fireEvent('refresh', this);
29021     },
29022     
29023     onRemove : function(e, el, o)
29024     {
29025         e.preventDefault();
29026         
29027         this.fireEvent('remove', this, o);
29028         
29029     },
29030     
29031     remove : function(o)
29032     {
29033         var files = [];
29034         
29035         Roo.each(this.files, function(file){
29036             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29037                 files.push(file);
29038                 return;
29039             }
29040
29041             o.target.remove();
29042
29043         }, this);
29044         
29045         this.files = files;
29046         
29047         this.refresh();
29048     },
29049     
29050     clear : function()
29051     {
29052         Roo.each(this.files, function(file){
29053             if(!file.target){
29054                 return;
29055             }
29056             
29057             file.target.remove();
29058
29059         }, this);
29060         
29061         this.files = [];
29062         
29063         this.refresh();
29064     },
29065     
29066     onClick : function(e, el, o)
29067     {
29068         e.preventDefault();
29069         
29070         this.fireEvent('click', this, o);
29071         
29072     },
29073     
29074     closable : function(closable)
29075     {
29076         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29077             
29078             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29079             
29080             if(closable){
29081                 el.show();
29082                 return;
29083             }
29084             
29085             el.hide();
29086             
29087         }, this);
29088     },
29089     
29090     xhrOnLoad : function(xhr)
29091     {
29092         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29093             el.remove();
29094         }, this);
29095         
29096         if (xhr.readyState !== 4) {
29097             this.arrange();
29098             this.fireEvent('exception', this, xhr);
29099             return;
29100         }
29101
29102         var response = Roo.decode(xhr.responseText);
29103         
29104         if(!response.success){
29105             this.arrange();
29106             this.fireEvent('exception', this, xhr);
29107             return;
29108         }
29109         
29110         var file = this.renderPreview(response.data);
29111         
29112         this.files.push(file);
29113         
29114         this.arrange();
29115         
29116         this.fireEvent('afterupload', this, xhr);
29117         
29118     },
29119     
29120     xhrOnError : function(xhr)
29121     {
29122         Roo.log('xhr on error');
29123         
29124         var response = Roo.decode(xhr.responseText);
29125           
29126         Roo.log(response);
29127         
29128         this.arrange();
29129     },
29130     
29131     process : function(file)
29132     {
29133         if(this.fireEvent('process', this, file) !== false){
29134             if(this.editable && file.type.indexOf('image') != -1){
29135                 this.fireEvent('edit', this, file);
29136                 return;
29137             }
29138
29139             this.uploadStart(file, false);
29140
29141             return;
29142         }
29143         
29144     },
29145     
29146     uploadStart : function(file, crop)
29147     {
29148         this.xhr = new XMLHttpRequest();
29149         
29150         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29151             this.arrange();
29152             return;
29153         }
29154         
29155         file.xhr = this.xhr;
29156             
29157         this.managerEl.createChild({
29158             tag : 'div',
29159             cls : 'roo-document-manager-loading',
29160             cn : [
29161                 {
29162                     tag : 'div',
29163                     tooltip : file.name,
29164                     cls : 'roo-document-manager-thumb',
29165                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29166                 }
29167             ]
29168
29169         });
29170
29171         this.xhr.open(this.method, this.url, true);
29172         
29173         var headers = {
29174             "Accept": "application/json",
29175             "Cache-Control": "no-cache",
29176             "X-Requested-With": "XMLHttpRequest"
29177         };
29178         
29179         for (var headerName in headers) {
29180             var headerValue = headers[headerName];
29181             if (headerValue) {
29182                 this.xhr.setRequestHeader(headerName, headerValue);
29183             }
29184         }
29185         
29186         var _this = this;
29187         
29188         this.xhr.onload = function()
29189         {
29190             _this.xhrOnLoad(_this.xhr);
29191         }
29192         
29193         this.xhr.onerror = function()
29194         {
29195             _this.xhrOnError(_this.xhr);
29196         }
29197         
29198         var formData = new FormData();
29199
29200         formData.append('returnHTML', 'NO');
29201         
29202         if(crop){
29203             formData.append('crop', crop);
29204         }
29205         
29206         formData.append(this.paramName, file, file.name);
29207         
29208         var options = {
29209             file : file, 
29210             manually : false
29211         };
29212         
29213         if(this.fireEvent('prepare', this, formData, options) != false){
29214             
29215             if(options.manually){
29216                 return;
29217             }
29218             
29219             this.xhr.send(formData);
29220             return;
29221         };
29222         
29223         this.uploadCancel();
29224     },
29225     
29226     uploadCancel : function()
29227     {
29228         if (this.xhr) {
29229             this.xhr.abort();
29230         }
29231         
29232         this.delegates = [];
29233         
29234         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29235             el.remove();
29236         }, this);
29237         
29238         this.arrange();
29239     },
29240     
29241     renderPreview : function(file)
29242     {
29243         if(typeof(file.target) != 'undefined' && file.target){
29244             return file;
29245         }
29246         
29247         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29248         
29249         var previewEl = this.managerEl.createChild({
29250             tag : 'div',
29251             cls : 'roo-document-manager-preview',
29252             cn : [
29253                 {
29254                     tag : 'div',
29255                     tooltip : file[this.toolTipName],
29256                     cls : 'roo-document-manager-thumb',
29257                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29258                 },
29259                 {
29260                     tag : 'button',
29261                     cls : 'close',
29262                     html : '<i class="fa fa-times-circle"></i>'
29263                 }
29264             ]
29265         });
29266
29267         var close = previewEl.select('button.close', true).first();
29268
29269         close.on('click', this.onRemove, this, file);
29270
29271         file.target = previewEl;
29272
29273         var image = previewEl.select('img', true).first();
29274         
29275         var _this = this;
29276         
29277         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29278         
29279         image.on('click', this.onClick, this, file);
29280         
29281         this.fireEvent('previewrendered', this, file);
29282         
29283         return file;
29284         
29285     },
29286     
29287     onPreviewLoad : function(file, image)
29288     {
29289         if(typeof(file.target) == 'undefined' || !file.target){
29290             return;
29291         }
29292         
29293         var width = image.dom.naturalWidth || image.dom.width;
29294         var height = image.dom.naturalHeight || image.dom.height;
29295         
29296         if(width > height){
29297             file.target.addClass('wide');
29298             return;
29299         }
29300         
29301         file.target.addClass('tall');
29302         return;
29303         
29304     },
29305     
29306     uploadFromSource : function(file, crop)
29307     {
29308         this.xhr = new XMLHttpRequest();
29309         
29310         this.managerEl.createChild({
29311             tag : 'div',
29312             cls : 'roo-document-manager-loading',
29313             cn : [
29314                 {
29315                     tag : 'div',
29316                     tooltip : file.name,
29317                     cls : 'roo-document-manager-thumb',
29318                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29319                 }
29320             ]
29321
29322         });
29323
29324         this.xhr.open(this.method, this.url, true);
29325         
29326         var headers = {
29327             "Accept": "application/json",
29328             "Cache-Control": "no-cache",
29329             "X-Requested-With": "XMLHttpRequest"
29330         };
29331         
29332         for (var headerName in headers) {
29333             var headerValue = headers[headerName];
29334             if (headerValue) {
29335                 this.xhr.setRequestHeader(headerName, headerValue);
29336             }
29337         }
29338         
29339         var _this = this;
29340         
29341         this.xhr.onload = function()
29342         {
29343             _this.xhrOnLoad(_this.xhr);
29344         }
29345         
29346         this.xhr.onerror = function()
29347         {
29348             _this.xhrOnError(_this.xhr);
29349         }
29350         
29351         var formData = new FormData();
29352
29353         formData.append('returnHTML', 'NO');
29354         
29355         formData.append('crop', crop);
29356         
29357         if(typeof(file.filename) != 'undefined'){
29358             formData.append('filename', file.filename);
29359         }
29360         
29361         if(typeof(file.mimetype) != 'undefined'){
29362             formData.append('mimetype', file.mimetype);
29363         }
29364         
29365         Roo.log(formData);
29366         
29367         if(this.fireEvent('prepare', this, formData) != false){
29368             this.xhr.send(formData);
29369         };
29370     }
29371 });
29372
29373 /*
29374 * Licence: LGPL
29375 */
29376
29377 /**
29378  * @class Roo.bootstrap.DocumentViewer
29379  * @extends Roo.bootstrap.Component
29380  * Bootstrap DocumentViewer class
29381  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29382  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29383  * 
29384  * @constructor
29385  * Create a new DocumentViewer
29386  * @param {Object} config The config object
29387  */
29388
29389 Roo.bootstrap.DocumentViewer = function(config){
29390     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29391     
29392     this.addEvents({
29393         /**
29394          * @event initial
29395          * Fire after initEvent
29396          * @param {Roo.bootstrap.DocumentViewer} this
29397          */
29398         "initial" : true,
29399         /**
29400          * @event click
29401          * Fire after click
29402          * @param {Roo.bootstrap.DocumentViewer} this
29403          */
29404         "click" : true,
29405         /**
29406          * @event download
29407          * Fire after download button
29408          * @param {Roo.bootstrap.DocumentViewer} this
29409          */
29410         "download" : true,
29411         /**
29412          * @event trash
29413          * Fire after trash button
29414          * @param {Roo.bootstrap.DocumentViewer} this
29415          */
29416         "trash" : true
29417         
29418     });
29419 };
29420
29421 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29422     
29423     showDownload : true,
29424     
29425     showTrash : true,
29426     
29427     getAutoCreate : function()
29428     {
29429         var cfg = {
29430             tag : 'div',
29431             cls : 'roo-document-viewer',
29432             cn : [
29433                 {
29434                     tag : 'div',
29435                     cls : 'roo-document-viewer-body',
29436                     cn : [
29437                         {
29438                             tag : 'div',
29439                             cls : 'roo-document-viewer-thumb',
29440                             cn : [
29441                                 {
29442                                     tag : 'img',
29443                                     cls : 'roo-document-viewer-image'
29444                                 }
29445                             ]
29446                         }
29447                     ]
29448                 },
29449                 {
29450                     tag : 'div',
29451                     cls : 'roo-document-viewer-footer',
29452                     cn : {
29453                         tag : 'div',
29454                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29455                         cn : [
29456                             {
29457                                 tag : 'div',
29458                                 cls : 'btn-group roo-document-viewer-download',
29459                                 cn : [
29460                                     {
29461                                         tag : 'button',
29462                                         cls : 'btn btn-default',
29463                                         html : '<i class="fa fa-download"></i>'
29464                                     }
29465                                 ]
29466                             },
29467                             {
29468                                 tag : 'div',
29469                                 cls : 'btn-group roo-document-viewer-trash',
29470                                 cn : [
29471                                     {
29472                                         tag : 'button',
29473                                         cls : 'btn btn-default',
29474                                         html : '<i class="fa fa-trash"></i>'
29475                                     }
29476                                 ]
29477                             }
29478                         ]
29479                     }
29480                 }
29481             ]
29482         };
29483         
29484         return cfg;
29485     },
29486     
29487     initEvents : function()
29488     {
29489         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29490         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29491         
29492         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29493         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29494         
29495         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29496         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29497         
29498         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29499         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29500         
29501         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29502         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29503         
29504         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29505         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29506         
29507         this.bodyEl.on('click', this.onClick, this);
29508         this.downloadBtn.on('click', this.onDownload, this);
29509         this.trashBtn.on('click', this.onTrash, this);
29510         
29511         this.downloadBtn.hide();
29512         this.trashBtn.hide();
29513         
29514         if(this.showDownload){
29515             this.downloadBtn.show();
29516         }
29517         
29518         if(this.showTrash){
29519             this.trashBtn.show();
29520         }
29521         
29522         if(!this.showDownload && !this.showTrash) {
29523             this.footerEl.hide();
29524         }
29525         
29526     },
29527     
29528     initial : function()
29529     {
29530         this.fireEvent('initial', this);
29531         
29532     },
29533     
29534     onClick : function(e)
29535     {
29536         e.preventDefault();
29537         
29538         this.fireEvent('click', this);
29539     },
29540     
29541     onDownload : function(e)
29542     {
29543         e.preventDefault();
29544         
29545         this.fireEvent('download', this);
29546     },
29547     
29548     onTrash : function(e)
29549     {
29550         e.preventDefault();
29551         
29552         this.fireEvent('trash', this);
29553     }
29554     
29555 });
29556 /*
29557  * - LGPL
29558  *
29559  * nav progress bar
29560  * 
29561  */
29562
29563 /**
29564  * @class Roo.bootstrap.NavProgressBar
29565  * @extends Roo.bootstrap.Component
29566  * Bootstrap NavProgressBar class
29567  * 
29568  * @constructor
29569  * Create a new nav progress bar
29570  * @param {Object} config The config object
29571  */
29572
29573 Roo.bootstrap.NavProgressBar = function(config){
29574     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29575
29576     this.bullets = this.bullets || [];
29577    
29578 //    Roo.bootstrap.NavProgressBar.register(this);
29579      this.addEvents({
29580         /**
29581              * @event changed
29582              * Fires when the active item changes
29583              * @param {Roo.bootstrap.NavProgressBar} this
29584              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29585              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29586          */
29587         'changed': true
29588      });
29589     
29590 };
29591
29592 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29593     
29594     bullets : [],
29595     barItems : [],
29596     
29597     getAutoCreate : function()
29598     {
29599         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29600         
29601         cfg = {
29602             tag : 'div',
29603             cls : 'roo-navigation-bar-group',
29604             cn : [
29605                 {
29606                     tag : 'div',
29607                     cls : 'roo-navigation-top-bar'
29608                 },
29609                 {
29610                     tag : 'div',
29611                     cls : 'roo-navigation-bullets-bar',
29612                     cn : [
29613                         {
29614                             tag : 'ul',
29615                             cls : 'roo-navigation-bar'
29616                         }
29617                     ]
29618                 },
29619                 
29620                 {
29621                     tag : 'div',
29622                     cls : 'roo-navigation-bottom-bar'
29623                 }
29624             ]
29625             
29626         };
29627         
29628         return cfg;
29629         
29630     },
29631     
29632     initEvents: function() 
29633     {
29634         
29635     },
29636     
29637     onRender : function(ct, position) 
29638     {
29639         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29640         
29641         if(this.bullets.length){
29642             Roo.each(this.bullets, function(b){
29643                this.addItem(b);
29644             }, this);
29645         }
29646         
29647         this.format();
29648         
29649     },
29650     
29651     addItem : function(cfg)
29652     {
29653         var item = new Roo.bootstrap.NavProgressItem(cfg);
29654         
29655         item.parentId = this.id;
29656         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29657         
29658         if(cfg.html){
29659             var top = new Roo.bootstrap.Element({
29660                 tag : 'div',
29661                 cls : 'roo-navigation-bar-text'
29662             });
29663             
29664             var bottom = new Roo.bootstrap.Element({
29665                 tag : 'div',
29666                 cls : 'roo-navigation-bar-text'
29667             });
29668             
29669             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29670             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29671             
29672             var topText = new Roo.bootstrap.Element({
29673                 tag : 'span',
29674                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29675             });
29676             
29677             var bottomText = new Roo.bootstrap.Element({
29678                 tag : 'span',
29679                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29680             });
29681             
29682             topText.onRender(top.el, null);
29683             bottomText.onRender(bottom.el, null);
29684             
29685             item.topEl = top;
29686             item.bottomEl = bottom;
29687         }
29688         
29689         this.barItems.push(item);
29690         
29691         return item;
29692     },
29693     
29694     getActive : function()
29695     {
29696         var active = false;
29697         
29698         Roo.each(this.barItems, function(v){
29699             
29700             if (!v.isActive()) {
29701                 return;
29702             }
29703             
29704             active = v;
29705             return false;
29706             
29707         });
29708         
29709         return active;
29710     },
29711     
29712     setActiveItem : function(item)
29713     {
29714         var prev = false;
29715         
29716         Roo.each(this.barItems, function(v){
29717             if (v.rid == item.rid) {
29718                 return ;
29719             }
29720             
29721             if (v.isActive()) {
29722                 v.setActive(false);
29723                 prev = v;
29724             }
29725         });
29726
29727         item.setActive(true);
29728         
29729         this.fireEvent('changed', this, item, prev);
29730     },
29731     
29732     getBarItem: function(rid)
29733     {
29734         var ret = false;
29735         
29736         Roo.each(this.barItems, function(e) {
29737             if (e.rid != rid) {
29738                 return;
29739             }
29740             
29741             ret =  e;
29742             return false;
29743         });
29744         
29745         return ret;
29746     },
29747     
29748     indexOfItem : function(item)
29749     {
29750         var index = false;
29751         
29752         Roo.each(this.barItems, function(v, i){
29753             
29754             if (v.rid != item.rid) {
29755                 return;
29756             }
29757             
29758             index = i;
29759             return false
29760         });
29761         
29762         return index;
29763     },
29764     
29765     setActiveNext : function()
29766     {
29767         var i = this.indexOfItem(this.getActive());
29768         
29769         if (i > this.barItems.length) {
29770             return;
29771         }
29772         
29773         this.setActiveItem(this.barItems[i+1]);
29774     },
29775     
29776     setActivePrev : function()
29777     {
29778         var i = this.indexOfItem(this.getActive());
29779         
29780         if (i  < 1) {
29781             return;
29782         }
29783         
29784         this.setActiveItem(this.barItems[i-1]);
29785     },
29786     
29787     format : function()
29788     {
29789         if(!this.barItems.length){
29790             return;
29791         }
29792      
29793         var width = 100 / this.barItems.length;
29794         
29795         Roo.each(this.barItems, function(i){
29796             i.el.setStyle('width', width + '%');
29797             i.topEl.el.setStyle('width', width + '%');
29798             i.bottomEl.el.setStyle('width', width + '%');
29799         }, this);
29800         
29801     }
29802     
29803 });
29804 /*
29805  * - LGPL
29806  *
29807  * Nav Progress Item
29808  * 
29809  */
29810
29811 /**
29812  * @class Roo.bootstrap.NavProgressItem
29813  * @extends Roo.bootstrap.Component
29814  * Bootstrap NavProgressItem class
29815  * @cfg {String} rid the reference id
29816  * @cfg {Boolean} active (true|false) Is item active default false
29817  * @cfg {Boolean} disabled (true|false) Is item active default false
29818  * @cfg {String} html
29819  * @cfg {String} position (top|bottom) text position default bottom
29820  * @cfg {String} icon show icon instead of number
29821  * 
29822  * @constructor
29823  * Create a new NavProgressItem
29824  * @param {Object} config The config object
29825  */
29826 Roo.bootstrap.NavProgressItem = function(config){
29827     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29828     this.addEvents({
29829         // raw events
29830         /**
29831          * @event click
29832          * The raw click event for the entire grid.
29833          * @param {Roo.bootstrap.NavProgressItem} this
29834          * @param {Roo.EventObject} e
29835          */
29836         "click" : true
29837     });
29838    
29839 };
29840
29841 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29842     
29843     rid : '',
29844     active : false,
29845     disabled : false,
29846     html : '',
29847     position : 'bottom',
29848     icon : false,
29849     
29850     getAutoCreate : function()
29851     {
29852         var iconCls = 'roo-navigation-bar-item-icon';
29853         
29854         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29855         
29856         var cfg = {
29857             tag: 'li',
29858             cls: 'roo-navigation-bar-item',
29859             cn : [
29860                 {
29861                     tag : 'i',
29862                     cls : iconCls
29863                 }
29864             ]
29865         };
29866         
29867         if(this.active){
29868             cfg.cls += ' active';
29869         }
29870         if(this.disabled){
29871             cfg.cls += ' disabled';
29872         }
29873         
29874         return cfg;
29875     },
29876     
29877     disable : function()
29878     {
29879         this.setDisabled(true);
29880     },
29881     
29882     enable : function()
29883     {
29884         this.setDisabled(false);
29885     },
29886     
29887     initEvents: function() 
29888     {
29889         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29890         
29891         this.iconEl.on('click', this.onClick, this);
29892     },
29893     
29894     onClick : function(e)
29895     {
29896         e.preventDefault();
29897         
29898         if(this.disabled){
29899             return;
29900         }
29901         
29902         if(this.fireEvent('click', this, e) === false){
29903             return;
29904         };
29905         
29906         this.parent().setActiveItem(this);
29907     },
29908     
29909     isActive: function () 
29910     {
29911         return this.active;
29912     },
29913     
29914     setActive : function(state)
29915     {
29916         if(this.active == state){
29917             return;
29918         }
29919         
29920         this.active = state;
29921         
29922         if (state) {
29923             this.el.addClass('active');
29924             return;
29925         }
29926         
29927         this.el.removeClass('active');
29928         
29929         return;
29930     },
29931     
29932     setDisabled : function(state)
29933     {
29934         if(this.disabled == state){
29935             return;
29936         }
29937         
29938         this.disabled = state;
29939         
29940         if (state) {
29941             this.el.addClass('disabled');
29942             return;
29943         }
29944         
29945         this.el.removeClass('disabled');
29946     },
29947     
29948     tooltipEl : function()
29949     {
29950         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29951     }
29952 });
29953  
29954
29955  /*
29956  * - LGPL
29957  *
29958  * FieldLabel
29959  * 
29960  */
29961
29962 /**
29963  * @class Roo.bootstrap.FieldLabel
29964  * @extends Roo.bootstrap.Component
29965  * Bootstrap FieldLabel class
29966  * @cfg {String} html contents of the element
29967  * @cfg {String} tag tag of the element default label
29968  * @cfg {String} cls class of the element
29969  * @cfg {String} target label target 
29970  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29971  * @cfg {String} invalidClass default "text-warning"
29972  * @cfg {String} validClass default "text-success"
29973  * @cfg {String} iconTooltip default "This field is required"
29974  * @cfg {String} indicatorpos (left|right) default left
29975  * 
29976  * @constructor
29977  * Create a new FieldLabel
29978  * @param {Object} config The config object
29979  */
29980
29981 Roo.bootstrap.FieldLabel = function(config){
29982     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29983     
29984     this.addEvents({
29985             /**
29986              * @event invalid
29987              * Fires after the field has been marked as invalid.
29988              * @param {Roo.form.FieldLabel} this
29989              * @param {String} msg The validation message
29990              */
29991             invalid : true,
29992             /**
29993              * @event valid
29994              * Fires after the field has been validated with no errors.
29995              * @param {Roo.form.FieldLabel} this
29996              */
29997             valid : true
29998         });
29999 };
30000
30001 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30002     
30003     tag: 'label',
30004     cls: '',
30005     html: '',
30006     target: '',
30007     allowBlank : true,
30008     invalidClass : 'has-warning',
30009     validClass : 'has-success',
30010     iconTooltip : 'This field is required',
30011     indicatorpos : 'left',
30012     
30013     getAutoCreate : function(){
30014         
30015         var cls = "";
30016         if (!this.allowBlank) {
30017             cls  = "visible";
30018         }
30019         
30020         var cfg = {
30021             tag : this.tag,
30022             cls : 'roo-bootstrap-field-label ' + this.cls,
30023             for : this.target,
30024             cn : [
30025                 {
30026                     tag : 'i',
30027                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30028                     tooltip : this.iconTooltip
30029                 },
30030                 {
30031                     tag : 'span',
30032                     html : this.html
30033                 }
30034             ] 
30035         };
30036         
30037         if(this.indicatorpos == 'right'){
30038             var cfg = {
30039                 tag : this.tag,
30040                 cls : 'roo-bootstrap-field-label ' + this.cls,
30041                 for : this.target,
30042                 cn : [
30043                     {
30044                         tag : 'span',
30045                         html : this.html
30046                     },
30047                     {
30048                         tag : 'i',
30049                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30050                         tooltip : this.iconTooltip
30051                     }
30052                 ] 
30053             };
30054         }
30055         
30056         return cfg;
30057     },
30058     
30059     initEvents: function() 
30060     {
30061         Roo.bootstrap.Element.superclass.initEvents.call(this);
30062         
30063         this.indicator = this.indicatorEl();
30064         
30065         if(this.indicator){
30066             this.indicator.removeClass('visible');
30067             this.indicator.addClass('invisible');
30068         }
30069         
30070         Roo.bootstrap.FieldLabel.register(this);
30071     },
30072     
30073     indicatorEl : function()
30074     {
30075         var indicator = this.el.select('i.roo-required-indicator',true).first();
30076         
30077         if(!indicator){
30078             return false;
30079         }
30080         
30081         return indicator;
30082         
30083     },
30084     
30085     /**
30086      * Mark this field as valid
30087      */
30088     markValid : function()
30089     {
30090         if(this.indicator){
30091             this.indicator.removeClass('visible');
30092             this.indicator.addClass('invisible');
30093         }
30094         
30095         this.el.removeClass(this.invalidClass);
30096         
30097         this.el.addClass(this.validClass);
30098         
30099         this.fireEvent('valid', this);
30100     },
30101     
30102     /**
30103      * Mark this field as invalid
30104      * @param {String} msg The validation message
30105      */
30106     markInvalid : function(msg)
30107     {
30108         if(this.indicator){
30109             this.indicator.removeClass('invisible');
30110             this.indicator.addClass('visible');
30111         }
30112         
30113         this.el.removeClass(this.validClass);
30114         
30115         this.el.addClass(this.invalidClass);
30116         
30117         this.fireEvent('invalid', this, msg);
30118     }
30119     
30120    
30121 });
30122
30123 Roo.apply(Roo.bootstrap.FieldLabel, {
30124     
30125     groups: {},
30126     
30127      /**
30128     * register a FieldLabel Group
30129     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30130     */
30131     register : function(label)
30132     {
30133         if(this.groups.hasOwnProperty(label.target)){
30134             return;
30135         }
30136      
30137         this.groups[label.target] = label;
30138         
30139     },
30140     /**
30141     * fetch a FieldLabel Group based on the target
30142     * @param {string} target
30143     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30144     */
30145     get: function(target) {
30146         if (typeof(this.groups[target]) == 'undefined') {
30147             return false;
30148         }
30149         
30150         return this.groups[target] ;
30151     }
30152 });
30153
30154  
30155
30156  /*
30157  * - LGPL
30158  *
30159  * page DateSplitField.
30160  * 
30161  */
30162
30163
30164 /**
30165  * @class Roo.bootstrap.DateSplitField
30166  * @extends Roo.bootstrap.Component
30167  * Bootstrap DateSplitField class
30168  * @cfg {string} fieldLabel - the label associated
30169  * @cfg {Number} labelWidth set the width of label (0-12)
30170  * @cfg {String} labelAlign (top|left)
30171  * @cfg {Boolean} dayAllowBlank (true|false) default false
30172  * @cfg {Boolean} monthAllowBlank (true|false) default false
30173  * @cfg {Boolean} yearAllowBlank (true|false) default false
30174  * @cfg {string} dayPlaceholder 
30175  * @cfg {string} monthPlaceholder
30176  * @cfg {string} yearPlaceholder
30177  * @cfg {string} dayFormat default 'd'
30178  * @cfg {string} monthFormat default 'm'
30179  * @cfg {string} yearFormat default 'Y'
30180  * @cfg {Number} labellg set the width of label (1-12)
30181  * @cfg {Number} labelmd set the width of label (1-12)
30182  * @cfg {Number} labelsm set the width of label (1-12)
30183  * @cfg {Number} labelxs set the width of label (1-12)
30184
30185  *     
30186  * @constructor
30187  * Create a new DateSplitField
30188  * @param {Object} config The config object
30189  */
30190
30191 Roo.bootstrap.DateSplitField = function(config){
30192     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30193     
30194     this.addEvents({
30195         // raw events
30196          /**
30197          * @event years
30198          * getting the data of years
30199          * @param {Roo.bootstrap.DateSplitField} this
30200          * @param {Object} years
30201          */
30202         "years" : true,
30203         /**
30204          * @event days
30205          * getting the data of days
30206          * @param {Roo.bootstrap.DateSplitField} this
30207          * @param {Object} days
30208          */
30209         "days" : true,
30210         /**
30211          * @event invalid
30212          * Fires after the field has been marked as invalid.
30213          * @param {Roo.form.Field} this
30214          * @param {String} msg The validation message
30215          */
30216         invalid : true,
30217        /**
30218          * @event valid
30219          * Fires after the field has been validated with no errors.
30220          * @param {Roo.form.Field} this
30221          */
30222         valid : true
30223     });
30224 };
30225
30226 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30227     
30228     fieldLabel : '',
30229     labelAlign : 'top',
30230     labelWidth : 3,
30231     dayAllowBlank : false,
30232     monthAllowBlank : false,
30233     yearAllowBlank : false,
30234     dayPlaceholder : '',
30235     monthPlaceholder : '',
30236     yearPlaceholder : '',
30237     dayFormat : 'd',
30238     monthFormat : 'm',
30239     yearFormat : 'Y',
30240     isFormField : true,
30241     labellg : 0,
30242     labelmd : 0,
30243     labelsm : 0,
30244     labelxs : 0,
30245     
30246     getAutoCreate : function()
30247     {
30248         var cfg = {
30249             tag : 'div',
30250             cls : 'row roo-date-split-field-group',
30251             cn : [
30252                 {
30253                     tag : 'input',
30254                     type : 'hidden',
30255                     cls : 'form-hidden-field roo-date-split-field-group-value',
30256                     name : this.name
30257                 }
30258             ]
30259         };
30260         
30261         var labelCls = 'col-md-12';
30262         var contentCls = 'col-md-4';
30263         
30264         if(this.fieldLabel){
30265             
30266             var label = {
30267                 tag : 'div',
30268                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30269                 cn : [
30270                     {
30271                         tag : 'label',
30272                         html : this.fieldLabel
30273                     }
30274                 ]
30275             };
30276             
30277             if(this.labelAlign == 'left'){
30278             
30279                 if(this.labelWidth > 12){
30280                     label.style = "width: " + this.labelWidth + 'px';
30281                 }
30282
30283                 if(this.labelWidth < 13 && this.labelmd == 0){
30284                     this.labelmd = this.labelWidth;
30285                 }
30286
30287                 if(this.labellg > 0){
30288                     labelCls = ' col-lg-' + this.labellg;
30289                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30290                 }
30291
30292                 if(this.labelmd > 0){
30293                     labelCls = ' col-md-' + this.labelmd;
30294                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30295                 }
30296
30297                 if(this.labelsm > 0){
30298                     labelCls = ' col-sm-' + this.labelsm;
30299                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30300                 }
30301
30302                 if(this.labelxs > 0){
30303                     labelCls = ' col-xs-' + this.labelxs;
30304                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30305                 }
30306             }
30307             
30308             label.cls += ' ' + labelCls;
30309             
30310             cfg.cn.push(label);
30311         }
30312         
30313         Roo.each(['day', 'month', 'year'], function(t){
30314             cfg.cn.push({
30315                 tag : 'div',
30316                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30317             });
30318         }, this);
30319         
30320         return cfg;
30321     },
30322     
30323     inputEl: function ()
30324     {
30325         return this.el.select('.roo-date-split-field-group-value', true).first();
30326     },
30327     
30328     onRender : function(ct, position) 
30329     {
30330         var _this = this;
30331         
30332         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30333         
30334         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30335         
30336         this.dayField = new Roo.bootstrap.ComboBox({
30337             allowBlank : this.dayAllowBlank,
30338             alwaysQuery : true,
30339             displayField : 'value',
30340             editable : false,
30341             fieldLabel : '',
30342             forceSelection : true,
30343             mode : 'local',
30344             placeholder : this.dayPlaceholder,
30345             selectOnFocus : true,
30346             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30347             triggerAction : 'all',
30348             typeAhead : true,
30349             valueField : 'value',
30350             store : new Roo.data.SimpleStore({
30351                 data : (function() {    
30352                     var days = [];
30353                     _this.fireEvent('days', _this, days);
30354                     return days;
30355                 })(),
30356                 fields : [ 'value' ]
30357             }),
30358             listeners : {
30359                 select : function (_self, record, index)
30360                 {
30361                     _this.setValue(_this.getValue());
30362                 }
30363             }
30364         });
30365
30366         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30367         
30368         this.monthField = new Roo.bootstrap.MonthField({
30369             after : '<i class=\"fa fa-calendar\"></i>',
30370             allowBlank : this.monthAllowBlank,
30371             placeholder : this.monthPlaceholder,
30372             readOnly : true,
30373             listeners : {
30374                 render : function (_self)
30375                 {
30376                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30377                         e.preventDefault();
30378                         _self.focus();
30379                     });
30380                 },
30381                 select : function (_self, oldvalue, newvalue)
30382                 {
30383                     _this.setValue(_this.getValue());
30384                 }
30385             }
30386         });
30387         
30388         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30389         
30390         this.yearField = new Roo.bootstrap.ComboBox({
30391             allowBlank : this.yearAllowBlank,
30392             alwaysQuery : true,
30393             displayField : 'value',
30394             editable : false,
30395             fieldLabel : '',
30396             forceSelection : true,
30397             mode : 'local',
30398             placeholder : this.yearPlaceholder,
30399             selectOnFocus : true,
30400             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30401             triggerAction : 'all',
30402             typeAhead : true,
30403             valueField : 'value',
30404             store : new Roo.data.SimpleStore({
30405                 data : (function() {
30406                     var years = [];
30407                     _this.fireEvent('years', _this, years);
30408                     return years;
30409                 })(),
30410                 fields : [ 'value' ]
30411             }),
30412             listeners : {
30413                 select : function (_self, record, index)
30414                 {
30415                     _this.setValue(_this.getValue());
30416                 }
30417             }
30418         });
30419
30420         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30421     },
30422     
30423     setValue : function(v, format)
30424     {
30425         this.inputEl.dom.value = v;
30426         
30427         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30428         
30429         var d = Date.parseDate(v, f);
30430         
30431         if(!d){
30432             this.validate();
30433             return;
30434         }
30435         
30436         this.setDay(d.format(this.dayFormat));
30437         this.setMonth(d.format(this.monthFormat));
30438         this.setYear(d.format(this.yearFormat));
30439         
30440         this.validate();
30441         
30442         return;
30443     },
30444     
30445     setDay : function(v)
30446     {
30447         this.dayField.setValue(v);
30448         this.inputEl.dom.value = this.getValue();
30449         this.validate();
30450         return;
30451     },
30452     
30453     setMonth : function(v)
30454     {
30455         this.monthField.setValue(v, true);
30456         this.inputEl.dom.value = this.getValue();
30457         this.validate();
30458         return;
30459     },
30460     
30461     setYear : function(v)
30462     {
30463         this.yearField.setValue(v);
30464         this.inputEl.dom.value = this.getValue();
30465         this.validate();
30466         return;
30467     },
30468     
30469     getDay : function()
30470     {
30471         return this.dayField.getValue();
30472     },
30473     
30474     getMonth : function()
30475     {
30476         return this.monthField.getValue();
30477     },
30478     
30479     getYear : function()
30480     {
30481         return this.yearField.getValue();
30482     },
30483     
30484     getValue : function()
30485     {
30486         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30487         
30488         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30489         
30490         return date;
30491     },
30492     
30493     reset : function()
30494     {
30495         this.setDay('');
30496         this.setMonth('');
30497         this.setYear('');
30498         this.inputEl.dom.value = '';
30499         this.validate();
30500         return;
30501     },
30502     
30503     validate : function()
30504     {
30505         var d = this.dayField.validate();
30506         var m = this.monthField.validate();
30507         var y = this.yearField.validate();
30508         
30509         var valid = true;
30510         
30511         if(
30512                 (!this.dayAllowBlank && !d) ||
30513                 (!this.monthAllowBlank && !m) ||
30514                 (!this.yearAllowBlank && !y)
30515         ){
30516             valid = false;
30517         }
30518         
30519         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30520             return valid;
30521         }
30522         
30523         if(valid){
30524             this.markValid();
30525             return valid;
30526         }
30527         
30528         this.markInvalid();
30529         
30530         return valid;
30531     },
30532     
30533     markValid : function()
30534     {
30535         
30536         var label = this.el.select('label', true).first();
30537         var icon = this.el.select('i.fa-star', true).first();
30538
30539         if(label && icon){
30540             icon.remove();
30541         }
30542         
30543         this.fireEvent('valid', this);
30544     },
30545     
30546      /**
30547      * Mark this field as invalid
30548      * @param {String} msg The validation message
30549      */
30550     markInvalid : function(msg)
30551     {
30552         
30553         var label = this.el.select('label', true).first();
30554         var icon = this.el.select('i.fa-star', true).first();
30555
30556         if(label && !icon){
30557             this.el.select('.roo-date-split-field-label', true).createChild({
30558                 tag : 'i',
30559                 cls : 'text-danger fa fa-lg fa-star',
30560                 tooltip : 'This field is required',
30561                 style : 'margin-right:5px;'
30562             }, label, true);
30563         }
30564         
30565         this.fireEvent('invalid', this, msg);
30566     },
30567     
30568     clearInvalid : function()
30569     {
30570         var label = this.el.select('label', true).first();
30571         var icon = this.el.select('i.fa-star', true).first();
30572
30573         if(label && icon){
30574             icon.remove();
30575         }
30576         
30577         this.fireEvent('valid', this);
30578     },
30579     
30580     getName: function()
30581     {
30582         return this.name;
30583     }
30584     
30585 });
30586
30587  /**
30588  *
30589  * This is based on 
30590  * http://masonry.desandro.com
30591  *
30592  * The idea is to render all the bricks based on vertical width...
30593  *
30594  * The original code extends 'outlayer' - we might need to use that....
30595  * 
30596  */
30597
30598
30599 /**
30600  * @class Roo.bootstrap.LayoutMasonry
30601  * @extends Roo.bootstrap.Component
30602  * Bootstrap Layout Masonry class
30603  * 
30604  * @constructor
30605  * Create a new Element
30606  * @param {Object} config The config object
30607  */
30608
30609 Roo.bootstrap.LayoutMasonry = function(config){
30610     
30611     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30612     
30613     this.bricks = [];
30614     
30615     Roo.bootstrap.LayoutMasonry.register(this);
30616     
30617     this.addEvents({
30618         // raw events
30619         /**
30620          * @event layout
30621          * Fire after layout the items
30622          * @param {Roo.bootstrap.LayoutMasonry} this
30623          * @param {Roo.EventObject} e
30624          */
30625         "layout" : true
30626     });
30627     
30628 };
30629
30630 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30631     
30632     /**
30633      * @cfg {Boolean} isLayoutInstant = no animation?
30634      */   
30635     isLayoutInstant : false, // needed?
30636    
30637     /**
30638      * @cfg {Number} boxWidth  width of the columns
30639      */   
30640     boxWidth : 450,
30641     
30642       /**
30643      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30644      */   
30645     boxHeight : 0,
30646     
30647     /**
30648      * @cfg {Number} padWidth padding below box..
30649      */   
30650     padWidth : 10, 
30651     
30652     /**
30653      * @cfg {Number} gutter gutter width..
30654      */   
30655     gutter : 10,
30656     
30657      /**
30658      * @cfg {Number} maxCols maximum number of columns
30659      */   
30660     
30661     maxCols: 0,
30662     
30663     /**
30664      * @cfg {Boolean} isAutoInitial defalut true
30665      */   
30666     isAutoInitial : true, 
30667     
30668     containerWidth: 0,
30669     
30670     /**
30671      * @cfg {Boolean} isHorizontal defalut false
30672      */   
30673     isHorizontal : false, 
30674
30675     currentSize : null,
30676     
30677     tag: 'div',
30678     
30679     cls: '',
30680     
30681     bricks: null, //CompositeElement
30682     
30683     cols : 1,
30684     
30685     _isLayoutInited : false,
30686     
30687 //    isAlternative : false, // only use for vertical layout...
30688     
30689     /**
30690      * @cfg {Number} alternativePadWidth padding below box..
30691      */   
30692     alternativePadWidth : 50,
30693     
30694     selectedBrick : [],
30695     
30696     getAutoCreate : function(){
30697         
30698         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30699         
30700         var cfg = {
30701             tag: this.tag,
30702             cls: 'blog-masonary-wrapper ' + this.cls,
30703             cn : {
30704                 cls : 'mas-boxes masonary'
30705             }
30706         };
30707         
30708         return cfg;
30709     },
30710     
30711     getChildContainer: function( )
30712     {
30713         if (this.boxesEl) {
30714             return this.boxesEl;
30715         }
30716         
30717         this.boxesEl = this.el.select('.mas-boxes').first();
30718         
30719         return this.boxesEl;
30720     },
30721     
30722     
30723     initEvents : function()
30724     {
30725         var _this = this;
30726         
30727         if(this.isAutoInitial){
30728             Roo.log('hook children rendered');
30729             this.on('childrenrendered', function() {
30730                 Roo.log('children rendered');
30731                 _this.initial();
30732             } ,this);
30733         }
30734     },
30735     
30736     initial : function()
30737     {
30738         this.selectedBrick = [];
30739         
30740         this.currentSize = this.el.getBox(true);
30741         
30742         Roo.EventManager.onWindowResize(this.resize, this); 
30743
30744         if(!this.isAutoInitial){
30745             this.layout();
30746             return;
30747         }
30748         
30749         this.layout();
30750         
30751         return;
30752         //this.layout.defer(500,this);
30753         
30754     },
30755     
30756     resize : function()
30757     {
30758         var cs = this.el.getBox(true);
30759         
30760         if (
30761                 this.currentSize.width == cs.width && 
30762                 this.currentSize.x == cs.x && 
30763                 this.currentSize.height == cs.height && 
30764                 this.currentSize.y == cs.y 
30765         ) {
30766             Roo.log("no change in with or X or Y");
30767             return;
30768         }
30769         
30770         this.currentSize = cs;
30771         
30772         this.layout();
30773         
30774     },
30775     
30776     layout : function()
30777     {   
30778         this._resetLayout();
30779         
30780         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30781         
30782         this.layoutItems( isInstant );
30783       
30784         this._isLayoutInited = true;
30785         
30786         this.fireEvent('layout', this);
30787         
30788     },
30789     
30790     _resetLayout : function()
30791     {
30792         if(this.isHorizontal){
30793             this.horizontalMeasureColumns();
30794             return;
30795         }
30796         
30797         this.verticalMeasureColumns();
30798         
30799     },
30800     
30801     verticalMeasureColumns : function()
30802     {
30803         this.getContainerWidth();
30804         
30805 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30806 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30807 //            return;
30808 //        }
30809         
30810         var boxWidth = this.boxWidth + this.padWidth;
30811         
30812         if(this.containerWidth < this.boxWidth){
30813             boxWidth = this.containerWidth
30814         }
30815         
30816         var containerWidth = this.containerWidth;
30817         
30818         var cols = Math.floor(containerWidth / boxWidth);
30819         
30820         this.cols = Math.max( cols, 1 );
30821         
30822         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30823         
30824         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30825         
30826         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30827         
30828         this.colWidth = boxWidth + avail - this.padWidth;
30829         
30830         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30831         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30832     },
30833     
30834     horizontalMeasureColumns : function()
30835     {
30836         this.getContainerWidth();
30837         
30838         var boxWidth = this.boxWidth;
30839         
30840         if(this.containerWidth < boxWidth){
30841             boxWidth = this.containerWidth;
30842         }
30843         
30844         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30845         
30846         this.el.setHeight(boxWidth);
30847         
30848     },
30849     
30850     getContainerWidth : function()
30851     {
30852         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30853     },
30854     
30855     layoutItems : function( isInstant )
30856     {
30857         Roo.log(this.bricks);
30858         
30859         var items = Roo.apply([], this.bricks);
30860         
30861         if(this.isHorizontal){
30862             this._horizontalLayoutItems( items , isInstant );
30863             return;
30864         }
30865         
30866 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30867 //            this._verticalAlternativeLayoutItems( items , isInstant );
30868 //            return;
30869 //        }
30870         
30871         this._verticalLayoutItems( items , isInstant );
30872         
30873     },
30874     
30875     _verticalLayoutItems : function ( items , isInstant)
30876     {
30877         if ( !items || !items.length ) {
30878             return;
30879         }
30880         
30881         var standard = [
30882             ['xs', 'xs', 'xs', 'tall'],
30883             ['xs', 'xs', 'tall'],
30884             ['xs', 'xs', 'sm'],
30885             ['xs', 'xs', 'xs'],
30886             ['xs', 'tall'],
30887             ['xs', 'sm'],
30888             ['xs', 'xs'],
30889             ['xs'],
30890             
30891             ['sm', 'xs', 'xs'],
30892             ['sm', 'xs'],
30893             ['sm'],
30894             
30895             ['tall', 'xs', 'xs', 'xs'],
30896             ['tall', 'xs', 'xs'],
30897             ['tall', 'xs'],
30898             ['tall']
30899             
30900         ];
30901         
30902         var queue = [];
30903         
30904         var boxes = [];
30905         
30906         var box = [];
30907         
30908         Roo.each(items, function(item, k){
30909             
30910             switch (item.size) {
30911                 // these layouts take up a full box,
30912                 case 'md' :
30913                 case 'md-left' :
30914                 case 'md-right' :
30915                 case 'wide' :
30916                     
30917                     if(box.length){
30918                         boxes.push(box);
30919                         box = [];
30920                     }
30921                     
30922                     boxes.push([item]);
30923                     
30924                     break;
30925                     
30926                 case 'xs' :
30927                 case 'sm' :
30928                 case 'tall' :
30929                     
30930                     box.push(item);
30931                     
30932                     break;
30933                 default :
30934                     break;
30935                     
30936             }
30937             
30938         }, this);
30939         
30940         if(box.length){
30941             boxes.push(box);
30942             box = [];
30943         }
30944         
30945         var filterPattern = function(box, length)
30946         {
30947             if(!box.length){
30948                 return;
30949             }
30950             
30951             var match = false;
30952             
30953             var pattern = box.slice(0, length);
30954             
30955             var format = [];
30956             
30957             Roo.each(pattern, function(i){
30958                 format.push(i.size);
30959             }, this);
30960             
30961             Roo.each(standard, function(s){
30962                 
30963                 if(String(s) != String(format)){
30964                     return;
30965                 }
30966                 
30967                 match = true;
30968                 return false;
30969                 
30970             }, this);
30971             
30972             if(!match && length == 1){
30973                 return;
30974             }
30975             
30976             if(!match){
30977                 filterPattern(box, length - 1);
30978                 return;
30979             }
30980                 
30981             queue.push(pattern);
30982
30983             box = box.slice(length, box.length);
30984
30985             filterPattern(box, 4);
30986
30987             return;
30988             
30989         }
30990         
30991         Roo.each(boxes, function(box, k){
30992             
30993             if(!box.length){
30994                 return;
30995             }
30996             
30997             if(box.length == 1){
30998                 queue.push(box);
30999                 return;
31000             }
31001             
31002             filterPattern(box, 4);
31003             
31004         }, this);
31005         
31006         this._processVerticalLayoutQueue( queue, isInstant );
31007         
31008     },
31009     
31010 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31011 //    {
31012 //        if ( !items || !items.length ) {
31013 //            return;
31014 //        }
31015 //
31016 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31017 //        
31018 //    },
31019     
31020     _horizontalLayoutItems : function ( items , isInstant)
31021     {
31022         if ( !items || !items.length || items.length < 3) {
31023             return;
31024         }
31025         
31026         items.reverse();
31027         
31028         var eItems = items.slice(0, 3);
31029         
31030         items = items.slice(3, items.length);
31031         
31032         var standard = [
31033             ['xs', 'xs', 'xs', 'wide'],
31034             ['xs', 'xs', 'wide'],
31035             ['xs', 'xs', 'sm'],
31036             ['xs', 'xs', 'xs'],
31037             ['xs', 'wide'],
31038             ['xs', 'sm'],
31039             ['xs', 'xs'],
31040             ['xs'],
31041             
31042             ['sm', 'xs', 'xs'],
31043             ['sm', 'xs'],
31044             ['sm'],
31045             
31046             ['wide', 'xs', 'xs', 'xs'],
31047             ['wide', 'xs', 'xs'],
31048             ['wide', 'xs'],
31049             ['wide'],
31050             
31051             ['wide-thin']
31052         ];
31053         
31054         var queue = [];
31055         
31056         var boxes = [];
31057         
31058         var box = [];
31059         
31060         Roo.each(items, function(item, k){
31061             
31062             switch (item.size) {
31063                 case 'md' :
31064                 case 'md-left' :
31065                 case 'md-right' :
31066                 case 'tall' :
31067                     
31068                     if(box.length){
31069                         boxes.push(box);
31070                         box = [];
31071                     }
31072                     
31073                     boxes.push([item]);
31074                     
31075                     break;
31076                     
31077                 case 'xs' :
31078                 case 'sm' :
31079                 case 'wide' :
31080                 case 'wide-thin' :
31081                     
31082                     box.push(item);
31083                     
31084                     break;
31085                 default :
31086                     break;
31087                     
31088             }
31089             
31090         }, this);
31091         
31092         if(box.length){
31093             boxes.push(box);
31094             box = [];
31095         }
31096         
31097         var filterPattern = function(box, length)
31098         {
31099             if(!box.length){
31100                 return;
31101             }
31102             
31103             var match = false;
31104             
31105             var pattern = box.slice(0, length);
31106             
31107             var format = [];
31108             
31109             Roo.each(pattern, function(i){
31110                 format.push(i.size);
31111             }, this);
31112             
31113             Roo.each(standard, function(s){
31114                 
31115                 if(String(s) != String(format)){
31116                     return;
31117                 }
31118                 
31119                 match = true;
31120                 return false;
31121                 
31122             }, this);
31123             
31124             if(!match && length == 1){
31125                 return;
31126             }
31127             
31128             if(!match){
31129                 filterPattern(box, length - 1);
31130                 return;
31131             }
31132                 
31133             queue.push(pattern);
31134
31135             box = box.slice(length, box.length);
31136
31137             filterPattern(box, 4);
31138
31139             return;
31140             
31141         }
31142         
31143         Roo.each(boxes, function(box, k){
31144             
31145             if(!box.length){
31146                 return;
31147             }
31148             
31149             if(box.length == 1){
31150                 queue.push(box);
31151                 return;
31152             }
31153             
31154             filterPattern(box, 4);
31155             
31156         }, this);
31157         
31158         
31159         var prune = [];
31160         
31161         var pos = this.el.getBox(true);
31162         
31163         var minX = pos.x;
31164         
31165         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31166         
31167         var hit_end = false;
31168         
31169         Roo.each(queue, function(box){
31170             
31171             if(hit_end){
31172                 
31173                 Roo.each(box, function(b){
31174                 
31175                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31176                     b.el.hide();
31177
31178                 }, this);
31179
31180                 return;
31181             }
31182             
31183             var mx = 0;
31184             
31185             Roo.each(box, function(b){
31186                 
31187                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31188                 b.el.show();
31189
31190                 mx = Math.max(mx, b.x);
31191                 
31192             }, this);
31193             
31194             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31195             
31196             if(maxX < minX){
31197                 
31198                 Roo.each(box, function(b){
31199                 
31200                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31201                     b.el.hide();
31202                     
31203                 }, this);
31204                 
31205                 hit_end = true;
31206                 
31207                 return;
31208             }
31209             
31210             prune.push(box);
31211             
31212         }, this);
31213         
31214         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31215     },
31216     
31217     /** Sets position of item in DOM
31218     * @param {Element} item
31219     * @param {Number} x - horizontal position
31220     * @param {Number} y - vertical position
31221     * @param {Boolean} isInstant - disables transitions
31222     */
31223     _processVerticalLayoutQueue : function( queue, isInstant )
31224     {
31225         var pos = this.el.getBox(true);
31226         var x = pos.x;
31227         var y = pos.y;
31228         var maxY = [];
31229         
31230         for (var i = 0; i < this.cols; i++){
31231             maxY[i] = pos.y;
31232         }
31233         
31234         Roo.each(queue, function(box, k){
31235             
31236             var col = k % this.cols;
31237             
31238             Roo.each(box, function(b,kk){
31239                 
31240                 b.el.position('absolute');
31241                 
31242                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31243                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31244                 
31245                 if(b.size == 'md-left' || b.size == 'md-right'){
31246                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31247                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31248                 }
31249                 
31250                 b.el.setWidth(width);
31251                 b.el.setHeight(height);
31252                 // iframe?
31253                 b.el.select('iframe',true).setSize(width,height);
31254                 
31255             }, this);
31256             
31257             for (var i = 0; i < this.cols; i++){
31258                 
31259                 if(maxY[i] < maxY[col]){
31260                     col = i;
31261                     continue;
31262                 }
31263                 
31264                 col = Math.min(col, i);
31265                 
31266             }
31267             
31268             x = pos.x + col * (this.colWidth + this.padWidth);
31269             
31270             y = maxY[col];
31271             
31272             var positions = [];
31273             
31274             switch (box.length){
31275                 case 1 :
31276                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31277                     break;
31278                 case 2 :
31279                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31280                     break;
31281                 case 3 :
31282                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31283                     break;
31284                 case 4 :
31285                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31286                     break;
31287                 default :
31288                     break;
31289             }
31290             
31291             Roo.each(box, function(b,kk){
31292                 
31293                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31294                 
31295                 var sz = b.el.getSize();
31296                 
31297                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31298                 
31299             }, this);
31300             
31301         }, this);
31302         
31303         var mY = 0;
31304         
31305         for (var i = 0; i < this.cols; i++){
31306             mY = Math.max(mY, maxY[i]);
31307         }
31308         
31309         this.el.setHeight(mY - pos.y);
31310         
31311     },
31312     
31313 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31314 //    {
31315 //        var pos = this.el.getBox(true);
31316 //        var x = pos.x;
31317 //        var y = pos.y;
31318 //        var maxX = pos.right;
31319 //        
31320 //        var maxHeight = 0;
31321 //        
31322 //        Roo.each(items, function(item, k){
31323 //            
31324 //            var c = k % 2;
31325 //            
31326 //            item.el.position('absolute');
31327 //                
31328 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31329 //
31330 //            item.el.setWidth(width);
31331 //
31332 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31333 //
31334 //            item.el.setHeight(height);
31335 //            
31336 //            if(c == 0){
31337 //                item.el.setXY([x, y], isInstant ? false : true);
31338 //            } else {
31339 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31340 //            }
31341 //            
31342 //            y = y + height + this.alternativePadWidth;
31343 //            
31344 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31345 //            
31346 //        }, this);
31347 //        
31348 //        this.el.setHeight(maxHeight);
31349 //        
31350 //    },
31351     
31352     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31353     {
31354         var pos = this.el.getBox(true);
31355         
31356         var minX = pos.x;
31357         var minY = pos.y;
31358         
31359         var maxX = pos.right;
31360         
31361         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31362         
31363         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31364         
31365         Roo.each(queue, function(box, k){
31366             
31367             Roo.each(box, function(b, kk){
31368                 
31369                 b.el.position('absolute');
31370                 
31371                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31372                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31373                 
31374                 if(b.size == 'md-left' || b.size == 'md-right'){
31375                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31376                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31377                 }
31378                 
31379                 b.el.setWidth(width);
31380                 b.el.setHeight(height);
31381                 
31382             }, this);
31383             
31384             if(!box.length){
31385                 return;
31386             }
31387             
31388             var positions = [];
31389             
31390             switch (box.length){
31391                 case 1 :
31392                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31393                     break;
31394                 case 2 :
31395                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31396                     break;
31397                 case 3 :
31398                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31399                     break;
31400                 case 4 :
31401                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31402                     break;
31403                 default :
31404                     break;
31405             }
31406             
31407             Roo.each(box, function(b,kk){
31408                 
31409                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31410                 
31411                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31412                 
31413             }, this);
31414             
31415         }, this);
31416         
31417     },
31418     
31419     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31420     {
31421         Roo.each(eItems, function(b,k){
31422             
31423             b.size = (k == 0) ? 'sm' : 'xs';
31424             b.x = (k == 0) ? 2 : 1;
31425             b.y = (k == 0) ? 2 : 1;
31426             
31427             b.el.position('absolute');
31428             
31429             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31430                 
31431             b.el.setWidth(width);
31432             
31433             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31434             
31435             b.el.setHeight(height);
31436             
31437         }, this);
31438
31439         var positions = [];
31440         
31441         positions.push({
31442             x : maxX - this.unitWidth * 2 - this.gutter,
31443             y : minY
31444         });
31445         
31446         positions.push({
31447             x : maxX - this.unitWidth,
31448             y : minY + (this.unitWidth + this.gutter) * 2
31449         });
31450         
31451         positions.push({
31452             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31453             y : minY
31454         });
31455         
31456         Roo.each(eItems, function(b,k){
31457             
31458             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31459
31460         }, this);
31461         
31462     },
31463     
31464     getVerticalOneBoxColPositions : function(x, y, box)
31465     {
31466         var pos = [];
31467         
31468         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31469         
31470         if(box[0].size == 'md-left'){
31471             rand = 0;
31472         }
31473         
31474         if(box[0].size == 'md-right'){
31475             rand = 1;
31476         }
31477         
31478         pos.push({
31479             x : x + (this.unitWidth + this.gutter) * rand,
31480             y : y
31481         });
31482         
31483         return pos;
31484     },
31485     
31486     getVerticalTwoBoxColPositions : function(x, y, box)
31487     {
31488         var pos = [];
31489         
31490         if(box[0].size == 'xs'){
31491             
31492             pos.push({
31493                 x : x,
31494                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31495             });
31496
31497             pos.push({
31498                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31499                 y : y
31500             });
31501             
31502             return pos;
31503             
31504         }
31505         
31506         pos.push({
31507             x : x,
31508             y : y
31509         });
31510
31511         pos.push({
31512             x : x + (this.unitWidth + this.gutter) * 2,
31513             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31514         });
31515         
31516         return pos;
31517         
31518     },
31519     
31520     getVerticalThreeBoxColPositions : function(x, y, box)
31521     {
31522         var pos = [];
31523         
31524         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31525             
31526             pos.push({
31527                 x : x,
31528                 y : y
31529             });
31530
31531             pos.push({
31532                 x : x + (this.unitWidth + this.gutter) * 1,
31533                 y : y
31534             });
31535             
31536             pos.push({
31537                 x : x + (this.unitWidth + this.gutter) * 2,
31538                 y : y
31539             });
31540             
31541             return pos;
31542             
31543         }
31544         
31545         if(box[0].size == 'xs' && box[1].size == 'xs'){
31546             
31547             pos.push({
31548                 x : x,
31549                 y : y
31550             });
31551
31552             pos.push({
31553                 x : x,
31554                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31555             });
31556             
31557             pos.push({
31558                 x : x + (this.unitWidth + this.gutter) * 1,
31559                 y : y
31560             });
31561             
31562             return pos;
31563             
31564         }
31565         
31566         pos.push({
31567             x : x,
31568             y : y
31569         });
31570
31571         pos.push({
31572             x : x + (this.unitWidth + this.gutter) * 2,
31573             y : y
31574         });
31575
31576         pos.push({
31577             x : x + (this.unitWidth + this.gutter) * 2,
31578             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31579         });
31580             
31581         return pos;
31582         
31583     },
31584     
31585     getVerticalFourBoxColPositions : function(x, y, box)
31586     {
31587         var pos = [];
31588         
31589         if(box[0].size == 'xs'){
31590             
31591             pos.push({
31592                 x : x,
31593                 y : y
31594             });
31595
31596             pos.push({
31597                 x : x,
31598                 y : y + (this.unitHeight + this.gutter) * 1
31599             });
31600             
31601             pos.push({
31602                 x : x,
31603                 y : y + (this.unitHeight + this.gutter) * 2
31604             });
31605             
31606             pos.push({
31607                 x : x + (this.unitWidth + this.gutter) * 1,
31608                 y : y
31609             });
31610             
31611             return pos;
31612             
31613         }
31614         
31615         pos.push({
31616             x : x,
31617             y : y
31618         });
31619
31620         pos.push({
31621             x : x + (this.unitWidth + this.gutter) * 2,
31622             y : y
31623         });
31624
31625         pos.push({
31626             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31627             y : y + (this.unitHeight + this.gutter) * 1
31628         });
31629
31630         pos.push({
31631             x : x + (this.unitWidth + this.gutter) * 2,
31632             y : y + (this.unitWidth + this.gutter) * 2
31633         });
31634
31635         return pos;
31636         
31637     },
31638     
31639     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31640     {
31641         var pos = [];
31642         
31643         if(box[0].size == 'md-left'){
31644             pos.push({
31645                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31646                 y : minY
31647             });
31648             
31649             return pos;
31650         }
31651         
31652         if(box[0].size == 'md-right'){
31653             pos.push({
31654                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31655                 y : minY + (this.unitWidth + this.gutter) * 1
31656             });
31657             
31658             return pos;
31659         }
31660         
31661         var rand = Math.floor(Math.random() * (4 - box[0].y));
31662         
31663         pos.push({
31664             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31665             y : minY + (this.unitWidth + this.gutter) * rand
31666         });
31667         
31668         return pos;
31669         
31670     },
31671     
31672     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31673     {
31674         var pos = [];
31675         
31676         if(box[0].size == 'xs'){
31677             
31678             pos.push({
31679                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31680                 y : minY
31681             });
31682
31683             pos.push({
31684                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31685                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31686             });
31687             
31688             return pos;
31689             
31690         }
31691         
31692         pos.push({
31693             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31694             y : minY
31695         });
31696
31697         pos.push({
31698             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31699             y : minY + (this.unitWidth + this.gutter) * 2
31700         });
31701         
31702         return pos;
31703         
31704     },
31705     
31706     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31707     {
31708         var pos = [];
31709         
31710         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31711             
31712             pos.push({
31713                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31714                 y : minY
31715             });
31716
31717             pos.push({
31718                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31719                 y : minY + (this.unitWidth + this.gutter) * 1
31720             });
31721             
31722             pos.push({
31723                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31724                 y : minY + (this.unitWidth + this.gutter) * 2
31725             });
31726             
31727             return pos;
31728             
31729         }
31730         
31731         if(box[0].size == 'xs' && box[1].size == 'xs'){
31732             
31733             pos.push({
31734                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31735                 y : minY
31736             });
31737
31738             pos.push({
31739                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31740                 y : minY
31741             });
31742             
31743             pos.push({
31744                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31745                 y : minY + (this.unitWidth + this.gutter) * 1
31746             });
31747             
31748             return pos;
31749             
31750         }
31751         
31752         pos.push({
31753             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31754             y : minY
31755         });
31756
31757         pos.push({
31758             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31759             y : minY + (this.unitWidth + this.gutter) * 2
31760         });
31761
31762         pos.push({
31763             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31764             y : minY + (this.unitWidth + this.gutter) * 2
31765         });
31766             
31767         return pos;
31768         
31769     },
31770     
31771     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31772     {
31773         var pos = [];
31774         
31775         if(box[0].size == 'xs'){
31776             
31777             pos.push({
31778                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31779                 y : minY
31780             });
31781
31782             pos.push({
31783                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31784                 y : minY
31785             });
31786             
31787             pos.push({
31788                 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),
31789                 y : minY
31790             });
31791             
31792             pos.push({
31793                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31794                 y : minY + (this.unitWidth + this.gutter) * 1
31795             });
31796             
31797             return pos;
31798             
31799         }
31800         
31801         pos.push({
31802             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31803             y : minY
31804         });
31805         
31806         pos.push({
31807             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31808             y : minY + (this.unitWidth + this.gutter) * 2
31809         });
31810         
31811         pos.push({
31812             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31813             y : minY + (this.unitWidth + this.gutter) * 2
31814         });
31815         
31816         pos.push({
31817             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),
31818             y : minY + (this.unitWidth + this.gutter) * 2
31819         });
31820
31821         return pos;
31822         
31823     },
31824     
31825     /**
31826     * remove a Masonry Brick
31827     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31828     */
31829     removeBrick : function(brick_id)
31830     {
31831         if (!brick_id) {
31832             return;
31833         }
31834         
31835         for (var i = 0; i<this.bricks.length; i++) {
31836             if (this.bricks[i].id == brick_id) {
31837                 this.bricks.splice(i,1);
31838                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31839                 this.initial();
31840             }
31841         }
31842     },
31843     
31844     /**
31845     * adds a Masonry Brick
31846     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31847     */
31848     addBrick : function(cfg)
31849     {
31850         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31851         //this.register(cn);
31852         cn.parentId = this.id;
31853         cn.onRender(this.el, null);
31854         return cn;
31855     },
31856     
31857     /**
31858     * register a Masonry Brick
31859     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31860     */
31861     
31862     register : function(brick)
31863     {
31864         this.bricks.push(brick);
31865         brick.masonryId = this.id;
31866     },
31867     
31868     /**
31869     * clear all the Masonry Brick
31870     */
31871     clearAll : function()
31872     {
31873         this.bricks = [];
31874         //this.getChildContainer().dom.innerHTML = "";
31875         this.el.dom.innerHTML = '';
31876     },
31877     
31878     getSelected : function()
31879     {
31880         if (!this.selectedBrick) {
31881             return false;
31882         }
31883         
31884         return this.selectedBrick;
31885     }
31886 });
31887
31888 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31889     
31890     groups: {},
31891      /**
31892     * register a Masonry Layout
31893     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31894     */
31895     
31896     register : function(layout)
31897     {
31898         this.groups[layout.id] = layout;
31899     },
31900     /**
31901     * fetch a  Masonry Layout based on the masonry layout ID
31902     * @param {string} the masonry layout to add
31903     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31904     */
31905     
31906     get: function(layout_id) {
31907         if (typeof(this.groups[layout_id]) == 'undefined') {
31908             return false;
31909         }
31910         return this.groups[layout_id] ;
31911     }
31912     
31913     
31914     
31915 });
31916
31917  
31918
31919  /**
31920  *
31921  * This is based on 
31922  * http://masonry.desandro.com
31923  *
31924  * The idea is to render all the bricks based on vertical width...
31925  *
31926  * The original code extends 'outlayer' - we might need to use that....
31927  * 
31928  */
31929
31930
31931 /**
31932  * @class Roo.bootstrap.LayoutMasonryAuto
31933  * @extends Roo.bootstrap.Component
31934  * Bootstrap Layout Masonry class
31935  * 
31936  * @constructor
31937  * Create a new Element
31938  * @param {Object} config The config object
31939  */
31940
31941 Roo.bootstrap.LayoutMasonryAuto = function(config){
31942     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31943 };
31944
31945 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31946     
31947       /**
31948      * @cfg {Boolean} isFitWidth  - resize the width..
31949      */   
31950     isFitWidth : false,  // options..
31951     /**
31952      * @cfg {Boolean} isOriginLeft = left align?
31953      */   
31954     isOriginLeft : true,
31955     /**
31956      * @cfg {Boolean} isOriginTop = top align?
31957      */   
31958     isOriginTop : false,
31959     /**
31960      * @cfg {Boolean} isLayoutInstant = no animation?
31961      */   
31962     isLayoutInstant : false, // needed?
31963     /**
31964      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31965      */   
31966     isResizingContainer : true,
31967     /**
31968      * @cfg {Number} columnWidth  width of the columns 
31969      */   
31970     
31971     columnWidth : 0,
31972     
31973     /**
31974      * @cfg {Number} maxCols maximum number of columns
31975      */   
31976     
31977     maxCols: 0,
31978     /**
31979      * @cfg {Number} padHeight padding below box..
31980      */   
31981     
31982     padHeight : 10, 
31983     
31984     /**
31985      * @cfg {Boolean} isAutoInitial defalut true
31986      */   
31987     
31988     isAutoInitial : true, 
31989     
31990     // private?
31991     gutter : 0,
31992     
31993     containerWidth: 0,
31994     initialColumnWidth : 0,
31995     currentSize : null,
31996     
31997     colYs : null, // array.
31998     maxY : 0,
31999     padWidth: 10,
32000     
32001     
32002     tag: 'div',
32003     cls: '',
32004     bricks: null, //CompositeElement
32005     cols : 0, // array?
32006     // element : null, // wrapped now this.el
32007     _isLayoutInited : null, 
32008     
32009     
32010     getAutoCreate : function(){
32011         
32012         var cfg = {
32013             tag: this.tag,
32014             cls: 'blog-masonary-wrapper ' + this.cls,
32015             cn : {
32016                 cls : 'mas-boxes masonary'
32017             }
32018         };
32019         
32020         return cfg;
32021     },
32022     
32023     getChildContainer: function( )
32024     {
32025         if (this.boxesEl) {
32026             return this.boxesEl;
32027         }
32028         
32029         this.boxesEl = this.el.select('.mas-boxes').first();
32030         
32031         return this.boxesEl;
32032     },
32033     
32034     
32035     initEvents : function()
32036     {
32037         var _this = this;
32038         
32039         if(this.isAutoInitial){
32040             Roo.log('hook children rendered');
32041             this.on('childrenrendered', function() {
32042                 Roo.log('children rendered');
32043                 _this.initial();
32044             } ,this);
32045         }
32046         
32047     },
32048     
32049     initial : function()
32050     {
32051         this.reloadItems();
32052
32053         this.currentSize = this.el.getBox(true);
32054
32055         /// was window resize... - let's see if this works..
32056         Roo.EventManager.onWindowResize(this.resize, this); 
32057
32058         if(!this.isAutoInitial){
32059             this.layout();
32060             return;
32061         }
32062         
32063         this.layout.defer(500,this);
32064     },
32065     
32066     reloadItems: function()
32067     {
32068         this.bricks = this.el.select('.masonry-brick', true);
32069         
32070         this.bricks.each(function(b) {
32071             //Roo.log(b.getSize());
32072             if (!b.attr('originalwidth')) {
32073                 b.attr('originalwidth',  b.getSize().width);
32074             }
32075             
32076         });
32077         
32078         Roo.log(this.bricks.elements.length);
32079     },
32080     
32081     resize : function()
32082     {
32083         Roo.log('resize');
32084         var cs = this.el.getBox(true);
32085         
32086         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32087             Roo.log("no change in with or X");
32088             return;
32089         }
32090         this.currentSize = cs;
32091         this.layout();
32092     },
32093     
32094     layout : function()
32095     {
32096          Roo.log('layout');
32097         this._resetLayout();
32098         //this._manageStamps();
32099       
32100         // don't animate first layout
32101         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32102         this.layoutItems( isInstant );
32103       
32104         // flag for initalized
32105         this._isLayoutInited = true;
32106     },
32107     
32108     layoutItems : function( isInstant )
32109     {
32110         //var items = this._getItemsForLayout( this.items );
32111         // original code supports filtering layout items.. we just ignore it..
32112         
32113         this._layoutItems( this.bricks , isInstant );
32114       
32115         this._postLayout();
32116     },
32117     _layoutItems : function ( items , isInstant)
32118     {
32119        //this.fireEvent( 'layout', this, items );
32120     
32121
32122         if ( !items || !items.elements.length ) {
32123           // no items, emit event with empty array
32124             return;
32125         }
32126
32127         var queue = [];
32128         items.each(function(item) {
32129             Roo.log("layout item");
32130             Roo.log(item);
32131             // get x/y object from method
32132             var position = this._getItemLayoutPosition( item );
32133             // enqueue
32134             position.item = item;
32135             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32136             queue.push( position );
32137         }, this);
32138       
32139         this._processLayoutQueue( queue );
32140     },
32141     /** Sets position of item in DOM
32142     * @param {Element} item
32143     * @param {Number} x - horizontal position
32144     * @param {Number} y - vertical position
32145     * @param {Boolean} isInstant - disables transitions
32146     */
32147     _processLayoutQueue : function( queue )
32148     {
32149         for ( var i=0, len = queue.length; i < len; i++ ) {
32150             var obj = queue[i];
32151             obj.item.position('absolute');
32152             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32153         }
32154     },
32155       
32156     
32157     /**
32158     * Any logic you want to do after each layout,
32159     * i.e. size the container
32160     */
32161     _postLayout : function()
32162     {
32163         this.resizeContainer();
32164     },
32165     
32166     resizeContainer : function()
32167     {
32168         if ( !this.isResizingContainer ) {
32169             return;
32170         }
32171         var size = this._getContainerSize();
32172         if ( size ) {
32173             this.el.setSize(size.width,size.height);
32174             this.boxesEl.setSize(size.width,size.height);
32175         }
32176     },
32177     
32178     
32179     
32180     _resetLayout : function()
32181     {
32182         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32183         this.colWidth = this.el.getWidth();
32184         //this.gutter = this.el.getWidth(); 
32185         
32186         this.measureColumns();
32187
32188         // reset column Y
32189         var i = this.cols;
32190         this.colYs = [];
32191         while (i--) {
32192             this.colYs.push( 0 );
32193         }
32194     
32195         this.maxY = 0;
32196     },
32197
32198     measureColumns : function()
32199     {
32200         this.getContainerWidth();
32201       // if columnWidth is 0, default to outerWidth of first item
32202         if ( !this.columnWidth ) {
32203             var firstItem = this.bricks.first();
32204             Roo.log(firstItem);
32205             this.columnWidth  = this.containerWidth;
32206             if (firstItem && firstItem.attr('originalwidth') ) {
32207                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32208             }
32209             // columnWidth fall back to item of first element
32210             Roo.log("set column width?");
32211                         this.initialColumnWidth = this.columnWidth  ;
32212
32213             // if first elem has no width, default to size of container
32214             
32215         }
32216         
32217         
32218         if (this.initialColumnWidth) {
32219             this.columnWidth = this.initialColumnWidth;
32220         }
32221         
32222         
32223             
32224         // column width is fixed at the top - however if container width get's smaller we should
32225         // reduce it...
32226         
32227         // this bit calcs how man columns..
32228             
32229         var columnWidth = this.columnWidth += this.gutter;
32230       
32231         // calculate columns
32232         var containerWidth = this.containerWidth + this.gutter;
32233         
32234         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32235         // fix rounding errors, typically with gutters
32236         var excess = columnWidth - containerWidth % columnWidth;
32237         
32238         
32239         // if overshoot is less than a pixel, round up, otherwise floor it
32240         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32241         cols = Math[ mathMethod ]( cols );
32242         this.cols = Math.max( cols, 1 );
32243         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32244         
32245          // padding positioning..
32246         var totalColWidth = this.cols * this.columnWidth;
32247         var padavail = this.containerWidth - totalColWidth;
32248         // so for 2 columns - we need 3 'pads'
32249         
32250         var padNeeded = (1+this.cols) * this.padWidth;
32251         
32252         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32253         
32254         this.columnWidth += padExtra
32255         //this.padWidth = Math.floor(padavail /  ( this.cols));
32256         
32257         // adjust colum width so that padding is fixed??
32258         
32259         // we have 3 columns ... total = width * 3
32260         // we have X left over... that should be used by 
32261         
32262         //if (this.expandC) {
32263             
32264         //}
32265         
32266         
32267         
32268     },
32269     
32270     getContainerWidth : function()
32271     {
32272        /* // container is parent if fit width
32273         var container = this.isFitWidth ? this.element.parentNode : this.element;
32274         // check that this.size and size are there
32275         // IE8 triggers resize on body size change, so they might not be
32276         
32277         var size = getSize( container );  //FIXME
32278         this.containerWidth = size && size.innerWidth; //FIXME
32279         */
32280          
32281         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32282         
32283     },
32284     
32285     _getItemLayoutPosition : function( item )  // what is item?
32286     {
32287         // we resize the item to our columnWidth..
32288       
32289         item.setWidth(this.columnWidth);
32290         item.autoBoxAdjust  = false;
32291         
32292         var sz = item.getSize();
32293  
32294         // how many columns does this brick span
32295         var remainder = this.containerWidth % this.columnWidth;
32296         
32297         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32298         // round if off by 1 pixel, otherwise use ceil
32299         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32300         colSpan = Math.min( colSpan, this.cols );
32301         
32302         // normally this should be '1' as we dont' currently allow multi width columns..
32303         
32304         var colGroup = this._getColGroup( colSpan );
32305         // get the minimum Y value from the columns
32306         var minimumY = Math.min.apply( Math, colGroup );
32307         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32308         
32309         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32310          
32311         // position the brick
32312         var position = {
32313             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32314             y: this.currentSize.y + minimumY + this.padHeight
32315         };
32316         
32317         Roo.log(position);
32318         // apply setHeight to necessary columns
32319         var setHeight = minimumY + sz.height + this.padHeight;
32320         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32321         
32322         var setSpan = this.cols + 1 - colGroup.length;
32323         for ( var i = 0; i < setSpan; i++ ) {
32324           this.colYs[ shortColIndex + i ] = setHeight ;
32325         }
32326       
32327         return position;
32328     },
32329     
32330     /**
32331      * @param {Number} colSpan - number of columns the element spans
32332      * @returns {Array} colGroup
32333      */
32334     _getColGroup : function( colSpan )
32335     {
32336         if ( colSpan < 2 ) {
32337           // if brick spans only one column, use all the column Ys
32338           return this.colYs;
32339         }
32340       
32341         var colGroup = [];
32342         // how many different places could this brick fit horizontally
32343         var groupCount = this.cols + 1 - colSpan;
32344         // for each group potential horizontal position
32345         for ( var i = 0; i < groupCount; i++ ) {
32346           // make an array of colY values for that one group
32347           var groupColYs = this.colYs.slice( i, i + colSpan );
32348           // and get the max value of the array
32349           colGroup[i] = Math.max.apply( Math, groupColYs );
32350         }
32351         return colGroup;
32352     },
32353     /*
32354     _manageStamp : function( stamp )
32355     {
32356         var stampSize =  stamp.getSize();
32357         var offset = stamp.getBox();
32358         // get the columns that this stamp affects
32359         var firstX = this.isOriginLeft ? offset.x : offset.right;
32360         var lastX = firstX + stampSize.width;
32361         var firstCol = Math.floor( firstX / this.columnWidth );
32362         firstCol = Math.max( 0, firstCol );
32363         
32364         var lastCol = Math.floor( lastX / this.columnWidth );
32365         // lastCol should not go over if multiple of columnWidth #425
32366         lastCol -= lastX % this.columnWidth ? 0 : 1;
32367         lastCol = Math.min( this.cols - 1, lastCol );
32368         
32369         // set colYs to bottom of the stamp
32370         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32371             stampSize.height;
32372             
32373         for ( var i = firstCol; i <= lastCol; i++ ) {
32374           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32375         }
32376     },
32377     */
32378     
32379     _getContainerSize : function()
32380     {
32381         this.maxY = Math.max.apply( Math, this.colYs );
32382         var size = {
32383             height: this.maxY
32384         };
32385       
32386         if ( this.isFitWidth ) {
32387             size.width = this._getContainerFitWidth();
32388         }
32389       
32390         return size;
32391     },
32392     
32393     _getContainerFitWidth : function()
32394     {
32395         var unusedCols = 0;
32396         // count unused columns
32397         var i = this.cols;
32398         while ( --i ) {
32399           if ( this.colYs[i] !== 0 ) {
32400             break;
32401           }
32402           unusedCols++;
32403         }
32404         // fit container to columns that have been used
32405         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32406     },
32407     
32408     needsResizeLayout : function()
32409     {
32410         var previousWidth = this.containerWidth;
32411         this.getContainerWidth();
32412         return previousWidth !== this.containerWidth;
32413     }
32414  
32415 });
32416
32417  
32418
32419  /*
32420  * - LGPL
32421  *
32422  * element
32423  * 
32424  */
32425
32426 /**
32427  * @class Roo.bootstrap.MasonryBrick
32428  * @extends Roo.bootstrap.Component
32429  * Bootstrap MasonryBrick class
32430  * 
32431  * @constructor
32432  * Create a new MasonryBrick
32433  * @param {Object} config The config object
32434  */
32435
32436 Roo.bootstrap.MasonryBrick = function(config){
32437     
32438     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32439     
32440     Roo.bootstrap.MasonryBrick.register(this);
32441     
32442     this.addEvents({
32443         // raw events
32444         /**
32445          * @event click
32446          * When a MasonryBrick is clcik
32447          * @param {Roo.bootstrap.MasonryBrick} this
32448          * @param {Roo.EventObject} e
32449          */
32450         "click" : true
32451     });
32452 };
32453
32454 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32455     
32456     /**
32457      * @cfg {String} title
32458      */   
32459     title : '',
32460     /**
32461      * @cfg {String} html
32462      */   
32463     html : '',
32464     /**
32465      * @cfg {String} bgimage
32466      */   
32467     bgimage : '',
32468     /**
32469      * @cfg {String} videourl
32470      */   
32471     videourl : '',
32472     /**
32473      * @cfg {String} cls
32474      */   
32475     cls : '',
32476     /**
32477      * @cfg {String} href
32478      */   
32479     href : '',
32480     /**
32481      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32482      */   
32483     size : 'xs',
32484     
32485     /**
32486      * @cfg {String} placetitle (center|bottom)
32487      */   
32488     placetitle : '',
32489     
32490     /**
32491      * @cfg {Boolean} isFitContainer defalut true
32492      */   
32493     isFitContainer : true, 
32494     
32495     /**
32496      * @cfg {Boolean} preventDefault defalut false
32497      */   
32498     preventDefault : false, 
32499     
32500     /**
32501      * @cfg {Boolean} inverse defalut false
32502      */   
32503     maskInverse : false, 
32504     
32505     getAutoCreate : function()
32506     {
32507         if(!this.isFitContainer){
32508             return this.getSplitAutoCreate();
32509         }
32510         
32511         var cls = 'masonry-brick masonry-brick-full';
32512         
32513         if(this.href.length){
32514             cls += ' masonry-brick-link';
32515         }
32516         
32517         if(this.bgimage.length){
32518             cls += ' masonry-brick-image';
32519         }
32520         
32521         if(this.maskInverse){
32522             cls += ' mask-inverse';
32523         }
32524         
32525         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32526             cls += ' enable-mask';
32527         }
32528         
32529         if(this.size){
32530             cls += ' masonry-' + this.size + '-brick';
32531         }
32532         
32533         if(this.placetitle.length){
32534             
32535             switch (this.placetitle) {
32536                 case 'center' :
32537                     cls += ' masonry-center-title';
32538                     break;
32539                 case 'bottom' :
32540                     cls += ' masonry-bottom-title';
32541                     break;
32542                 default:
32543                     break;
32544             }
32545             
32546         } else {
32547             if(!this.html.length && !this.bgimage.length){
32548                 cls += ' masonry-center-title';
32549             }
32550
32551             if(!this.html.length && this.bgimage.length){
32552                 cls += ' masonry-bottom-title';
32553             }
32554         }
32555         
32556         if(this.cls){
32557             cls += ' ' + this.cls;
32558         }
32559         
32560         var cfg = {
32561             tag: (this.href.length) ? 'a' : 'div',
32562             cls: cls,
32563             cn: [
32564                 {
32565                     tag: 'div',
32566                     cls: 'masonry-brick-mask'
32567                 },
32568                 {
32569                     tag: 'div',
32570                     cls: 'masonry-brick-paragraph',
32571                     cn: []
32572                 }
32573             ]
32574         };
32575         
32576         if(this.href.length){
32577             cfg.href = this.href;
32578         }
32579         
32580         var cn = cfg.cn[1].cn;
32581         
32582         if(this.title.length){
32583             cn.push({
32584                 tag: 'h4',
32585                 cls: 'masonry-brick-title',
32586                 html: this.title
32587             });
32588         }
32589         
32590         if(this.html.length){
32591             cn.push({
32592                 tag: 'p',
32593                 cls: 'masonry-brick-text',
32594                 html: this.html
32595             });
32596         }
32597         
32598         if (!this.title.length && !this.html.length) {
32599             cfg.cn[1].cls += ' hide';
32600         }
32601         
32602         if(this.bgimage.length){
32603             cfg.cn.push({
32604                 tag: 'img',
32605                 cls: 'masonry-brick-image-view',
32606                 src: this.bgimage
32607             });
32608         }
32609         
32610         if(this.videourl.length){
32611             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32612             // youtube support only?
32613             cfg.cn.push({
32614                 tag: 'iframe',
32615                 cls: 'masonry-brick-image-view',
32616                 src: vurl,
32617                 frameborder : 0,
32618                 allowfullscreen : true
32619             });
32620         }
32621         
32622         return cfg;
32623         
32624     },
32625     
32626     getSplitAutoCreate : function()
32627     {
32628         var cls = 'masonry-brick masonry-brick-split';
32629         
32630         if(this.href.length){
32631             cls += ' masonry-brick-link';
32632         }
32633         
32634         if(this.bgimage.length){
32635             cls += ' masonry-brick-image';
32636         }
32637         
32638         if(this.size){
32639             cls += ' masonry-' + this.size + '-brick';
32640         }
32641         
32642         switch (this.placetitle) {
32643             case 'center' :
32644                 cls += ' masonry-center-title';
32645                 break;
32646             case 'bottom' :
32647                 cls += ' masonry-bottom-title';
32648                 break;
32649             default:
32650                 if(!this.bgimage.length){
32651                     cls += ' masonry-center-title';
32652                 }
32653
32654                 if(this.bgimage.length){
32655                     cls += ' masonry-bottom-title';
32656                 }
32657                 break;
32658         }
32659         
32660         if(this.cls){
32661             cls += ' ' + this.cls;
32662         }
32663         
32664         var cfg = {
32665             tag: (this.href.length) ? 'a' : 'div',
32666             cls: cls,
32667             cn: [
32668                 {
32669                     tag: 'div',
32670                     cls: 'masonry-brick-split-head',
32671                     cn: [
32672                         {
32673                             tag: 'div',
32674                             cls: 'masonry-brick-paragraph',
32675                             cn: []
32676                         }
32677                     ]
32678                 },
32679                 {
32680                     tag: 'div',
32681                     cls: 'masonry-brick-split-body',
32682                     cn: []
32683                 }
32684             ]
32685         };
32686         
32687         if(this.href.length){
32688             cfg.href = this.href;
32689         }
32690         
32691         if(this.title.length){
32692             cfg.cn[0].cn[0].cn.push({
32693                 tag: 'h4',
32694                 cls: 'masonry-brick-title',
32695                 html: this.title
32696             });
32697         }
32698         
32699         if(this.html.length){
32700             cfg.cn[1].cn.push({
32701                 tag: 'p',
32702                 cls: 'masonry-brick-text',
32703                 html: this.html
32704             });
32705         }
32706
32707         if(this.bgimage.length){
32708             cfg.cn[0].cn.push({
32709                 tag: 'img',
32710                 cls: 'masonry-brick-image-view',
32711                 src: this.bgimage
32712             });
32713         }
32714         
32715         if(this.videourl.length){
32716             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32717             // youtube support only?
32718             cfg.cn[0].cn.cn.push({
32719                 tag: 'iframe',
32720                 cls: 'masonry-brick-image-view',
32721                 src: vurl,
32722                 frameborder : 0,
32723                 allowfullscreen : true
32724             });
32725         }
32726         
32727         return cfg;
32728     },
32729     
32730     initEvents: function() 
32731     {
32732         switch (this.size) {
32733             case 'xs' :
32734                 this.x = 1;
32735                 this.y = 1;
32736                 break;
32737             case 'sm' :
32738                 this.x = 2;
32739                 this.y = 2;
32740                 break;
32741             case 'md' :
32742             case 'md-left' :
32743             case 'md-right' :
32744                 this.x = 3;
32745                 this.y = 3;
32746                 break;
32747             case 'tall' :
32748                 this.x = 2;
32749                 this.y = 3;
32750                 break;
32751             case 'wide' :
32752                 this.x = 3;
32753                 this.y = 2;
32754                 break;
32755             case 'wide-thin' :
32756                 this.x = 3;
32757                 this.y = 1;
32758                 break;
32759                         
32760             default :
32761                 break;
32762         }
32763         
32764         if(Roo.isTouch){
32765             this.el.on('touchstart', this.onTouchStart, this);
32766             this.el.on('touchmove', this.onTouchMove, this);
32767             this.el.on('touchend', this.onTouchEnd, this);
32768             this.el.on('contextmenu', this.onContextMenu, this);
32769         } else {
32770             this.el.on('mouseenter'  ,this.enter, this);
32771             this.el.on('mouseleave', this.leave, this);
32772             this.el.on('click', this.onClick, this);
32773         }
32774         
32775         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32776             this.parent().bricks.push(this);   
32777         }
32778         
32779     },
32780     
32781     onClick: function(e, el)
32782     {
32783         var time = this.endTimer - this.startTimer;
32784         // Roo.log(e.preventDefault());
32785         if(Roo.isTouch){
32786             if(time > 1000){
32787                 e.preventDefault();
32788                 return;
32789             }
32790         }
32791         
32792         if(!this.preventDefault){
32793             return;
32794         }
32795         
32796         e.preventDefault();
32797         
32798         if (this.activeClass != '') {
32799             this.selectBrick();
32800         }
32801         
32802         this.fireEvent('click', this, e);
32803     },
32804     
32805     enter: function(e, el)
32806     {
32807         e.preventDefault();
32808         
32809         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32810             return;
32811         }
32812         
32813         if(this.bgimage.length && this.html.length){
32814             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32815         }
32816     },
32817     
32818     leave: function(e, el)
32819     {
32820         e.preventDefault();
32821         
32822         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32823             return;
32824         }
32825         
32826         if(this.bgimage.length && this.html.length){
32827             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32828         }
32829     },
32830     
32831     onTouchStart: function(e, el)
32832     {
32833 //        e.preventDefault();
32834         
32835         this.touchmoved = false;
32836         
32837         if(!this.isFitContainer){
32838             return;
32839         }
32840         
32841         if(!this.bgimage.length || !this.html.length){
32842             return;
32843         }
32844         
32845         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32846         
32847         this.timer = new Date().getTime();
32848         
32849     },
32850     
32851     onTouchMove: function(e, el)
32852     {
32853         this.touchmoved = true;
32854     },
32855     
32856     onContextMenu : function(e,el)
32857     {
32858         e.preventDefault();
32859         e.stopPropagation();
32860         return false;
32861     },
32862     
32863     onTouchEnd: function(e, el)
32864     {
32865 //        e.preventDefault();
32866         
32867         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32868         
32869             this.leave(e,el);
32870             
32871             return;
32872         }
32873         
32874         if(!this.bgimage.length || !this.html.length){
32875             
32876             if(this.href.length){
32877                 window.location.href = this.href;
32878             }
32879             
32880             return;
32881         }
32882         
32883         if(!this.isFitContainer){
32884             return;
32885         }
32886         
32887         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32888         
32889         window.location.href = this.href;
32890     },
32891     
32892     //selection on single brick only
32893     selectBrick : function() {
32894         
32895         if (!this.parentId) {
32896             return;
32897         }
32898         
32899         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32900         var index = m.selectedBrick.indexOf(this.id);
32901         
32902         if ( index > -1) {
32903             m.selectedBrick.splice(index,1);
32904             this.el.removeClass(this.activeClass);
32905             return;
32906         }
32907         
32908         for(var i = 0; i < m.selectedBrick.length; i++) {
32909             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32910             b.el.removeClass(b.activeClass);
32911         }
32912         
32913         m.selectedBrick = [];
32914         
32915         m.selectedBrick.push(this.id);
32916         this.el.addClass(this.activeClass);
32917         return;
32918     },
32919     
32920     isSelected : function(){
32921         return this.el.hasClass(this.activeClass);
32922         
32923     }
32924 });
32925
32926 Roo.apply(Roo.bootstrap.MasonryBrick, {
32927     
32928     //groups: {},
32929     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32930      /**
32931     * register a Masonry Brick
32932     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32933     */
32934     
32935     register : function(brick)
32936     {
32937         //this.groups[brick.id] = brick;
32938         this.groups.add(brick.id, brick);
32939     },
32940     /**
32941     * fetch a  masonry brick based on the masonry brick ID
32942     * @param {string} the masonry brick to add
32943     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32944     */
32945     
32946     get: function(brick_id) 
32947     {
32948         // if (typeof(this.groups[brick_id]) == 'undefined') {
32949         //     return false;
32950         // }
32951         // return this.groups[brick_id] ;
32952         
32953         if(this.groups.key(brick_id)) {
32954             return this.groups.key(brick_id);
32955         }
32956         
32957         return false;
32958     }
32959     
32960     
32961     
32962 });
32963
32964  /*
32965  * - LGPL
32966  *
32967  * element
32968  * 
32969  */
32970
32971 /**
32972  * @class Roo.bootstrap.Brick
32973  * @extends Roo.bootstrap.Component
32974  * Bootstrap Brick class
32975  * 
32976  * @constructor
32977  * Create a new Brick
32978  * @param {Object} config The config object
32979  */
32980
32981 Roo.bootstrap.Brick = function(config){
32982     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32983     
32984     this.addEvents({
32985         // raw events
32986         /**
32987          * @event click
32988          * When a Brick is click
32989          * @param {Roo.bootstrap.Brick} this
32990          * @param {Roo.EventObject} e
32991          */
32992         "click" : true
32993     });
32994 };
32995
32996 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32997     
32998     /**
32999      * @cfg {String} title
33000      */   
33001     title : '',
33002     /**
33003      * @cfg {String} html
33004      */   
33005     html : '',
33006     /**
33007      * @cfg {String} bgimage
33008      */   
33009     bgimage : '',
33010     /**
33011      * @cfg {String} cls
33012      */   
33013     cls : '',
33014     /**
33015      * @cfg {String} href
33016      */   
33017     href : '',
33018     /**
33019      * @cfg {String} video
33020      */   
33021     video : '',
33022     /**
33023      * @cfg {Boolean} square
33024      */   
33025     square : true,
33026     
33027     getAutoCreate : function()
33028     {
33029         var cls = 'roo-brick';
33030         
33031         if(this.href.length){
33032             cls += ' roo-brick-link';
33033         }
33034         
33035         if(this.bgimage.length){
33036             cls += ' roo-brick-image';
33037         }
33038         
33039         if(!this.html.length && !this.bgimage.length){
33040             cls += ' roo-brick-center-title';
33041         }
33042         
33043         if(!this.html.length && this.bgimage.length){
33044             cls += ' roo-brick-bottom-title';
33045         }
33046         
33047         if(this.cls){
33048             cls += ' ' + this.cls;
33049         }
33050         
33051         var cfg = {
33052             tag: (this.href.length) ? 'a' : 'div',
33053             cls: cls,
33054             cn: [
33055                 {
33056                     tag: 'div',
33057                     cls: 'roo-brick-paragraph',
33058                     cn: []
33059                 }
33060             ]
33061         };
33062         
33063         if(this.href.length){
33064             cfg.href = this.href;
33065         }
33066         
33067         var cn = cfg.cn[0].cn;
33068         
33069         if(this.title.length){
33070             cn.push({
33071                 tag: 'h4',
33072                 cls: 'roo-brick-title',
33073                 html: this.title
33074             });
33075         }
33076         
33077         if(this.html.length){
33078             cn.push({
33079                 tag: 'p',
33080                 cls: 'roo-brick-text',
33081                 html: this.html
33082             });
33083         } else {
33084             cn.cls += ' hide';
33085         }
33086         
33087         if(this.bgimage.length){
33088             cfg.cn.push({
33089                 tag: 'img',
33090                 cls: 'roo-brick-image-view',
33091                 src: this.bgimage
33092             });
33093         }
33094         
33095         return cfg;
33096     },
33097     
33098     initEvents: function() 
33099     {
33100         if(this.title.length || this.html.length){
33101             this.el.on('mouseenter'  ,this.enter, this);
33102             this.el.on('mouseleave', this.leave, this);
33103         }
33104         
33105         Roo.EventManager.onWindowResize(this.resize, this); 
33106         
33107         if(this.bgimage.length){
33108             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33109             this.imageEl.on('load', this.onImageLoad, this);
33110             return;
33111         }
33112         
33113         this.resize();
33114     },
33115     
33116     onImageLoad : function()
33117     {
33118         this.resize();
33119     },
33120     
33121     resize : function()
33122     {
33123         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33124         
33125         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33126         
33127         if(this.bgimage.length){
33128             var image = this.el.select('.roo-brick-image-view', true).first();
33129             
33130             image.setWidth(paragraph.getWidth());
33131             
33132             if(this.square){
33133                 image.setHeight(paragraph.getWidth());
33134             }
33135             
33136             this.el.setHeight(image.getHeight());
33137             paragraph.setHeight(image.getHeight());
33138             
33139         }
33140         
33141     },
33142     
33143     enter: function(e, el)
33144     {
33145         e.preventDefault();
33146         
33147         if(this.bgimage.length){
33148             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33149             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33150         }
33151     },
33152     
33153     leave: function(e, el)
33154     {
33155         e.preventDefault();
33156         
33157         if(this.bgimage.length){
33158             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33159             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33160         }
33161     }
33162     
33163 });
33164
33165  
33166
33167  /*
33168  * - LGPL
33169  *
33170  * Number field 
33171  */
33172
33173 /**
33174  * @class Roo.bootstrap.NumberField
33175  * @extends Roo.bootstrap.Input
33176  * Bootstrap NumberField class
33177  * 
33178  * 
33179  * 
33180  * 
33181  * @constructor
33182  * Create a new NumberField
33183  * @param {Object} config The config object
33184  */
33185
33186 Roo.bootstrap.NumberField = function(config){
33187     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33188 };
33189
33190 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33191     
33192     /**
33193      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33194      */
33195     allowDecimals : true,
33196     /**
33197      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33198      */
33199     decimalSeparator : ".",
33200     /**
33201      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33202      */
33203     decimalPrecision : 2,
33204     /**
33205      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33206      */
33207     allowNegative : true,
33208     
33209     /**
33210      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33211      */
33212     allowZero: true,
33213     /**
33214      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33215      */
33216     minValue : Number.NEGATIVE_INFINITY,
33217     /**
33218      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33219      */
33220     maxValue : Number.MAX_VALUE,
33221     /**
33222      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33223      */
33224     minText : "The minimum value for this field is {0}",
33225     /**
33226      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33227      */
33228     maxText : "The maximum value for this field is {0}",
33229     /**
33230      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33231      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33232      */
33233     nanText : "{0} is not a valid number",
33234     /**
33235      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33236      */
33237     thousandsDelimiter : false,
33238     /**
33239      * @cfg {String} valueAlign alignment of value
33240      */
33241     valueAlign : "left",
33242
33243     getAutoCreate : function()
33244     {
33245         var hiddenInput = {
33246             tag: 'input',
33247             type: 'hidden',
33248             id: Roo.id(),
33249             cls: 'hidden-number-input'
33250         };
33251         
33252         if (this.name) {
33253             hiddenInput.name = this.name;
33254         }
33255         
33256         this.name = '';
33257         
33258         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33259         
33260         this.name = hiddenInput.name;
33261         
33262         if(cfg.cn.length > 0) {
33263             cfg.cn.push(hiddenInput);
33264         }
33265         
33266         return cfg;
33267     },
33268
33269     // private
33270     initEvents : function()
33271     {   
33272         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33273         
33274         var allowed = "0123456789";
33275         
33276         if(this.allowDecimals){
33277             allowed += this.decimalSeparator;
33278         }
33279         
33280         if(this.allowNegative){
33281             allowed += "-";
33282         }
33283         
33284         if(this.thousandsDelimiter) {
33285             allowed += ",";
33286         }
33287         
33288         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33289         
33290         var keyPress = function(e){
33291             
33292             var k = e.getKey();
33293             
33294             var c = e.getCharCode();
33295             
33296             if(
33297                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33298                     allowed.indexOf(String.fromCharCode(c)) === -1
33299             ){
33300                 e.stopEvent();
33301                 return;
33302             }
33303             
33304             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33305                 return;
33306             }
33307             
33308             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33309                 e.stopEvent();
33310             }
33311         };
33312         
33313         this.el.on("keypress", keyPress, this);
33314     },
33315     
33316     validateValue : function(value)
33317     {
33318         
33319         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33320             return false;
33321         }
33322         
33323         var num = this.parseValue(value);
33324         
33325         if(isNaN(num)){
33326             this.markInvalid(String.format(this.nanText, value));
33327             return false;
33328         }
33329         
33330         if(num < this.minValue){
33331             this.markInvalid(String.format(this.minText, this.minValue));
33332             return false;
33333         }
33334         
33335         if(num > this.maxValue){
33336             this.markInvalid(String.format(this.maxText, this.maxValue));
33337             return false;
33338         }
33339         
33340         return true;
33341     },
33342
33343     getValue : function()
33344     {
33345         var v = this.hiddenEl().getValue();
33346         
33347         return this.fixPrecision(this.parseValue(v));
33348     },
33349
33350     parseValue : function(value)
33351     {
33352         if(this.thousandsDelimiter) {
33353             value += "";
33354             r = new RegExp(",", "g");
33355             value = value.replace(r, "");
33356         }
33357         
33358         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33359         return isNaN(value) ? '' : value;
33360     },
33361
33362     fixPrecision : function(value)
33363     {
33364         if(this.thousandsDelimiter) {
33365             value += "";
33366             r = new RegExp(",", "g");
33367             value = value.replace(r, "");
33368         }
33369         
33370         var nan = isNaN(value);
33371         
33372         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33373             return nan ? '' : value;
33374         }
33375         return parseFloat(value).toFixed(this.decimalPrecision);
33376     },
33377
33378     setValue : function(v)
33379     {
33380         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33381         
33382         this.value = v;
33383         
33384         if(this.rendered){
33385             
33386             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33387             
33388             this.inputEl().dom.value = (v == '') ? '' :
33389                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33390             
33391             if(!this.allowZero && v === '0') {
33392                 this.hiddenEl().dom.value = '';
33393                 this.inputEl().dom.value = '';
33394             }
33395             
33396             this.validate();
33397         }
33398     },
33399
33400     decimalPrecisionFcn : function(v)
33401     {
33402         return Math.floor(v);
33403     },
33404
33405     beforeBlur : function()
33406     {
33407         var v = this.parseValue(this.getRawValue());
33408         
33409         if(v || v === 0 || v === ''){
33410             this.setValue(v);
33411         }
33412     },
33413     
33414     hiddenEl : function()
33415     {
33416         return this.el.select('input.hidden-number-input',true).first();
33417     }
33418     
33419 });
33420
33421  
33422
33423 /*
33424 * Licence: LGPL
33425 */
33426
33427 /**
33428  * @class Roo.bootstrap.DocumentSlider
33429  * @extends Roo.bootstrap.Component
33430  * Bootstrap DocumentSlider class
33431  * 
33432  * @constructor
33433  * Create a new DocumentViewer
33434  * @param {Object} config The config object
33435  */
33436
33437 Roo.bootstrap.DocumentSlider = function(config){
33438     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33439     
33440     this.files = [];
33441     
33442     this.addEvents({
33443         /**
33444          * @event initial
33445          * Fire after initEvent
33446          * @param {Roo.bootstrap.DocumentSlider} this
33447          */
33448         "initial" : true,
33449         /**
33450          * @event update
33451          * Fire after update
33452          * @param {Roo.bootstrap.DocumentSlider} this
33453          */
33454         "update" : true,
33455         /**
33456          * @event click
33457          * Fire after click
33458          * @param {Roo.bootstrap.DocumentSlider} this
33459          */
33460         "click" : true
33461     });
33462 };
33463
33464 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33465     
33466     files : false,
33467     
33468     indicator : 0,
33469     
33470     getAutoCreate : function()
33471     {
33472         var cfg = {
33473             tag : 'div',
33474             cls : 'roo-document-slider',
33475             cn : [
33476                 {
33477                     tag : 'div',
33478                     cls : 'roo-document-slider-header',
33479                     cn : [
33480                         {
33481                             tag : 'div',
33482                             cls : 'roo-document-slider-header-title'
33483                         }
33484                     ]
33485                 },
33486                 {
33487                     tag : 'div',
33488                     cls : 'roo-document-slider-body',
33489                     cn : [
33490                         {
33491                             tag : 'div',
33492                             cls : 'roo-document-slider-prev',
33493                             cn : [
33494                                 {
33495                                     tag : 'i',
33496                                     cls : 'fa fa-chevron-left'
33497                                 }
33498                             ]
33499                         },
33500                         {
33501                             tag : 'div',
33502                             cls : 'roo-document-slider-thumb',
33503                             cn : [
33504                                 {
33505                                     tag : 'img',
33506                                     cls : 'roo-document-slider-image'
33507                                 }
33508                             ]
33509                         },
33510                         {
33511                             tag : 'div',
33512                             cls : 'roo-document-slider-next',
33513                             cn : [
33514                                 {
33515                                     tag : 'i',
33516                                     cls : 'fa fa-chevron-right'
33517                                 }
33518                             ]
33519                         }
33520                     ]
33521                 }
33522             ]
33523         };
33524         
33525         return cfg;
33526     },
33527     
33528     initEvents : function()
33529     {
33530         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33531         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33532         
33533         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33534         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33535         
33536         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33537         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33538         
33539         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33540         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33541         
33542         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33543         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33544         
33545         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33546         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33547         
33548         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33549         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33550         
33551         this.thumbEl.on('click', this.onClick, this);
33552         
33553         this.prevIndicator.on('click', this.prev, this);
33554         
33555         this.nextIndicator.on('click', this.next, this);
33556         
33557     },
33558     
33559     initial : function()
33560     {
33561         if(this.files.length){
33562             this.indicator = 1;
33563             this.update()
33564         }
33565         
33566         this.fireEvent('initial', this);
33567     },
33568     
33569     update : function()
33570     {
33571         this.imageEl.attr('src', this.files[this.indicator - 1]);
33572         
33573         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33574         
33575         this.prevIndicator.show();
33576         
33577         if(this.indicator == 1){
33578             this.prevIndicator.hide();
33579         }
33580         
33581         this.nextIndicator.show();
33582         
33583         if(this.indicator == this.files.length){
33584             this.nextIndicator.hide();
33585         }
33586         
33587         this.thumbEl.scrollTo('top');
33588         
33589         this.fireEvent('update', this);
33590     },
33591     
33592     onClick : function(e)
33593     {
33594         e.preventDefault();
33595         
33596         this.fireEvent('click', this);
33597     },
33598     
33599     prev : function(e)
33600     {
33601         e.preventDefault();
33602         
33603         this.indicator = Math.max(1, this.indicator - 1);
33604         
33605         this.update();
33606     },
33607     
33608     next : function(e)
33609     {
33610         e.preventDefault();
33611         
33612         this.indicator = Math.min(this.files.length, this.indicator + 1);
33613         
33614         this.update();
33615     }
33616 });
33617 /*
33618  * - LGPL
33619  *
33620  * RadioSet
33621  *
33622  *
33623  */
33624
33625 /**
33626  * @class Roo.bootstrap.RadioSet
33627  * @extends Roo.bootstrap.Input
33628  * Bootstrap RadioSet class
33629  * @cfg {String} indicatorpos (left|right) default left
33630  * @cfg {Boolean} inline (true|false) inline the element (default true)
33631  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33632  * @constructor
33633  * Create a new RadioSet
33634  * @param {Object} config The config object
33635  */
33636
33637 Roo.bootstrap.RadioSet = function(config){
33638     
33639     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33640     
33641     this.radioes = [];
33642     
33643     Roo.bootstrap.RadioSet.register(this);
33644     
33645     this.addEvents({
33646         /**
33647         * @event check
33648         * Fires when the element is checked or unchecked.
33649         * @param {Roo.bootstrap.RadioSet} this This radio
33650         * @param {Roo.bootstrap.Radio} item The checked item
33651         */
33652        check : true,
33653        /**
33654         * @event click
33655         * Fires when the element is click.
33656         * @param {Roo.bootstrap.RadioSet} this This radio set
33657         * @param {Roo.bootstrap.Radio} item The checked item
33658         * @param {Roo.EventObject} e The event object
33659         */
33660        click : true
33661     });
33662     
33663 };
33664
33665 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33666
33667     radioes : false,
33668     
33669     inline : true,
33670     
33671     weight : '',
33672     
33673     indicatorpos : 'left',
33674     
33675     getAutoCreate : function()
33676     {
33677         var label = {
33678             tag : 'label',
33679             cls : 'roo-radio-set-label',
33680             cn : [
33681                 {
33682                     tag : 'span',
33683                     html : this.fieldLabel
33684                 }
33685             ]
33686         };
33687         
33688         if(this.indicatorpos == 'left'){
33689             label.cn.unshift({
33690                 tag : 'i',
33691                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33692                 tooltip : 'This field is required'
33693             });
33694         } else {
33695             label.cn.push({
33696                 tag : 'i',
33697                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33698                 tooltip : 'This field is required'
33699             });
33700         }
33701         
33702         var items = {
33703             tag : 'div',
33704             cls : 'roo-radio-set-items'
33705         };
33706         
33707         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33708         
33709         if (align === 'left' && this.fieldLabel.length) {
33710             
33711             items = {
33712                 cls : "roo-radio-set-right", 
33713                 cn: [
33714                     items
33715                 ]
33716             };
33717             
33718             if(this.labelWidth > 12){
33719                 label.style = "width: " + this.labelWidth + 'px';
33720             }
33721             
33722             if(this.labelWidth < 13 && this.labelmd == 0){
33723                 this.labelmd = this.labelWidth;
33724             }
33725             
33726             if(this.labellg > 0){
33727                 label.cls += ' col-lg-' + this.labellg;
33728                 items.cls += ' col-lg-' + (12 - this.labellg);
33729             }
33730             
33731             if(this.labelmd > 0){
33732                 label.cls += ' col-md-' + this.labelmd;
33733                 items.cls += ' col-md-' + (12 - this.labelmd);
33734             }
33735             
33736             if(this.labelsm > 0){
33737                 label.cls += ' col-sm-' + this.labelsm;
33738                 items.cls += ' col-sm-' + (12 - this.labelsm);
33739             }
33740             
33741             if(this.labelxs > 0){
33742                 label.cls += ' col-xs-' + this.labelxs;
33743                 items.cls += ' col-xs-' + (12 - this.labelxs);
33744             }
33745         }
33746         
33747         var cfg = {
33748             tag : 'div',
33749             cls : 'roo-radio-set',
33750             cn : [
33751                 {
33752                     tag : 'input',
33753                     cls : 'roo-radio-set-input',
33754                     type : 'hidden',
33755                     name : this.name,
33756                     value : this.value ? this.value :  ''
33757                 },
33758                 label,
33759                 items
33760             ]
33761         };
33762         
33763         if(this.weight.length){
33764             cfg.cls += ' roo-radio-' + this.weight;
33765         }
33766         
33767         if(this.inline) {
33768             cfg.cls += ' roo-radio-set-inline';
33769         }
33770         
33771         var settings=this;
33772         ['xs','sm','md','lg'].map(function(size){
33773             if (settings[size]) {
33774                 cfg.cls += ' col-' + size + '-' + settings[size];
33775             }
33776         });
33777         
33778         return cfg;
33779         
33780     },
33781
33782     initEvents : function()
33783     {
33784         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33785         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33786         
33787         if(!this.fieldLabel.length){
33788             this.labelEl.hide();
33789         }
33790         
33791         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33792         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33793         
33794         this.indicator = this.indicatorEl();
33795         
33796         if(this.indicator){
33797             this.indicator.addClass('invisible');
33798         }
33799         
33800         this.originalValue = this.getValue();
33801         
33802     },
33803     
33804     inputEl: function ()
33805     {
33806         return this.el.select('.roo-radio-set-input', true).first();
33807     },
33808     
33809     getChildContainer : function()
33810     {
33811         return this.itemsEl;
33812     },
33813     
33814     register : function(item)
33815     {
33816         this.radioes.push(item);
33817         
33818     },
33819     
33820     validate : function()
33821     {   
33822         if(this.getVisibilityEl().hasClass('hidden')){
33823             return true;
33824         }
33825         
33826         var valid = false;
33827         
33828         Roo.each(this.radioes, function(i){
33829             if(!i.checked){
33830                 return;
33831             }
33832             
33833             valid = true;
33834             return false;
33835         });
33836         
33837         if(this.allowBlank) {
33838             return true;
33839         }
33840         
33841         if(this.disabled || valid){
33842             this.markValid();
33843             return true;
33844         }
33845         
33846         this.markInvalid();
33847         return false;
33848         
33849     },
33850     
33851     markValid : function()
33852     {
33853         if(this.labelEl.isVisible(true)){
33854             this.indicatorEl().removeClass('visible');
33855             this.indicatorEl().addClass('invisible');
33856         }
33857         
33858         this.el.removeClass([this.invalidClass, this.validClass]);
33859         this.el.addClass(this.validClass);
33860         
33861         this.fireEvent('valid', this);
33862     },
33863     
33864     markInvalid : function(msg)
33865     {
33866         if(this.allowBlank || this.disabled){
33867             return;
33868         }
33869         
33870         if(this.labelEl.isVisible(true)){
33871             this.indicatorEl().removeClass('invisible');
33872             this.indicatorEl().addClass('visible');
33873         }
33874         
33875         this.el.removeClass([this.invalidClass, this.validClass]);
33876         this.el.addClass(this.invalidClass);
33877         
33878         this.fireEvent('invalid', this, msg);
33879         
33880     },
33881     
33882     setValue : function(v, suppressEvent)
33883     {   
33884         if(this.value === v){
33885             return;
33886         }
33887         
33888         this.value = v;
33889         
33890         if(this.rendered){
33891             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33892         }
33893         
33894         Roo.each(this.radioes, function(i){
33895             i.checked = false;
33896             i.el.removeClass('checked');
33897         });
33898         
33899         Roo.each(this.radioes, function(i){
33900             
33901             if(i.value === v || i.value.toString() === v.toString()){
33902                 i.checked = true;
33903                 i.el.addClass('checked');
33904                 
33905                 if(suppressEvent !== true){
33906                     this.fireEvent('check', this, i);
33907                 }
33908                 
33909                 return false;
33910             }
33911             
33912         }, this);
33913         
33914         this.validate();
33915     },
33916     
33917     clearInvalid : function(){
33918         
33919         if(!this.el || this.preventMark){
33920             return;
33921         }
33922         
33923         this.el.removeClass([this.invalidClass]);
33924         
33925         this.fireEvent('valid', this);
33926     }
33927     
33928 });
33929
33930 Roo.apply(Roo.bootstrap.RadioSet, {
33931     
33932     groups: {},
33933     
33934     register : function(set)
33935     {
33936         this.groups[set.name] = set;
33937     },
33938     
33939     get: function(name) 
33940     {
33941         if (typeof(this.groups[name]) == 'undefined') {
33942             return false;
33943         }
33944         
33945         return this.groups[name] ;
33946     }
33947     
33948 });
33949 /*
33950  * Based on:
33951  * Ext JS Library 1.1.1
33952  * Copyright(c) 2006-2007, Ext JS, LLC.
33953  *
33954  * Originally Released Under LGPL - original licence link has changed is not relivant.
33955  *
33956  * Fork - LGPL
33957  * <script type="text/javascript">
33958  */
33959
33960
33961 /**
33962  * @class Roo.bootstrap.SplitBar
33963  * @extends Roo.util.Observable
33964  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33965  * <br><br>
33966  * Usage:
33967  * <pre><code>
33968 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33969                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33970 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33971 split.minSize = 100;
33972 split.maxSize = 600;
33973 split.animate = true;
33974 split.on('moved', splitterMoved);
33975 </code></pre>
33976  * @constructor
33977  * Create a new SplitBar
33978  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33979  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33980  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33981  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33982                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33983                         position of the SplitBar).
33984  */
33985 Roo.bootstrap.SplitBar = function(cfg){
33986     
33987     /** @private */
33988     
33989     //{
33990     //  dragElement : elm
33991     //  resizingElement: el,
33992         // optional..
33993     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33994     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33995         // existingProxy ???
33996     //}
33997     
33998     this.el = Roo.get(cfg.dragElement, true);
33999     this.el.dom.unselectable = "on";
34000     /** @private */
34001     this.resizingEl = Roo.get(cfg.resizingElement, true);
34002
34003     /**
34004      * @private
34005      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34006      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34007      * @type Number
34008      */
34009     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34010     
34011     /**
34012      * The minimum size of the resizing element. (Defaults to 0)
34013      * @type Number
34014      */
34015     this.minSize = 0;
34016     
34017     /**
34018      * The maximum size of the resizing element. (Defaults to 2000)
34019      * @type Number
34020      */
34021     this.maxSize = 2000;
34022     
34023     /**
34024      * Whether to animate the transition to the new size
34025      * @type Boolean
34026      */
34027     this.animate = false;
34028     
34029     /**
34030      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34031      * @type Boolean
34032      */
34033     this.useShim = false;
34034     
34035     /** @private */
34036     this.shim = null;
34037     
34038     if(!cfg.existingProxy){
34039         /** @private */
34040         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34041     }else{
34042         this.proxy = Roo.get(cfg.existingProxy).dom;
34043     }
34044     /** @private */
34045     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34046     
34047     /** @private */
34048     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34049     
34050     /** @private */
34051     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34052     
34053     /** @private */
34054     this.dragSpecs = {};
34055     
34056     /**
34057      * @private The adapter to use to positon and resize elements
34058      */
34059     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34060     this.adapter.init(this);
34061     
34062     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34063         /** @private */
34064         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34065         this.el.addClass("roo-splitbar-h");
34066     }else{
34067         /** @private */
34068         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34069         this.el.addClass("roo-splitbar-v");
34070     }
34071     
34072     this.addEvents({
34073         /**
34074          * @event resize
34075          * Fires when the splitter is moved (alias for {@link #event-moved})
34076          * @param {Roo.bootstrap.SplitBar} this
34077          * @param {Number} newSize the new width or height
34078          */
34079         "resize" : true,
34080         /**
34081          * @event moved
34082          * Fires when the splitter is moved
34083          * @param {Roo.bootstrap.SplitBar} this
34084          * @param {Number} newSize the new width or height
34085          */
34086         "moved" : true,
34087         /**
34088          * @event beforeresize
34089          * Fires before the splitter is dragged
34090          * @param {Roo.bootstrap.SplitBar} this
34091          */
34092         "beforeresize" : true,
34093
34094         "beforeapply" : true
34095     });
34096
34097     Roo.util.Observable.call(this);
34098 };
34099
34100 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34101     onStartProxyDrag : function(x, y){
34102         this.fireEvent("beforeresize", this);
34103         if(!this.overlay){
34104             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34105             o.unselectable();
34106             o.enableDisplayMode("block");
34107             // all splitbars share the same overlay
34108             Roo.bootstrap.SplitBar.prototype.overlay = o;
34109         }
34110         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34111         this.overlay.show();
34112         Roo.get(this.proxy).setDisplayed("block");
34113         var size = this.adapter.getElementSize(this);
34114         this.activeMinSize = this.getMinimumSize();;
34115         this.activeMaxSize = this.getMaximumSize();;
34116         var c1 = size - this.activeMinSize;
34117         var c2 = Math.max(this.activeMaxSize - size, 0);
34118         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34119             this.dd.resetConstraints();
34120             this.dd.setXConstraint(
34121                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34122                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34123             );
34124             this.dd.setYConstraint(0, 0);
34125         }else{
34126             this.dd.resetConstraints();
34127             this.dd.setXConstraint(0, 0);
34128             this.dd.setYConstraint(
34129                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34130                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34131             );
34132          }
34133         this.dragSpecs.startSize = size;
34134         this.dragSpecs.startPoint = [x, y];
34135         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34136     },
34137     
34138     /** 
34139      * @private Called after the drag operation by the DDProxy
34140      */
34141     onEndProxyDrag : function(e){
34142         Roo.get(this.proxy).setDisplayed(false);
34143         var endPoint = Roo.lib.Event.getXY(e);
34144         if(this.overlay){
34145             this.overlay.hide();
34146         }
34147         var newSize;
34148         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34149             newSize = this.dragSpecs.startSize + 
34150                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34151                     endPoint[0] - this.dragSpecs.startPoint[0] :
34152                     this.dragSpecs.startPoint[0] - endPoint[0]
34153                 );
34154         }else{
34155             newSize = this.dragSpecs.startSize + 
34156                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34157                     endPoint[1] - this.dragSpecs.startPoint[1] :
34158                     this.dragSpecs.startPoint[1] - endPoint[1]
34159                 );
34160         }
34161         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34162         if(newSize != this.dragSpecs.startSize){
34163             if(this.fireEvent('beforeapply', this, newSize) !== false){
34164                 this.adapter.setElementSize(this, newSize);
34165                 this.fireEvent("moved", this, newSize);
34166                 this.fireEvent("resize", this, newSize);
34167             }
34168         }
34169     },
34170     
34171     /**
34172      * Get the adapter this SplitBar uses
34173      * @return The adapter object
34174      */
34175     getAdapter : function(){
34176         return this.adapter;
34177     },
34178     
34179     /**
34180      * Set the adapter this SplitBar uses
34181      * @param {Object} adapter A SplitBar adapter object
34182      */
34183     setAdapter : function(adapter){
34184         this.adapter = adapter;
34185         this.adapter.init(this);
34186     },
34187     
34188     /**
34189      * Gets the minimum size for the resizing element
34190      * @return {Number} The minimum size
34191      */
34192     getMinimumSize : function(){
34193         return this.minSize;
34194     },
34195     
34196     /**
34197      * Sets the minimum size for the resizing element
34198      * @param {Number} minSize The minimum size
34199      */
34200     setMinimumSize : function(minSize){
34201         this.minSize = minSize;
34202     },
34203     
34204     /**
34205      * Gets the maximum size for the resizing element
34206      * @return {Number} The maximum size
34207      */
34208     getMaximumSize : function(){
34209         return this.maxSize;
34210     },
34211     
34212     /**
34213      * Sets the maximum size for the resizing element
34214      * @param {Number} maxSize The maximum size
34215      */
34216     setMaximumSize : function(maxSize){
34217         this.maxSize = maxSize;
34218     },
34219     
34220     /**
34221      * Sets the initialize size for the resizing element
34222      * @param {Number} size The initial size
34223      */
34224     setCurrentSize : function(size){
34225         var oldAnimate = this.animate;
34226         this.animate = false;
34227         this.adapter.setElementSize(this, size);
34228         this.animate = oldAnimate;
34229     },
34230     
34231     /**
34232      * Destroy this splitbar. 
34233      * @param {Boolean} removeEl True to remove the element
34234      */
34235     destroy : function(removeEl){
34236         if(this.shim){
34237             this.shim.remove();
34238         }
34239         this.dd.unreg();
34240         this.proxy.parentNode.removeChild(this.proxy);
34241         if(removeEl){
34242             this.el.remove();
34243         }
34244     }
34245 });
34246
34247 /**
34248  * @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.
34249  */
34250 Roo.bootstrap.SplitBar.createProxy = function(dir){
34251     var proxy = new Roo.Element(document.createElement("div"));
34252     proxy.unselectable();
34253     var cls = 'roo-splitbar-proxy';
34254     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34255     document.body.appendChild(proxy.dom);
34256     return proxy.dom;
34257 };
34258
34259 /** 
34260  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34261  * Default Adapter. It assumes the splitter and resizing element are not positioned
34262  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34263  */
34264 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34265 };
34266
34267 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34268     // do nothing for now
34269     init : function(s){
34270     
34271     },
34272     /**
34273      * Called before drag operations to get the current size of the resizing element. 
34274      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34275      */
34276      getElementSize : function(s){
34277         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34278             return s.resizingEl.getWidth();
34279         }else{
34280             return s.resizingEl.getHeight();
34281         }
34282     },
34283     
34284     /**
34285      * Called after drag operations to set the size of the resizing element.
34286      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34287      * @param {Number} newSize The new size to set
34288      * @param {Function} onComplete A function to be invoked when resizing is complete
34289      */
34290     setElementSize : function(s, newSize, onComplete){
34291         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34292             if(!s.animate){
34293                 s.resizingEl.setWidth(newSize);
34294                 if(onComplete){
34295                     onComplete(s, newSize);
34296                 }
34297             }else{
34298                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34299             }
34300         }else{
34301             
34302             if(!s.animate){
34303                 s.resizingEl.setHeight(newSize);
34304                 if(onComplete){
34305                     onComplete(s, newSize);
34306                 }
34307             }else{
34308                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34309             }
34310         }
34311     }
34312 };
34313
34314 /** 
34315  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34316  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34317  * Adapter that  moves the splitter element to align with the resized sizing element. 
34318  * Used with an absolute positioned SplitBar.
34319  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34320  * document.body, make sure you assign an id to the body element.
34321  */
34322 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34323     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34324     this.container = Roo.get(container);
34325 };
34326
34327 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34328     init : function(s){
34329         this.basic.init(s);
34330     },
34331     
34332     getElementSize : function(s){
34333         return this.basic.getElementSize(s);
34334     },
34335     
34336     setElementSize : function(s, newSize, onComplete){
34337         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34338     },
34339     
34340     moveSplitter : function(s){
34341         var yes = Roo.bootstrap.SplitBar;
34342         switch(s.placement){
34343             case yes.LEFT:
34344                 s.el.setX(s.resizingEl.getRight());
34345                 break;
34346             case yes.RIGHT:
34347                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34348                 break;
34349             case yes.TOP:
34350                 s.el.setY(s.resizingEl.getBottom());
34351                 break;
34352             case yes.BOTTOM:
34353                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34354                 break;
34355         }
34356     }
34357 };
34358
34359 /**
34360  * Orientation constant - Create a vertical SplitBar
34361  * @static
34362  * @type Number
34363  */
34364 Roo.bootstrap.SplitBar.VERTICAL = 1;
34365
34366 /**
34367  * Orientation constant - Create a horizontal SplitBar
34368  * @static
34369  * @type Number
34370  */
34371 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34372
34373 /**
34374  * Placement constant - The resizing element is to the left of the splitter element
34375  * @static
34376  * @type Number
34377  */
34378 Roo.bootstrap.SplitBar.LEFT = 1;
34379
34380 /**
34381  * Placement constant - The resizing element is to the right of the splitter element
34382  * @static
34383  * @type Number
34384  */
34385 Roo.bootstrap.SplitBar.RIGHT = 2;
34386
34387 /**
34388  * Placement constant - The resizing element is positioned above the splitter element
34389  * @static
34390  * @type Number
34391  */
34392 Roo.bootstrap.SplitBar.TOP = 3;
34393
34394 /**
34395  * Placement constant - The resizing element is positioned under splitter element
34396  * @static
34397  * @type Number
34398  */
34399 Roo.bootstrap.SplitBar.BOTTOM = 4;
34400 Roo.namespace("Roo.bootstrap.layout");/*
34401  * Based on:
34402  * Ext JS Library 1.1.1
34403  * Copyright(c) 2006-2007, Ext JS, LLC.
34404  *
34405  * Originally Released Under LGPL - original licence link has changed is not relivant.
34406  *
34407  * Fork - LGPL
34408  * <script type="text/javascript">
34409  */
34410
34411 /**
34412  * @class Roo.bootstrap.layout.Manager
34413  * @extends Roo.bootstrap.Component
34414  * Base class for layout managers.
34415  */
34416 Roo.bootstrap.layout.Manager = function(config)
34417 {
34418     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34419
34420
34421
34422
34423
34424     /** false to disable window resize monitoring @type Boolean */
34425     this.monitorWindowResize = true;
34426     this.regions = {};
34427     this.addEvents({
34428         /**
34429          * @event layout
34430          * Fires when a layout is performed.
34431          * @param {Roo.LayoutManager} this
34432          */
34433         "layout" : true,
34434         /**
34435          * @event regionresized
34436          * Fires when the user resizes a region.
34437          * @param {Roo.LayoutRegion} region The resized region
34438          * @param {Number} newSize The new size (width for east/west, height for north/south)
34439          */
34440         "regionresized" : true,
34441         /**
34442          * @event regioncollapsed
34443          * Fires when a region is collapsed.
34444          * @param {Roo.LayoutRegion} region The collapsed region
34445          */
34446         "regioncollapsed" : true,
34447         /**
34448          * @event regionexpanded
34449          * Fires when a region is expanded.
34450          * @param {Roo.LayoutRegion} region The expanded region
34451          */
34452         "regionexpanded" : true
34453     });
34454     this.updating = false;
34455
34456     if (config.el) {
34457         this.el = Roo.get(config.el);
34458         this.initEvents();
34459     }
34460
34461 };
34462
34463 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34464
34465
34466     regions : null,
34467
34468     monitorWindowResize : true,
34469
34470
34471     updating : false,
34472
34473
34474     onRender : function(ct, position)
34475     {
34476         if(!this.el){
34477             this.el = Roo.get(ct);
34478             this.initEvents();
34479         }
34480         //this.fireEvent('render',this);
34481     },
34482
34483
34484     initEvents: function()
34485     {
34486
34487
34488         // ie scrollbar fix
34489         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34490             document.body.scroll = "no";
34491         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34492             this.el.position('relative');
34493         }
34494         this.id = this.el.id;
34495         this.el.addClass("roo-layout-container");
34496         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34497         if(this.el.dom != document.body ) {
34498             this.el.on('resize', this.layout,this);
34499             this.el.on('show', this.layout,this);
34500         }
34501
34502     },
34503
34504     /**
34505      * Returns true if this layout is currently being updated
34506      * @return {Boolean}
34507      */
34508     isUpdating : function(){
34509         return this.updating;
34510     },
34511
34512     /**
34513      * Suspend the LayoutManager from doing auto-layouts while
34514      * making multiple add or remove calls
34515      */
34516     beginUpdate : function(){
34517         this.updating = true;
34518     },
34519
34520     /**
34521      * Restore auto-layouts and optionally disable the manager from performing a layout
34522      * @param {Boolean} noLayout true to disable a layout update
34523      */
34524     endUpdate : function(noLayout){
34525         this.updating = false;
34526         if(!noLayout){
34527             this.layout();
34528         }
34529     },
34530
34531     layout: function(){
34532         // abstract...
34533     },
34534
34535     onRegionResized : function(region, newSize){
34536         this.fireEvent("regionresized", region, newSize);
34537         this.layout();
34538     },
34539
34540     onRegionCollapsed : function(region){
34541         this.fireEvent("regioncollapsed", region);
34542     },
34543
34544     onRegionExpanded : function(region){
34545         this.fireEvent("regionexpanded", region);
34546     },
34547
34548     /**
34549      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34550      * performs box-model adjustments.
34551      * @return {Object} The size as an object {width: (the width), height: (the height)}
34552      */
34553     getViewSize : function()
34554     {
34555         var size;
34556         if(this.el.dom != document.body){
34557             size = this.el.getSize();
34558         }else{
34559             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34560         }
34561         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34562         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34563         return size;
34564     },
34565
34566     /**
34567      * Returns the Element this layout is bound to.
34568      * @return {Roo.Element}
34569      */
34570     getEl : function(){
34571         return this.el;
34572     },
34573
34574     /**
34575      * Returns the specified region.
34576      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34577      * @return {Roo.LayoutRegion}
34578      */
34579     getRegion : function(target){
34580         return this.regions[target.toLowerCase()];
34581     },
34582
34583     onWindowResize : function(){
34584         if(this.monitorWindowResize){
34585             this.layout();
34586         }
34587     }
34588 });
34589 /*
34590  * Based on:
34591  * Ext JS Library 1.1.1
34592  * Copyright(c) 2006-2007, Ext JS, LLC.
34593  *
34594  * Originally Released Under LGPL - original licence link has changed is not relivant.
34595  *
34596  * Fork - LGPL
34597  * <script type="text/javascript">
34598  */
34599 /**
34600  * @class Roo.bootstrap.layout.Border
34601  * @extends Roo.bootstrap.layout.Manager
34602  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34603  * please see: examples/bootstrap/nested.html<br><br>
34604  
34605 <b>The container the layout is rendered into can be either the body element or any other element.
34606 If it is not the body element, the container needs to either be an absolute positioned element,
34607 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34608 the container size if it is not the body element.</b>
34609
34610 * @constructor
34611 * Create a new Border
34612 * @param {Object} config Configuration options
34613  */
34614 Roo.bootstrap.layout.Border = function(config){
34615     config = config || {};
34616     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34617     
34618     
34619     
34620     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34621         if(config[region]){
34622             config[region].region = region;
34623             this.addRegion(config[region]);
34624         }
34625     },this);
34626     
34627 };
34628
34629 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34630
34631 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34632     /**
34633      * Creates and adds a new region if it doesn't already exist.
34634      * @param {String} target The target region key (north, south, east, west or center).
34635      * @param {Object} config The regions config object
34636      * @return {BorderLayoutRegion} The new region
34637      */
34638     addRegion : function(config)
34639     {
34640         if(!this.regions[config.region]){
34641             var r = this.factory(config);
34642             this.bindRegion(r);
34643         }
34644         return this.regions[config.region];
34645     },
34646
34647     // private (kinda)
34648     bindRegion : function(r){
34649         this.regions[r.config.region] = r;
34650         
34651         r.on("visibilitychange",    this.layout, this);
34652         r.on("paneladded",          this.layout, this);
34653         r.on("panelremoved",        this.layout, this);
34654         r.on("invalidated",         this.layout, this);
34655         r.on("resized",             this.onRegionResized, this);
34656         r.on("collapsed",           this.onRegionCollapsed, this);
34657         r.on("expanded",            this.onRegionExpanded, this);
34658     },
34659
34660     /**
34661      * Performs a layout update.
34662      */
34663     layout : function()
34664     {
34665         if(this.updating) {
34666             return;
34667         }
34668         
34669         // render all the rebions if they have not been done alreayd?
34670         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34671             if(this.regions[region] && !this.regions[region].bodyEl){
34672                 this.regions[region].onRender(this.el)
34673             }
34674         },this);
34675         
34676         var size = this.getViewSize();
34677         var w = size.width;
34678         var h = size.height;
34679         var centerW = w;
34680         var centerH = h;
34681         var centerY = 0;
34682         var centerX = 0;
34683         //var x = 0, y = 0;
34684
34685         var rs = this.regions;
34686         var north = rs["north"];
34687         var south = rs["south"]; 
34688         var west = rs["west"];
34689         var east = rs["east"];
34690         var center = rs["center"];
34691         //if(this.hideOnLayout){ // not supported anymore
34692             //c.el.setStyle("display", "none");
34693         //}
34694         if(north && north.isVisible()){
34695             var b = north.getBox();
34696             var m = north.getMargins();
34697             b.width = w - (m.left+m.right);
34698             b.x = m.left;
34699             b.y = m.top;
34700             centerY = b.height + b.y + m.bottom;
34701             centerH -= centerY;
34702             north.updateBox(this.safeBox(b));
34703         }
34704         if(south && south.isVisible()){
34705             var b = south.getBox();
34706             var m = south.getMargins();
34707             b.width = w - (m.left+m.right);
34708             b.x = m.left;
34709             var totalHeight = (b.height + m.top + m.bottom);
34710             b.y = h - totalHeight + m.top;
34711             centerH -= totalHeight;
34712             south.updateBox(this.safeBox(b));
34713         }
34714         if(west && west.isVisible()){
34715             var b = west.getBox();
34716             var m = west.getMargins();
34717             b.height = centerH - (m.top+m.bottom);
34718             b.x = m.left;
34719             b.y = centerY + m.top;
34720             var totalWidth = (b.width + m.left + m.right);
34721             centerX += totalWidth;
34722             centerW -= totalWidth;
34723             west.updateBox(this.safeBox(b));
34724         }
34725         if(east && east.isVisible()){
34726             var b = east.getBox();
34727             var m = east.getMargins();
34728             b.height = centerH - (m.top+m.bottom);
34729             var totalWidth = (b.width + m.left + m.right);
34730             b.x = w - totalWidth + m.left;
34731             b.y = centerY + m.top;
34732             centerW -= totalWidth;
34733             east.updateBox(this.safeBox(b));
34734         }
34735         if(center){
34736             var m = center.getMargins();
34737             var centerBox = {
34738                 x: centerX + m.left,
34739                 y: centerY + m.top,
34740                 width: centerW - (m.left+m.right),
34741                 height: centerH - (m.top+m.bottom)
34742             };
34743             //if(this.hideOnLayout){
34744                 //center.el.setStyle("display", "block");
34745             //}
34746             center.updateBox(this.safeBox(centerBox));
34747         }
34748         this.el.repaint();
34749         this.fireEvent("layout", this);
34750     },
34751
34752     // private
34753     safeBox : function(box){
34754         box.width = Math.max(0, box.width);
34755         box.height = Math.max(0, box.height);
34756         return box;
34757     },
34758
34759     /**
34760      * Adds a ContentPanel (or subclass) to this layout.
34761      * @param {String} target The target region key (north, south, east, west or center).
34762      * @param {Roo.ContentPanel} panel The panel to add
34763      * @return {Roo.ContentPanel} The added panel
34764      */
34765     add : function(target, panel){
34766          
34767         target = target.toLowerCase();
34768         return this.regions[target].add(panel);
34769     },
34770
34771     /**
34772      * Remove a ContentPanel (or subclass) to this layout.
34773      * @param {String} target The target region key (north, south, east, west or center).
34774      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34775      * @return {Roo.ContentPanel} The removed panel
34776      */
34777     remove : function(target, panel){
34778         target = target.toLowerCase();
34779         return this.regions[target].remove(panel);
34780     },
34781
34782     /**
34783      * Searches all regions for a panel with the specified id
34784      * @param {String} panelId
34785      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34786      */
34787     findPanel : function(panelId){
34788         var rs = this.regions;
34789         for(var target in rs){
34790             if(typeof rs[target] != "function"){
34791                 var p = rs[target].getPanel(panelId);
34792                 if(p){
34793                     return p;
34794                 }
34795             }
34796         }
34797         return null;
34798     },
34799
34800     /**
34801      * Searches all regions for a panel with the specified id and activates (shows) it.
34802      * @param {String/ContentPanel} panelId The panels id or the panel itself
34803      * @return {Roo.ContentPanel} The shown panel or null
34804      */
34805     showPanel : function(panelId) {
34806       var rs = this.regions;
34807       for(var target in rs){
34808          var r = rs[target];
34809          if(typeof r != "function"){
34810             if(r.hasPanel(panelId)){
34811                return r.showPanel(panelId);
34812             }
34813          }
34814       }
34815       return null;
34816    },
34817
34818    /**
34819      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34820      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34821      */
34822    /*
34823     restoreState : function(provider){
34824         if(!provider){
34825             provider = Roo.state.Manager;
34826         }
34827         var sm = new Roo.LayoutStateManager();
34828         sm.init(this, provider);
34829     },
34830 */
34831  
34832  
34833     /**
34834      * Adds a xtype elements to the layout.
34835      * <pre><code>
34836
34837 layout.addxtype({
34838        xtype : 'ContentPanel',
34839        region: 'west',
34840        items: [ .... ]
34841    }
34842 );
34843
34844 layout.addxtype({
34845         xtype : 'NestedLayoutPanel',
34846         region: 'west',
34847         layout: {
34848            center: { },
34849            west: { }   
34850         },
34851         items : [ ... list of content panels or nested layout panels.. ]
34852    }
34853 );
34854 </code></pre>
34855      * @param {Object} cfg Xtype definition of item to add.
34856      */
34857     addxtype : function(cfg)
34858     {
34859         // basically accepts a pannel...
34860         // can accept a layout region..!?!?
34861         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34862         
34863         
34864         // theory?  children can only be panels??
34865         
34866         //if (!cfg.xtype.match(/Panel$/)) {
34867         //    return false;
34868         //}
34869         var ret = false;
34870         
34871         if (typeof(cfg.region) == 'undefined') {
34872             Roo.log("Failed to add Panel, region was not set");
34873             Roo.log(cfg);
34874             return false;
34875         }
34876         var region = cfg.region;
34877         delete cfg.region;
34878         
34879           
34880         var xitems = [];
34881         if (cfg.items) {
34882             xitems = cfg.items;
34883             delete cfg.items;
34884         }
34885         var nb = false;
34886         
34887         switch(cfg.xtype) 
34888         {
34889             case 'Content':  // ContentPanel (el, cfg)
34890             case 'Scroll':  // ContentPanel (el, cfg)
34891             case 'View': 
34892                 cfg.autoCreate = true;
34893                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34894                 //} else {
34895                 //    var el = this.el.createChild();
34896                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34897                 //}
34898                 
34899                 this.add(region, ret);
34900                 break;
34901             
34902             /*
34903             case 'TreePanel': // our new panel!
34904                 cfg.el = this.el.createChild();
34905                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34906                 this.add(region, ret);
34907                 break;
34908             */
34909             
34910             case 'Nest': 
34911                 // create a new Layout (which is  a Border Layout...
34912                 
34913                 var clayout = cfg.layout;
34914                 clayout.el  = this.el.createChild();
34915                 clayout.items   = clayout.items  || [];
34916                 
34917                 delete cfg.layout;
34918                 
34919                 // replace this exitems with the clayout ones..
34920                 xitems = clayout.items;
34921                  
34922                 // force background off if it's in center...
34923                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34924                     cfg.background = false;
34925                 }
34926                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34927                 
34928                 
34929                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34930                 //console.log('adding nested layout panel '  + cfg.toSource());
34931                 this.add(region, ret);
34932                 nb = {}; /// find first...
34933                 break;
34934             
34935             case 'Grid':
34936                 
34937                 // needs grid and region
34938                 
34939                 //var el = this.getRegion(region).el.createChild();
34940                 /*
34941                  *var el = this.el.createChild();
34942                 // create the grid first...
34943                 cfg.grid.container = el;
34944                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34945                 */
34946                 
34947                 if (region == 'center' && this.active ) {
34948                     cfg.background = false;
34949                 }
34950                 
34951                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34952                 
34953                 this.add(region, ret);
34954                 /*
34955                 if (cfg.background) {
34956                     // render grid on panel activation (if panel background)
34957                     ret.on('activate', function(gp) {
34958                         if (!gp.grid.rendered) {
34959                     //        gp.grid.render(el);
34960                         }
34961                     });
34962                 } else {
34963                   //  cfg.grid.render(el);
34964                 }
34965                 */
34966                 break;
34967            
34968            
34969             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34970                 // it was the old xcomponent building that caused this before.
34971                 // espeically if border is the top element in the tree.
34972                 ret = this;
34973                 break; 
34974                 
34975                     
34976                 
34977                 
34978                 
34979             default:
34980                 /*
34981                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34982                     
34983                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34984                     this.add(region, ret);
34985                 } else {
34986                 */
34987                     Roo.log(cfg);
34988                     throw "Can not add '" + cfg.xtype + "' to Border";
34989                     return null;
34990              
34991                                 
34992              
34993         }
34994         this.beginUpdate();
34995         // add children..
34996         var region = '';
34997         var abn = {};
34998         Roo.each(xitems, function(i)  {
34999             region = nb && i.region ? i.region : false;
35000             
35001             var add = ret.addxtype(i);
35002            
35003             if (region) {
35004                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35005                 if (!i.background) {
35006                     abn[region] = nb[region] ;
35007                 }
35008             }
35009             
35010         });
35011         this.endUpdate();
35012
35013         // make the last non-background panel active..
35014         //if (nb) { Roo.log(abn); }
35015         if (nb) {
35016             
35017             for(var r in abn) {
35018                 region = this.getRegion(r);
35019                 if (region) {
35020                     // tried using nb[r], but it does not work..
35021                      
35022                     region.showPanel(abn[r]);
35023                    
35024                 }
35025             }
35026         }
35027         return ret;
35028         
35029     },
35030     
35031     
35032 // private
35033     factory : function(cfg)
35034     {
35035         
35036         var validRegions = Roo.bootstrap.layout.Border.regions;
35037
35038         var target = cfg.region;
35039         cfg.mgr = this;
35040         
35041         var r = Roo.bootstrap.layout;
35042         Roo.log(target);
35043         switch(target){
35044             case "north":
35045                 return new r.North(cfg);
35046             case "south":
35047                 return new r.South(cfg);
35048             case "east":
35049                 return new r.East(cfg);
35050             case "west":
35051                 return new r.West(cfg);
35052             case "center":
35053                 return new r.Center(cfg);
35054         }
35055         throw 'Layout region "'+target+'" not supported.';
35056     }
35057     
35058     
35059 });
35060  /*
35061  * Based on:
35062  * Ext JS Library 1.1.1
35063  * Copyright(c) 2006-2007, Ext JS, LLC.
35064  *
35065  * Originally Released Under LGPL - original licence link has changed is not relivant.
35066  *
35067  * Fork - LGPL
35068  * <script type="text/javascript">
35069  */
35070  
35071 /**
35072  * @class Roo.bootstrap.layout.Basic
35073  * @extends Roo.util.Observable
35074  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35075  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35076  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35077  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35078  * @cfg {string}   region  the region that it inhabits..
35079  * @cfg {bool}   skipConfig skip config?
35080  * 
35081
35082  */
35083 Roo.bootstrap.layout.Basic = function(config){
35084     
35085     this.mgr = config.mgr;
35086     
35087     this.position = config.region;
35088     
35089     var skipConfig = config.skipConfig;
35090     
35091     this.events = {
35092         /**
35093          * @scope Roo.BasicLayoutRegion
35094          */
35095         
35096         /**
35097          * @event beforeremove
35098          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35099          * @param {Roo.LayoutRegion} this
35100          * @param {Roo.ContentPanel} panel The panel
35101          * @param {Object} e The cancel event object
35102          */
35103         "beforeremove" : true,
35104         /**
35105          * @event invalidated
35106          * Fires when the layout for this region is changed.
35107          * @param {Roo.LayoutRegion} this
35108          */
35109         "invalidated" : true,
35110         /**
35111          * @event visibilitychange
35112          * Fires when this region is shown or hidden 
35113          * @param {Roo.LayoutRegion} this
35114          * @param {Boolean} visibility true or false
35115          */
35116         "visibilitychange" : true,
35117         /**
35118          * @event paneladded
35119          * Fires when a panel is added. 
35120          * @param {Roo.LayoutRegion} this
35121          * @param {Roo.ContentPanel} panel The panel
35122          */
35123         "paneladded" : true,
35124         /**
35125          * @event panelremoved
35126          * Fires when a panel is removed. 
35127          * @param {Roo.LayoutRegion} this
35128          * @param {Roo.ContentPanel} panel The panel
35129          */
35130         "panelremoved" : true,
35131         /**
35132          * @event beforecollapse
35133          * Fires when this region before collapse.
35134          * @param {Roo.LayoutRegion} this
35135          */
35136         "beforecollapse" : true,
35137         /**
35138          * @event collapsed
35139          * Fires when this region is collapsed.
35140          * @param {Roo.LayoutRegion} this
35141          */
35142         "collapsed" : true,
35143         /**
35144          * @event expanded
35145          * Fires when this region is expanded.
35146          * @param {Roo.LayoutRegion} this
35147          */
35148         "expanded" : true,
35149         /**
35150          * @event slideshow
35151          * Fires when this region is slid into view.
35152          * @param {Roo.LayoutRegion} this
35153          */
35154         "slideshow" : true,
35155         /**
35156          * @event slidehide
35157          * Fires when this region slides out of view. 
35158          * @param {Roo.LayoutRegion} this
35159          */
35160         "slidehide" : true,
35161         /**
35162          * @event panelactivated
35163          * Fires when a panel is activated. 
35164          * @param {Roo.LayoutRegion} this
35165          * @param {Roo.ContentPanel} panel The activated panel
35166          */
35167         "panelactivated" : true,
35168         /**
35169          * @event resized
35170          * Fires when the user resizes this region. 
35171          * @param {Roo.LayoutRegion} this
35172          * @param {Number} newSize The new size (width for east/west, height for north/south)
35173          */
35174         "resized" : true
35175     };
35176     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35177     this.panels = new Roo.util.MixedCollection();
35178     this.panels.getKey = this.getPanelId.createDelegate(this);
35179     this.box = null;
35180     this.activePanel = null;
35181     // ensure listeners are added...
35182     
35183     if (config.listeners || config.events) {
35184         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35185             listeners : config.listeners || {},
35186             events : config.events || {}
35187         });
35188     }
35189     
35190     if(skipConfig !== true){
35191         this.applyConfig(config);
35192     }
35193 };
35194
35195 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35196 {
35197     getPanelId : function(p){
35198         return p.getId();
35199     },
35200     
35201     applyConfig : function(config){
35202         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35203         this.config = config;
35204         
35205     },
35206     
35207     /**
35208      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35209      * the width, for horizontal (north, south) the height.
35210      * @param {Number} newSize The new width or height
35211      */
35212     resizeTo : function(newSize){
35213         var el = this.el ? this.el :
35214                  (this.activePanel ? this.activePanel.getEl() : null);
35215         if(el){
35216             switch(this.position){
35217                 case "east":
35218                 case "west":
35219                     el.setWidth(newSize);
35220                     this.fireEvent("resized", this, newSize);
35221                 break;
35222                 case "north":
35223                 case "south":
35224                     el.setHeight(newSize);
35225                     this.fireEvent("resized", this, newSize);
35226                 break;                
35227             }
35228         }
35229     },
35230     
35231     getBox : function(){
35232         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35233     },
35234     
35235     getMargins : function(){
35236         return this.margins;
35237     },
35238     
35239     updateBox : function(box){
35240         this.box = box;
35241         var el = this.activePanel.getEl();
35242         el.dom.style.left = box.x + "px";
35243         el.dom.style.top = box.y + "px";
35244         this.activePanel.setSize(box.width, box.height);
35245     },
35246     
35247     /**
35248      * Returns the container element for this region.
35249      * @return {Roo.Element}
35250      */
35251     getEl : function(){
35252         return this.activePanel;
35253     },
35254     
35255     /**
35256      * Returns true if this region is currently visible.
35257      * @return {Boolean}
35258      */
35259     isVisible : function(){
35260         return this.activePanel ? true : false;
35261     },
35262     
35263     setActivePanel : function(panel){
35264         panel = this.getPanel(panel);
35265         if(this.activePanel && this.activePanel != panel){
35266             this.activePanel.setActiveState(false);
35267             this.activePanel.getEl().setLeftTop(-10000,-10000);
35268         }
35269         this.activePanel = panel;
35270         panel.setActiveState(true);
35271         if(this.box){
35272             panel.setSize(this.box.width, this.box.height);
35273         }
35274         this.fireEvent("panelactivated", this, panel);
35275         this.fireEvent("invalidated");
35276     },
35277     
35278     /**
35279      * Show the specified panel.
35280      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35281      * @return {Roo.ContentPanel} The shown panel or null
35282      */
35283     showPanel : function(panel){
35284         panel = this.getPanel(panel);
35285         if(panel){
35286             this.setActivePanel(panel);
35287         }
35288         return panel;
35289     },
35290     
35291     /**
35292      * Get the active panel for this region.
35293      * @return {Roo.ContentPanel} The active panel or null
35294      */
35295     getActivePanel : function(){
35296         return this.activePanel;
35297     },
35298     
35299     /**
35300      * Add the passed ContentPanel(s)
35301      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35302      * @return {Roo.ContentPanel} The panel added (if only one was added)
35303      */
35304     add : function(panel){
35305         if(arguments.length > 1){
35306             for(var i = 0, len = arguments.length; i < len; i++) {
35307                 this.add(arguments[i]);
35308             }
35309             return null;
35310         }
35311         if(this.hasPanel(panel)){
35312             this.showPanel(panel);
35313             return panel;
35314         }
35315         var el = panel.getEl();
35316         if(el.dom.parentNode != this.mgr.el.dom){
35317             this.mgr.el.dom.appendChild(el.dom);
35318         }
35319         if(panel.setRegion){
35320             panel.setRegion(this);
35321         }
35322         this.panels.add(panel);
35323         el.setStyle("position", "absolute");
35324         if(!panel.background){
35325             this.setActivePanel(panel);
35326             if(this.config.initialSize && this.panels.getCount()==1){
35327                 this.resizeTo(this.config.initialSize);
35328             }
35329         }
35330         this.fireEvent("paneladded", this, panel);
35331         return panel;
35332     },
35333     
35334     /**
35335      * Returns true if the panel is in this region.
35336      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35337      * @return {Boolean}
35338      */
35339     hasPanel : function(panel){
35340         if(typeof panel == "object"){ // must be panel obj
35341             panel = panel.getId();
35342         }
35343         return this.getPanel(panel) ? true : false;
35344     },
35345     
35346     /**
35347      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35348      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35349      * @param {Boolean} preservePanel Overrides the config preservePanel option
35350      * @return {Roo.ContentPanel} The panel that was removed
35351      */
35352     remove : function(panel, preservePanel){
35353         panel = this.getPanel(panel);
35354         if(!panel){
35355             return null;
35356         }
35357         var e = {};
35358         this.fireEvent("beforeremove", this, panel, e);
35359         if(e.cancel === true){
35360             return null;
35361         }
35362         var panelId = panel.getId();
35363         this.panels.removeKey(panelId);
35364         return panel;
35365     },
35366     
35367     /**
35368      * Returns the panel specified or null if it's not in this region.
35369      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35370      * @return {Roo.ContentPanel}
35371      */
35372     getPanel : function(id){
35373         if(typeof id == "object"){ // must be panel obj
35374             return id;
35375         }
35376         return this.panels.get(id);
35377     },
35378     
35379     /**
35380      * Returns this regions position (north/south/east/west/center).
35381      * @return {String} 
35382      */
35383     getPosition: function(){
35384         return this.position;    
35385     }
35386 });/*
35387  * Based on:
35388  * Ext JS Library 1.1.1
35389  * Copyright(c) 2006-2007, Ext JS, LLC.
35390  *
35391  * Originally Released Under LGPL - original licence link has changed is not relivant.
35392  *
35393  * Fork - LGPL
35394  * <script type="text/javascript">
35395  */
35396  
35397 /**
35398  * @class Roo.bootstrap.layout.Region
35399  * @extends Roo.bootstrap.layout.Basic
35400  * This class represents a region in a layout manager.
35401  
35402  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35403  * @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})
35404  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35405  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35406  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35407  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35408  * @cfg {String}    title           The title for the region (overrides panel titles)
35409  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35410  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35411  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35412  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35413  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35414  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35415  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35416  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35417  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35418  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35419
35420  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35421  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35422  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35423  * @cfg {Number}    width           For East/West panels
35424  * @cfg {Number}    height          For North/South panels
35425  * @cfg {Boolean}   split           To show the splitter
35426  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35427  * 
35428  * @cfg {string}   cls             Extra CSS classes to add to region
35429  * 
35430  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35431  * @cfg {string}   region  the region that it inhabits..
35432  *
35433
35434  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35435  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35436
35437  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35438  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35439  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35440  */
35441 Roo.bootstrap.layout.Region = function(config)
35442 {
35443     this.applyConfig(config);
35444
35445     var mgr = config.mgr;
35446     var pos = config.region;
35447     config.skipConfig = true;
35448     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35449     
35450     if (mgr.el) {
35451         this.onRender(mgr.el);   
35452     }
35453      
35454     this.visible = true;
35455     this.collapsed = false;
35456     this.unrendered_panels = [];
35457 };
35458
35459 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35460
35461     position: '', // set by wrapper (eg. north/south etc..)
35462     unrendered_panels : null,  // unrendered panels.
35463     createBody : function(){
35464         /** This region's body element 
35465         * @type Roo.Element */
35466         this.bodyEl = this.el.createChild({
35467                 tag: "div",
35468                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35469         });
35470     },
35471
35472     onRender: function(ctr, pos)
35473     {
35474         var dh = Roo.DomHelper;
35475         /** This region's container element 
35476         * @type Roo.Element */
35477         this.el = dh.append(ctr.dom, {
35478                 tag: "div",
35479                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35480             }, true);
35481         /** This region's title element 
35482         * @type Roo.Element */
35483     
35484         this.titleEl = dh.append(this.el.dom,
35485             {
35486                     tag: "div",
35487                     unselectable: "on",
35488                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35489                     children:[
35490                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35491                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35492                     ]}, true);
35493         
35494         this.titleEl.enableDisplayMode();
35495         /** This region's title text element 
35496         * @type HTMLElement */
35497         this.titleTextEl = this.titleEl.dom.firstChild;
35498         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35499         /*
35500         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35501         this.closeBtn.enableDisplayMode();
35502         this.closeBtn.on("click", this.closeClicked, this);
35503         this.closeBtn.hide();
35504     */
35505         this.createBody(this.config);
35506         if(this.config.hideWhenEmpty){
35507             this.hide();
35508             this.on("paneladded", this.validateVisibility, this);
35509             this.on("panelremoved", this.validateVisibility, this);
35510         }
35511         if(this.autoScroll){
35512             this.bodyEl.setStyle("overflow", "auto");
35513         }else{
35514             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35515         }
35516         //if(c.titlebar !== false){
35517             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35518                 this.titleEl.hide();
35519             }else{
35520                 this.titleEl.show();
35521                 if(this.config.title){
35522                     this.titleTextEl.innerHTML = this.config.title;
35523                 }
35524             }
35525         //}
35526         if(this.config.collapsed){
35527             this.collapse(true);
35528         }
35529         if(this.config.hidden){
35530             this.hide();
35531         }
35532         
35533         if (this.unrendered_panels && this.unrendered_panels.length) {
35534             for (var i =0;i< this.unrendered_panels.length; i++) {
35535                 this.add(this.unrendered_panels[i]);
35536             }
35537             this.unrendered_panels = null;
35538             
35539         }
35540         
35541     },
35542     
35543     applyConfig : function(c)
35544     {
35545         /*
35546          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35547             var dh = Roo.DomHelper;
35548             if(c.titlebar !== false){
35549                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35550                 this.collapseBtn.on("click", this.collapse, this);
35551                 this.collapseBtn.enableDisplayMode();
35552                 /*
35553                 if(c.showPin === true || this.showPin){
35554                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35555                     this.stickBtn.enableDisplayMode();
35556                     this.stickBtn.on("click", this.expand, this);
35557                     this.stickBtn.hide();
35558                 }
35559                 
35560             }
35561             */
35562             /** This region's collapsed element
35563             * @type Roo.Element */
35564             /*
35565              *
35566             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35567                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35568             ]}, true);
35569             
35570             if(c.floatable !== false){
35571                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35572                this.collapsedEl.on("click", this.collapseClick, this);
35573             }
35574
35575             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35576                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35577                    id: "message", unselectable: "on", style:{"float":"left"}});
35578                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35579              }
35580             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35581             this.expandBtn.on("click", this.expand, this);
35582             
35583         }
35584         
35585         if(this.collapseBtn){
35586             this.collapseBtn.setVisible(c.collapsible == true);
35587         }
35588         
35589         this.cmargins = c.cmargins || this.cmargins ||
35590                          (this.position == "west" || this.position == "east" ?
35591                              {top: 0, left: 2, right:2, bottom: 0} :
35592                              {top: 2, left: 0, right:0, bottom: 2});
35593         */
35594         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35595         
35596         
35597         this.bottomTabs = c.tabPosition != "top";
35598         
35599         this.autoScroll = c.autoScroll || false;
35600         
35601         
35602        
35603         
35604         this.duration = c.duration || .30;
35605         this.slideDuration = c.slideDuration || .45;
35606         this.config = c;
35607        
35608     },
35609     /**
35610      * Returns true if this region is currently visible.
35611      * @return {Boolean}
35612      */
35613     isVisible : function(){
35614         return this.visible;
35615     },
35616
35617     /**
35618      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35619      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35620      */
35621     //setCollapsedTitle : function(title){
35622     //    title = title || "&#160;";
35623      //   if(this.collapsedTitleTextEl){
35624       //      this.collapsedTitleTextEl.innerHTML = title;
35625        // }
35626     //},
35627
35628     getBox : function(){
35629         var b;
35630       //  if(!this.collapsed){
35631             b = this.el.getBox(false, true);
35632        // }else{
35633           //  b = this.collapsedEl.getBox(false, true);
35634         //}
35635         return b;
35636     },
35637
35638     getMargins : function(){
35639         return this.margins;
35640         //return this.collapsed ? this.cmargins : this.margins;
35641     },
35642 /*
35643     highlight : function(){
35644         this.el.addClass("x-layout-panel-dragover");
35645     },
35646
35647     unhighlight : function(){
35648         this.el.removeClass("x-layout-panel-dragover");
35649     },
35650 */
35651     updateBox : function(box)
35652     {
35653         if (!this.bodyEl) {
35654             return; // not rendered yet..
35655         }
35656         
35657         this.box = box;
35658         if(!this.collapsed){
35659             this.el.dom.style.left = box.x + "px";
35660             this.el.dom.style.top = box.y + "px";
35661             this.updateBody(box.width, box.height);
35662         }else{
35663             this.collapsedEl.dom.style.left = box.x + "px";
35664             this.collapsedEl.dom.style.top = box.y + "px";
35665             this.collapsedEl.setSize(box.width, box.height);
35666         }
35667         if(this.tabs){
35668             this.tabs.autoSizeTabs();
35669         }
35670     },
35671
35672     updateBody : function(w, h)
35673     {
35674         if(w !== null){
35675             this.el.setWidth(w);
35676             w -= this.el.getBorderWidth("rl");
35677             if(this.config.adjustments){
35678                 w += this.config.adjustments[0];
35679             }
35680         }
35681         if(h !== null && h > 0){
35682             this.el.setHeight(h);
35683             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35684             h -= this.el.getBorderWidth("tb");
35685             if(this.config.adjustments){
35686                 h += this.config.adjustments[1];
35687             }
35688             this.bodyEl.setHeight(h);
35689             if(this.tabs){
35690                 h = this.tabs.syncHeight(h);
35691             }
35692         }
35693         if(this.panelSize){
35694             w = w !== null ? w : this.panelSize.width;
35695             h = h !== null ? h : this.panelSize.height;
35696         }
35697         if(this.activePanel){
35698             var el = this.activePanel.getEl();
35699             w = w !== null ? w : el.getWidth();
35700             h = h !== null ? h : el.getHeight();
35701             this.panelSize = {width: w, height: h};
35702             this.activePanel.setSize(w, h);
35703         }
35704         if(Roo.isIE && this.tabs){
35705             this.tabs.el.repaint();
35706         }
35707     },
35708
35709     /**
35710      * Returns the container element for this region.
35711      * @return {Roo.Element}
35712      */
35713     getEl : function(){
35714         return this.el;
35715     },
35716
35717     /**
35718      * Hides this region.
35719      */
35720     hide : function(){
35721         //if(!this.collapsed){
35722             this.el.dom.style.left = "-2000px";
35723             this.el.hide();
35724         //}else{
35725          //   this.collapsedEl.dom.style.left = "-2000px";
35726          //   this.collapsedEl.hide();
35727        // }
35728         this.visible = false;
35729         this.fireEvent("visibilitychange", this, false);
35730     },
35731
35732     /**
35733      * Shows this region if it was previously hidden.
35734      */
35735     show : function(){
35736         //if(!this.collapsed){
35737             this.el.show();
35738         //}else{
35739         //    this.collapsedEl.show();
35740        // }
35741         this.visible = true;
35742         this.fireEvent("visibilitychange", this, true);
35743     },
35744 /*
35745     closeClicked : function(){
35746         if(this.activePanel){
35747             this.remove(this.activePanel);
35748         }
35749     },
35750
35751     collapseClick : function(e){
35752         if(this.isSlid){
35753            e.stopPropagation();
35754            this.slideIn();
35755         }else{
35756            e.stopPropagation();
35757            this.slideOut();
35758         }
35759     },
35760 */
35761     /**
35762      * Collapses this region.
35763      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35764      */
35765     /*
35766     collapse : function(skipAnim, skipCheck = false){
35767         if(this.collapsed) {
35768             return;
35769         }
35770         
35771         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35772             
35773             this.collapsed = true;
35774             if(this.split){
35775                 this.split.el.hide();
35776             }
35777             if(this.config.animate && skipAnim !== true){
35778                 this.fireEvent("invalidated", this);
35779                 this.animateCollapse();
35780             }else{
35781                 this.el.setLocation(-20000,-20000);
35782                 this.el.hide();
35783                 this.collapsedEl.show();
35784                 this.fireEvent("collapsed", this);
35785                 this.fireEvent("invalidated", this);
35786             }
35787         }
35788         
35789     },
35790 */
35791     animateCollapse : function(){
35792         // overridden
35793     },
35794
35795     /**
35796      * Expands this region if it was previously collapsed.
35797      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35798      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35799      */
35800     /*
35801     expand : function(e, skipAnim){
35802         if(e) {
35803             e.stopPropagation();
35804         }
35805         if(!this.collapsed || this.el.hasActiveFx()) {
35806             return;
35807         }
35808         if(this.isSlid){
35809             this.afterSlideIn();
35810             skipAnim = true;
35811         }
35812         this.collapsed = false;
35813         if(this.config.animate && skipAnim !== true){
35814             this.animateExpand();
35815         }else{
35816             this.el.show();
35817             if(this.split){
35818                 this.split.el.show();
35819             }
35820             this.collapsedEl.setLocation(-2000,-2000);
35821             this.collapsedEl.hide();
35822             this.fireEvent("invalidated", this);
35823             this.fireEvent("expanded", this);
35824         }
35825     },
35826 */
35827     animateExpand : function(){
35828         // overridden
35829     },
35830
35831     initTabs : function()
35832     {
35833         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35834         
35835         var ts = new Roo.bootstrap.panel.Tabs({
35836                 el: this.bodyEl.dom,
35837                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35838                 disableTooltips: this.config.disableTabTips,
35839                 toolbar : this.config.toolbar
35840             });
35841         
35842         if(this.config.hideTabs){
35843             ts.stripWrap.setDisplayed(false);
35844         }
35845         this.tabs = ts;
35846         ts.resizeTabs = this.config.resizeTabs === true;
35847         ts.minTabWidth = this.config.minTabWidth || 40;
35848         ts.maxTabWidth = this.config.maxTabWidth || 250;
35849         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35850         ts.monitorResize = false;
35851         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35852         ts.bodyEl.addClass('roo-layout-tabs-body');
35853         this.panels.each(this.initPanelAsTab, this);
35854     },
35855
35856     initPanelAsTab : function(panel){
35857         var ti = this.tabs.addTab(
35858             panel.getEl().id,
35859             panel.getTitle(),
35860             null,
35861             this.config.closeOnTab && panel.isClosable(),
35862             panel.tpl
35863         );
35864         if(panel.tabTip !== undefined){
35865             ti.setTooltip(panel.tabTip);
35866         }
35867         ti.on("activate", function(){
35868               this.setActivePanel(panel);
35869         }, this);
35870         
35871         if(this.config.closeOnTab){
35872             ti.on("beforeclose", function(t, e){
35873                 e.cancel = true;
35874                 this.remove(panel);
35875             }, this);
35876         }
35877         
35878         panel.tabItem = ti;
35879         
35880         return ti;
35881     },
35882
35883     updatePanelTitle : function(panel, title)
35884     {
35885         if(this.activePanel == panel){
35886             this.updateTitle(title);
35887         }
35888         if(this.tabs){
35889             var ti = this.tabs.getTab(panel.getEl().id);
35890             ti.setText(title);
35891             if(panel.tabTip !== undefined){
35892                 ti.setTooltip(panel.tabTip);
35893             }
35894         }
35895     },
35896
35897     updateTitle : function(title){
35898         if(this.titleTextEl && !this.config.title){
35899             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35900         }
35901     },
35902
35903     setActivePanel : function(panel)
35904     {
35905         panel = this.getPanel(panel);
35906         if(this.activePanel && this.activePanel != panel){
35907             if(this.activePanel.setActiveState(false) === false){
35908                 return;
35909             }
35910         }
35911         this.activePanel = panel;
35912         panel.setActiveState(true);
35913         if(this.panelSize){
35914             panel.setSize(this.panelSize.width, this.panelSize.height);
35915         }
35916         if(this.closeBtn){
35917             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35918         }
35919         this.updateTitle(panel.getTitle());
35920         if(this.tabs){
35921             this.fireEvent("invalidated", this);
35922         }
35923         this.fireEvent("panelactivated", this, panel);
35924     },
35925
35926     /**
35927      * Shows the specified panel.
35928      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35929      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35930      */
35931     showPanel : function(panel)
35932     {
35933         panel = this.getPanel(panel);
35934         if(panel){
35935             if(this.tabs){
35936                 var tab = this.tabs.getTab(panel.getEl().id);
35937                 if(tab.isHidden()){
35938                     this.tabs.unhideTab(tab.id);
35939                 }
35940                 tab.activate();
35941             }else{
35942                 this.setActivePanel(panel);
35943             }
35944         }
35945         return panel;
35946     },
35947
35948     /**
35949      * Get the active panel for this region.
35950      * @return {Roo.ContentPanel} The active panel or null
35951      */
35952     getActivePanel : function(){
35953         return this.activePanel;
35954     },
35955
35956     validateVisibility : function(){
35957         if(this.panels.getCount() < 1){
35958             this.updateTitle("&#160;");
35959             this.closeBtn.hide();
35960             this.hide();
35961         }else{
35962             if(!this.isVisible()){
35963                 this.show();
35964             }
35965         }
35966     },
35967
35968     /**
35969      * Adds the passed ContentPanel(s) to this region.
35970      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35971      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35972      */
35973     add : function(panel)
35974     {
35975         if(arguments.length > 1){
35976             for(var i = 0, len = arguments.length; i < len; i++) {
35977                 this.add(arguments[i]);
35978             }
35979             return null;
35980         }
35981         
35982         // if we have not been rendered yet, then we can not really do much of this..
35983         if (!this.bodyEl) {
35984             this.unrendered_panels.push(panel);
35985             return panel;
35986         }
35987         
35988         
35989         
35990         
35991         if(this.hasPanel(panel)){
35992             this.showPanel(panel);
35993             return panel;
35994         }
35995         panel.setRegion(this);
35996         this.panels.add(panel);
35997        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35998             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35999             // and hide them... ???
36000             this.bodyEl.dom.appendChild(panel.getEl().dom);
36001             if(panel.background !== true){
36002                 this.setActivePanel(panel);
36003             }
36004             this.fireEvent("paneladded", this, panel);
36005             return panel;
36006         }
36007         */
36008         if(!this.tabs){
36009             this.initTabs();
36010         }else{
36011             this.initPanelAsTab(panel);
36012         }
36013         
36014         
36015         if(panel.background !== true){
36016             this.tabs.activate(panel.getEl().id);
36017         }
36018         this.fireEvent("paneladded", this, panel);
36019         return panel;
36020     },
36021
36022     /**
36023      * Hides the tab for the specified panel.
36024      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36025      */
36026     hidePanel : function(panel){
36027         if(this.tabs && (panel = this.getPanel(panel))){
36028             this.tabs.hideTab(panel.getEl().id);
36029         }
36030     },
36031
36032     /**
36033      * Unhides the tab for a previously hidden panel.
36034      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36035      */
36036     unhidePanel : function(panel){
36037         if(this.tabs && (panel = this.getPanel(panel))){
36038             this.tabs.unhideTab(panel.getEl().id);
36039         }
36040     },
36041
36042     clearPanels : function(){
36043         while(this.panels.getCount() > 0){
36044              this.remove(this.panels.first());
36045         }
36046     },
36047
36048     /**
36049      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36050      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36051      * @param {Boolean} preservePanel Overrides the config preservePanel option
36052      * @return {Roo.ContentPanel} The panel that was removed
36053      */
36054     remove : function(panel, preservePanel)
36055     {
36056         panel = this.getPanel(panel);
36057         if(!panel){
36058             return null;
36059         }
36060         var e = {};
36061         this.fireEvent("beforeremove", this, panel, e);
36062         if(e.cancel === true){
36063             return null;
36064         }
36065         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36066         var panelId = panel.getId();
36067         this.panels.removeKey(panelId);
36068         if(preservePanel){
36069             document.body.appendChild(panel.getEl().dom);
36070         }
36071         if(this.tabs){
36072             this.tabs.removeTab(panel.getEl().id);
36073         }else if (!preservePanel){
36074             this.bodyEl.dom.removeChild(panel.getEl().dom);
36075         }
36076         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36077             var p = this.panels.first();
36078             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36079             tempEl.appendChild(p.getEl().dom);
36080             this.bodyEl.update("");
36081             this.bodyEl.dom.appendChild(p.getEl().dom);
36082             tempEl = null;
36083             this.updateTitle(p.getTitle());
36084             this.tabs = null;
36085             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36086             this.setActivePanel(p);
36087         }
36088         panel.setRegion(null);
36089         if(this.activePanel == panel){
36090             this.activePanel = null;
36091         }
36092         if(this.config.autoDestroy !== false && preservePanel !== true){
36093             try{panel.destroy();}catch(e){}
36094         }
36095         this.fireEvent("panelremoved", this, panel);
36096         return panel;
36097     },
36098
36099     /**
36100      * Returns the TabPanel component used by this region
36101      * @return {Roo.TabPanel}
36102      */
36103     getTabs : function(){
36104         return this.tabs;
36105     },
36106
36107     createTool : function(parentEl, className){
36108         var btn = Roo.DomHelper.append(parentEl, {
36109             tag: "div",
36110             cls: "x-layout-tools-button",
36111             children: [ {
36112                 tag: "div",
36113                 cls: "roo-layout-tools-button-inner " + className,
36114                 html: "&#160;"
36115             }]
36116         }, true);
36117         btn.addClassOnOver("roo-layout-tools-button-over");
36118         return btn;
36119     }
36120 });/*
36121  * Based on:
36122  * Ext JS Library 1.1.1
36123  * Copyright(c) 2006-2007, Ext JS, LLC.
36124  *
36125  * Originally Released Under LGPL - original licence link has changed is not relivant.
36126  *
36127  * Fork - LGPL
36128  * <script type="text/javascript">
36129  */
36130  
36131
36132
36133 /**
36134  * @class Roo.SplitLayoutRegion
36135  * @extends Roo.LayoutRegion
36136  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36137  */
36138 Roo.bootstrap.layout.Split = function(config){
36139     this.cursor = config.cursor;
36140     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36141 };
36142
36143 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36144 {
36145     splitTip : "Drag to resize.",
36146     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36147     useSplitTips : false,
36148
36149     applyConfig : function(config){
36150         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36151     },
36152     
36153     onRender : function(ctr,pos) {
36154         
36155         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36156         if(!this.config.split){
36157             return;
36158         }
36159         if(!this.split){
36160             
36161             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36162                             tag: "div",
36163                             id: this.el.id + "-split",
36164                             cls: "roo-layout-split roo-layout-split-"+this.position,
36165                             html: "&#160;"
36166             });
36167             /** The SplitBar for this region 
36168             * @type Roo.SplitBar */
36169             // does not exist yet...
36170             Roo.log([this.position, this.orientation]);
36171             
36172             this.split = new Roo.bootstrap.SplitBar({
36173                 dragElement : splitEl,
36174                 resizingElement: this.el,
36175                 orientation : this.orientation
36176             });
36177             
36178             this.split.on("moved", this.onSplitMove, this);
36179             this.split.useShim = this.config.useShim === true;
36180             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36181             if(this.useSplitTips){
36182                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36183             }
36184             //if(config.collapsible){
36185             //    this.split.el.on("dblclick", this.collapse,  this);
36186             //}
36187         }
36188         if(typeof this.config.minSize != "undefined"){
36189             this.split.minSize = this.config.minSize;
36190         }
36191         if(typeof this.config.maxSize != "undefined"){
36192             this.split.maxSize = this.config.maxSize;
36193         }
36194         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36195             this.hideSplitter();
36196         }
36197         
36198     },
36199
36200     getHMaxSize : function(){
36201          var cmax = this.config.maxSize || 10000;
36202          var center = this.mgr.getRegion("center");
36203          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36204     },
36205
36206     getVMaxSize : function(){
36207          var cmax = this.config.maxSize || 10000;
36208          var center = this.mgr.getRegion("center");
36209          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36210     },
36211
36212     onSplitMove : function(split, newSize){
36213         this.fireEvent("resized", this, newSize);
36214     },
36215     
36216     /** 
36217      * Returns the {@link Roo.SplitBar} for this region.
36218      * @return {Roo.SplitBar}
36219      */
36220     getSplitBar : function(){
36221         return this.split;
36222     },
36223     
36224     hide : function(){
36225         this.hideSplitter();
36226         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36227     },
36228
36229     hideSplitter : function(){
36230         if(this.split){
36231             this.split.el.setLocation(-2000,-2000);
36232             this.split.el.hide();
36233         }
36234     },
36235
36236     show : function(){
36237         if(this.split){
36238             this.split.el.show();
36239         }
36240         Roo.bootstrap.layout.Split.superclass.show.call(this);
36241     },
36242     
36243     beforeSlide: function(){
36244         if(Roo.isGecko){// firefox overflow auto bug workaround
36245             this.bodyEl.clip();
36246             if(this.tabs) {
36247                 this.tabs.bodyEl.clip();
36248             }
36249             if(this.activePanel){
36250                 this.activePanel.getEl().clip();
36251                 
36252                 if(this.activePanel.beforeSlide){
36253                     this.activePanel.beforeSlide();
36254                 }
36255             }
36256         }
36257     },
36258     
36259     afterSlide : function(){
36260         if(Roo.isGecko){// firefox overflow auto bug workaround
36261             this.bodyEl.unclip();
36262             if(this.tabs) {
36263                 this.tabs.bodyEl.unclip();
36264             }
36265             if(this.activePanel){
36266                 this.activePanel.getEl().unclip();
36267                 if(this.activePanel.afterSlide){
36268                     this.activePanel.afterSlide();
36269                 }
36270             }
36271         }
36272     },
36273
36274     initAutoHide : function(){
36275         if(this.autoHide !== false){
36276             if(!this.autoHideHd){
36277                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36278                 this.autoHideHd = {
36279                     "mouseout": function(e){
36280                         if(!e.within(this.el, true)){
36281                             st.delay(500);
36282                         }
36283                     },
36284                     "mouseover" : function(e){
36285                         st.cancel();
36286                     },
36287                     scope : this
36288                 };
36289             }
36290             this.el.on(this.autoHideHd);
36291         }
36292     },
36293
36294     clearAutoHide : function(){
36295         if(this.autoHide !== false){
36296             this.el.un("mouseout", this.autoHideHd.mouseout);
36297             this.el.un("mouseover", this.autoHideHd.mouseover);
36298         }
36299     },
36300
36301     clearMonitor : function(){
36302         Roo.get(document).un("click", this.slideInIf, this);
36303     },
36304
36305     // these names are backwards but not changed for compat
36306     slideOut : function(){
36307         if(this.isSlid || this.el.hasActiveFx()){
36308             return;
36309         }
36310         this.isSlid = true;
36311         if(this.collapseBtn){
36312             this.collapseBtn.hide();
36313         }
36314         this.closeBtnState = this.closeBtn.getStyle('display');
36315         this.closeBtn.hide();
36316         if(this.stickBtn){
36317             this.stickBtn.show();
36318         }
36319         this.el.show();
36320         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36321         this.beforeSlide();
36322         this.el.setStyle("z-index", 10001);
36323         this.el.slideIn(this.getSlideAnchor(), {
36324             callback: function(){
36325                 this.afterSlide();
36326                 this.initAutoHide();
36327                 Roo.get(document).on("click", this.slideInIf, this);
36328                 this.fireEvent("slideshow", this);
36329             },
36330             scope: this,
36331             block: true
36332         });
36333     },
36334
36335     afterSlideIn : function(){
36336         this.clearAutoHide();
36337         this.isSlid = false;
36338         this.clearMonitor();
36339         this.el.setStyle("z-index", "");
36340         if(this.collapseBtn){
36341             this.collapseBtn.show();
36342         }
36343         this.closeBtn.setStyle('display', this.closeBtnState);
36344         if(this.stickBtn){
36345             this.stickBtn.hide();
36346         }
36347         this.fireEvent("slidehide", this);
36348     },
36349
36350     slideIn : function(cb){
36351         if(!this.isSlid || this.el.hasActiveFx()){
36352             Roo.callback(cb);
36353             return;
36354         }
36355         this.isSlid = false;
36356         this.beforeSlide();
36357         this.el.slideOut(this.getSlideAnchor(), {
36358             callback: function(){
36359                 this.el.setLeftTop(-10000, -10000);
36360                 this.afterSlide();
36361                 this.afterSlideIn();
36362                 Roo.callback(cb);
36363             },
36364             scope: this,
36365             block: true
36366         });
36367     },
36368     
36369     slideInIf : function(e){
36370         if(!e.within(this.el)){
36371             this.slideIn();
36372         }
36373     },
36374
36375     animateCollapse : function(){
36376         this.beforeSlide();
36377         this.el.setStyle("z-index", 20000);
36378         var anchor = this.getSlideAnchor();
36379         this.el.slideOut(anchor, {
36380             callback : function(){
36381                 this.el.setStyle("z-index", "");
36382                 this.collapsedEl.slideIn(anchor, {duration:.3});
36383                 this.afterSlide();
36384                 this.el.setLocation(-10000,-10000);
36385                 this.el.hide();
36386                 this.fireEvent("collapsed", this);
36387             },
36388             scope: this,
36389             block: true
36390         });
36391     },
36392
36393     animateExpand : function(){
36394         this.beforeSlide();
36395         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36396         this.el.setStyle("z-index", 20000);
36397         this.collapsedEl.hide({
36398             duration:.1
36399         });
36400         this.el.slideIn(this.getSlideAnchor(), {
36401             callback : function(){
36402                 this.el.setStyle("z-index", "");
36403                 this.afterSlide();
36404                 if(this.split){
36405                     this.split.el.show();
36406                 }
36407                 this.fireEvent("invalidated", this);
36408                 this.fireEvent("expanded", this);
36409             },
36410             scope: this,
36411             block: true
36412         });
36413     },
36414
36415     anchors : {
36416         "west" : "left",
36417         "east" : "right",
36418         "north" : "top",
36419         "south" : "bottom"
36420     },
36421
36422     sanchors : {
36423         "west" : "l",
36424         "east" : "r",
36425         "north" : "t",
36426         "south" : "b"
36427     },
36428
36429     canchors : {
36430         "west" : "tl-tr",
36431         "east" : "tr-tl",
36432         "north" : "tl-bl",
36433         "south" : "bl-tl"
36434     },
36435
36436     getAnchor : function(){
36437         return this.anchors[this.position];
36438     },
36439
36440     getCollapseAnchor : function(){
36441         return this.canchors[this.position];
36442     },
36443
36444     getSlideAnchor : function(){
36445         return this.sanchors[this.position];
36446     },
36447
36448     getAlignAdj : function(){
36449         var cm = this.cmargins;
36450         switch(this.position){
36451             case "west":
36452                 return [0, 0];
36453             break;
36454             case "east":
36455                 return [0, 0];
36456             break;
36457             case "north":
36458                 return [0, 0];
36459             break;
36460             case "south":
36461                 return [0, 0];
36462             break;
36463         }
36464     },
36465
36466     getExpandAdj : function(){
36467         var c = this.collapsedEl, cm = this.cmargins;
36468         switch(this.position){
36469             case "west":
36470                 return [-(cm.right+c.getWidth()+cm.left), 0];
36471             break;
36472             case "east":
36473                 return [cm.right+c.getWidth()+cm.left, 0];
36474             break;
36475             case "north":
36476                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36477             break;
36478             case "south":
36479                 return [0, cm.top+cm.bottom+c.getHeight()];
36480             break;
36481         }
36482     }
36483 });/*
36484  * Based on:
36485  * Ext JS Library 1.1.1
36486  * Copyright(c) 2006-2007, Ext JS, LLC.
36487  *
36488  * Originally Released Under LGPL - original licence link has changed is not relivant.
36489  *
36490  * Fork - LGPL
36491  * <script type="text/javascript">
36492  */
36493 /*
36494  * These classes are private internal classes
36495  */
36496 Roo.bootstrap.layout.Center = function(config){
36497     config.region = "center";
36498     Roo.bootstrap.layout.Region.call(this, config);
36499     this.visible = true;
36500     this.minWidth = config.minWidth || 20;
36501     this.minHeight = config.minHeight || 20;
36502 };
36503
36504 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36505     hide : function(){
36506         // center panel can't be hidden
36507     },
36508     
36509     show : function(){
36510         // center panel can't be hidden
36511     },
36512     
36513     getMinWidth: function(){
36514         return this.minWidth;
36515     },
36516     
36517     getMinHeight: function(){
36518         return this.minHeight;
36519     }
36520 });
36521
36522
36523
36524
36525  
36526
36527
36528
36529
36530
36531 Roo.bootstrap.layout.North = function(config)
36532 {
36533     config.region = 'north';
36534     config.cursor = 'n-resize';
36535     
36536     Roo.bootstrap.layout.Split.call(this, config);
36537     
36538     
36539     if(this.split){
36540         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36541         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36542         this.split.el.addClass("roo-layout-split-v");
36543     }
36544     var size = config.initialSize || config.height;
36545     if(typeof size != "undefined"){
36546         this.el.setHeight(size);
36547     }
36548 };
36549 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36550 {
36551     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36552     
36553     
36554     
36555     getBox : function(){
36556         if(this.collapsed){
36557             return this.collapsedEl.getBox();
36558         }
36559         var box = this.el.getBox();
36560         if(this.split){
36561             box.height += this.split.el.getHeight();
36562         }
36563         return box;
36564     },
36565     
36566     updateBox : function(box){
36567         if(this.split && !this.collapsed){
36568             box.height -= this.split.el.getHeight();
36569             this.split.el.setLeft(box.x);
36570             this.split.el.setTop(box.y+box.height);
36571             this.split.el.setWidth(box.width);
36572         }
36573         if(this.collapsed){
36574             this.updateBody(box.width, null);
36575         }
36576         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36577     }
36578 });
36579
36580
36581
36582
36583
36584 Roo.bootstrap.layout.South = function(config){
36585     config.region = 'south';
36586     config.cursor = 's-resize';
36587     Roo.bootstrap.layout.Split.call(this, config);
36588     if(this.split){
36589         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36590         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36591         this.split.el.addClass("roo-layout-split-v");
36592     }
36593     var size = config.initialSize || config.height;
36594     if(typeof size != "undefined"){
36595         this.el.setHeight(size);
36596     }
36597 };
36598
36599 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36600     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36601     getBox : function(){
36602         if(this.collapsed){
36603             return this.collapsedEl.getBox();
36604         }
36605         var box = this.el.getBox();
36606         if(this.split){
36607             var sh = this.split.el.getHeight();
36608             box.height += sh;
36609             box.y -= sh;
36610         }
36611         return box;
36612     },
36613     
36614     updateBox : function(box){
36615         if(this.split && !this.collapsed){
36616             var sh = this.split.el.getHeight();
36617             box.height -= sh;
36618             box.y += sh;
36619             this.split.el.setLeft(box.x);
36620             this.split.el.setTop(box.y-sh);
36621             this.split.el.setWidth(box.width);
36622         }
36623         if(this.collapsed){
36624             this.updateBody(box.width, null);
36625         }
36626         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36627     }
36628 });
36629
36630 Roo.bootstrap.layout.East = function(config){
36631     config.region = "east";
36632     config.cursor = "e-resize";
36633     Roo.bootstrap.layout.Split.call(this, config);
36634     if(this.split){
36635         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36636         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36637         this.split.el.addClass("roo-layout-split-h");
36638     }
36639     var size = config.initialSize || config.width;
36640     if(typeof size != "undefined"){
36641         this.el.setWidth(size);
36642     }
36643 };
36644 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36645     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36646     getBox : function(){
36647         if(this.collapsed){
36648             return this.collapsedEl.getBox();
36649         }
36650         var box = this.el.getBox();
36651         if(this.split){
36652             var sw = this.split.el.getWidth();
36653             box.width += sw;
36654             box.x -= sw;
36655         }
36656         return box;
36657     },
36658
36659     updateBox : function(box){
36660         if(this.split && !this.collapsed){
36661             var sw = this.split.el.getWidth();
36662             box.width -= sw;
36663             this.split.el.setLeft(box.x);
36664             this.split.el.setTop(box.y);
36665             this.split.el.setHeight(box.height);
36666             box.x += sw;
36667         }
36668         if(this.collapsed){
36669             this.updateBody(null, box.height);
36670         }
36671         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36672     }
36673 });
36674
36675 Roo.bootstrap.layout.West = function(config){
36676     config.region = "west";
36677     config.cursor = "w-resize";
36678     
36679     Roo.bootstrap.layout.Split.call(this, config);
36680     if(this.split){
36681         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36682         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36683         this.split.el.addClass("roo-layout-split-h");
36684     }
36685     
36686 };
36687 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36688     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36689     
36690     onRender: function(ctr, pos)
36691     {
36692         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36693         var size = this.config.initialSize || this.config.width;
36694         if(typeof size != "undefined"){
36695             this.el.setWidth(size);
36696         }
36697     },
36698     
36699     getBox : function(){
36700         if(this.collapsed){
36701             return this.collapsedEl.getBox();
36702         }
36703         var box = this.el.getBox();
36704         if(this.split){
36705             box.width += this.split.el.getWidth();
36706         }
36707         return box;
36708     },
36709     
36710     updateBox : function(box){
36711         if(this.split && !this.collapsed){
36712             var sw = this.split.el.getWidth();
36713             box.width -= sw;
36714             this.split.el.setLeft(box.x+box.width);
36715             this.split.el.setTop(box.y);
36716             this.split.el.setHeight(box.height);
36717         }
36718         if(this.collapsed){
36719             this.updateBody(null, box.height);
36720         }
36721         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36722     }
36723 });
36724 Roo.namespace("Roo.bootstrap.panel");/*
36725  * Based on:
36726  * Ext JS Library 1.1.1
36727  * Copyright(c) 2006-2007, Ext JS, LLC.
36728  *
36729  * Originally Released Under LGPL - original licence link has changed is not relivant.
36730  *
36731  * Fork - LGPL
36732  * <script type="text/javascript">
36733  */
36734 /**
36735  * @class Roo.ContentPanel
36736  * @extends Roo.util.Observable
36737  * A basic ContentPanel element.
36738  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36739  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36740  * @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
36741  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36742  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36743  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36744  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36745  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36746  * @cfg {String} title          The title for this panel
36747  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36748  * @cfg {String} url            Calls {@link #setUrl} with this value
36749  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36750  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36751  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36752  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36753  * @cfg {Boolean} badges render the badges
36754
36755  * @constructor
36756  * Create a new ContentPanel.
36757  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36758  * @param {String/Object} config A string to set only the title or a config object
36759  * @param {String} content (optional) Set the HTML content for this panel
36760  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36761  */
36762 Roo.bootstrap.panel.Content = function( config){
36763     
36764     this.tpl = config.tpl || false;
36765     
36766     var el = config.el;
36767     var content = config.content;
36768
36769     if(config.autoCreate){ // xtype is available if this is called from factory
36770         el = Roo.id();
36771     }
36772     this.el = Roo.get(el);
36773     if(!this.el && config && config.autoCreate){
36774         if(typeof config.autoCreate == "object"){
36775             if(!config.autoCreate.id){
36776                 config.autoCreate.id = config.id||el;
36777             }
36778             this.el = Roo.DomHelper.append(document.body,
36779                         config.autoCreate, true);
36780         }else{
36781             var elcfg =  {   tag: "div",
36782                             cls: "roo-layout-inactive-content",
36783                             id: config.id||el
36784                             };
36785             if (config.html) {
36786                 elcfg.html = config.html;
36787                 
36788             }
36789                         
36790             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36791         }
36792     } 
36793     this.closable = false;
36794     this.loaded = false;
36795     this.active = false;
36796    
36797       
36798     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36799         
36800         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36801         
36802         this.wrapEl = this.el; //this.el.wrap();
36803         var ti = [];
36804         if (config.toolbar.items) {
36805             ti = config.toolbar.items ;
36806             delete config.toolbar.items ;
36807         }
36808         
36809         var nitems = [];
36810         this.toolbar.render(this.wrapEl, 'before');
36811         for(var i =0;i < ti.length;i++) {
36812           //  Roo.log(['add child', items[i]]);
36813             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36814         }
36815         this.toolbar.items = nitems;
36816         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36817         delete config.toolbar;
36818         
36819     }
36820     /*
36821     // xtype created footer. - not sure if will work as we normally have to render first..
36822     if (this.footer && !this.footer.el && this.footer.xtype) {
36823         if (!this.wrapEl) {
36824             this.wrapEl = this.el.wrap();
36825         }
36826     
36827         this.footer.container = this.wrapEl.createChild();
36828          
36829         this.footer = Roo.factory(this.footer, Roo);
36830         
36831     }
36832     */
36833     
36834      if(typeof config == "string"){
36835         this.title = config;
36836     }else{
36837         Roo.apply(this, config);
36838     }
36839     
36840     if(this.resizeEl){
36841         this.resizeEl = Roo.get(this.resizeEl, true);
36842     }else{
36843         this.resizeEl = this.el;
36844     }
36845     // handle view.xtype
36846     
36847  
36848     
36849     
36850     this.addEvents({
36851         /**
36852          * @event activate
36853          * Fires when this panel is activated. 
36854          * @param {Roo.ContentPanel} this
36855          */
36856         "activate" : true,
36857         /**
36858          * @event deactivate
36859          * Fires when this panel is activated. 
36860          * @param {Roo.ContentPanel} this
36861          */
36862         "deactivate" : true,
36863
36864         /**
36865          * @event resize
36866          * Fires when this panel is resized if fitToFrame is true.
36867          * @param {Roo.ContentPanel} this
36868          * @param {Number} width The width after any component adjustments
36869          * @param {Number} height The height after any component adjustments
36870          */
36871         "resize" : true,
36872         
36873          /**
36874          * @event render
36875          * Fires when this tab is created
36876          * @param {Roo.ContentPanel} this
36877          */
36878         "render" : true
36879         
36880         
36881         
36882     });
36883     
36884
36885     
36886     
36887     if(this.autoScroll){
36888         this.resizeEl.setStyle("overflow", "auto");
36889     } else {
36890         // fix randome scrolling
36891         //this.el.on('scroll', function() {
36892         //    Roo.log('fix random scolling');
36893         //    this.scrollTo('top',0); 
36894         //});
36895     }
36896     content = content || this.content;
36897     if(content){
36898         this.setContent(content);
36899     }
36900     if(config && config.url){
36901         this.setUrl(this.url, this.params, this.loadOnce);
36902     }
36903     
36904     
36905     
36906     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36907     
36908     if (this.view && typeof(this.view.xtype) != 'undefined') {
36909         this.view.el = this.el.appendChild(document.createElement("div"));
36910         this.view = Roo.factory(this.view); 
36911         this.view.render  &&  this.view.render(false, '');  
36912     }
36913     
36914     
36915     this.fireEvent('render', this);
36916 };
36917
36918 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36919     
36920     tabTip : '',
36921     
36922     setRegion : function(region){
36923         this.region = region;
36924         this.setActiveClass(region && !this.background);
36925     },
36926     
36927     
36928     setActiveClass: function(state)
36929     {
36930         if(state){
36931            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36932            this.el.setStyle('position','relative');
36933         }else{
36934            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36935            this.el.setStyle('position', 'absolute');
36936         } 
36937     },
36938     
36939     /**
36940      * Returns the toolbar for this Panel if one was configured. 
36941      * @return {Roo.Toolbar} 
36942      */
36943     getToolbar : function(){
36944         return this.toolbar;
36945     },
36946     
36947     setActiveState : function(active)
36948     {
36949         this.active = active;
36950         this.setActiveClass(active);
36951         if(!active){
36952             if(this.fireEvent("deactivate", this) === false){
36953                 return false;
36954             }
36955             return true;
36956         }
36957         this.fireEvent("activate", this);
36958         return true;
36959     },
36960     /**
36961      * Updates this panel's element
36962      * @param {String} content The new content
36963      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36964     */
36965     setContent : function(content, loadScripts){
36966         this.el.update(content, loadScripts);
36967     },
36968
36969     ignoreResize : function(w, h){
36970         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36971             return true;
36972         }else{
36973             this.lastSize = {width: w, height: h};
36974             return false;
36975         }
36976     },
36977     /**
36978      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36979      * @return {Roo.UpdateManager} The UpdateManager
36980      */
36981     getUpdateManager : function(){
36982         return this.el.getUpdateManager();
36983     },
36984      /**
36985      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36986      * @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:
36987 <pre><code>
36988 panel.load({
36989     url: "your-url.php",
36990     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36991     callback: yourFunction,
36992     scope: yourObject, //(optional scope)
36993     discardUrl: false,
36994     nocache: false,
36995     text: "Loading...",
36996     timeout: 30,
36997     scripts: false
36998 });
36999 </code></pre>
37000      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37001      * 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.
37002      * @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}
37003      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37004      * @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.
37005      * @return {Roo.ContentPanel} this
37006      */
37007     load : function(){
37008         var um = this.el.getUpdateManager();
37009         um.update.apply(um, arguments);
37010         return this;
37011     },
37012
37013
37014     /**
37015      * 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.
37016      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37017      * @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)
37018      * @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)
37019      * @return {Roo.UpdateManager} The UpdateManager
37020      */
37021     setUrl : function(url, params, loadOnce){
37022         if(this.refreshDelegate){
37023             this.removeListener("activate", this.refreshDelegate);
37024         }
37025         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37026         this.on("activate", this.refreshDelegate);
37027         return this.el.getUpdateManager();
37028     },
37029     
37030     _handleRefresh : function(url, params, loadOnce){
37031         if(!loadOnce || !this.loaded){
37032             var updater = this.el.getUpdateManager();
37033             updater.update(url, params, this._setLoaded.createDelegate(this));
37034         }
37035     },
37036     
37037     _setLoaded : function(){
37038         this.loaded = true;
37039     }, 
37040     
37041     /**
37042      * Returns this panel's id
37043      * @return {String} 
37044      */
37045     getId : function(){
37046         return this.el.id;
37047     },
37048     
37049     /** 
37050      * Returns this panel's element - used by regiosn to add.
37051      * @return {Roo.Element} 
37052      */
37053     getEl : function(){
37054         return this.wrapEl || this.el;
37055     },
37056     
37057    
37058     
37059     adjustForComponents : function(width, height)
37060     {
37061         //Roo.log('adjustForComponents ');
37062         if(this.resizeEl != this.el){
37063             width -= this.el.getFrameWidth('lr');
37064             height -= this.el.getFrameWidth('tb');
37065         }
37066         if(this.toolbar){
37067             var te = this.toolbar.getEl();
37068             te.setWidth(width);
37069             height -= te.getHeight();
37070         }
37071         if(this.footer){
37072             var te = this.footer.getEl();
37073             te.setWidth(width);
37074             height -= te.getHeight();
37075         }
37076         
37077         
37078         if(this.adjustments){
37079             width += this.adjustments[0];
37080             height += this.adjustments[1];
37081         }
37082         return {"width": width, "height": height};
37083     },
37084     
37085     setSize : function(width, height){
37086         if(this.fitToFrame && !this.ignoreResize(width, height)){
37087             if(this.fitContainer && this.resizeEl != this.el){
37088                 this.el.setSize(width, height);
37089             }
37090             var size = this.adjustForComponents(width, height);
37091             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37092             this.fireEvent('resize', this, size.width, size.height);
37093         }
37094     },
37095     
37096     /**
37097      * Returns this panel's title
37098      * @return {String} 
37099      */
37100     getTitle : function(){
37101         
37102         if (typeof(this.title) != 'object') {
37103             return this.title;
37104         }
37105         
37106         var t = '';
37107         for (var k in this.title) {
37108             if (!this.title.hasOwnProperty(k)) {
37109                 continue;
37110             }
37111             
37112             if (k.indexOf('-') >= 0) {
37113                 var s = k.split('-');
37114                 for (var i = 0; i<s.length; i++) {
37115                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37116                 }
37117             } else {
37118                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37119             }
37120         }
37121         return t;
37122     },
37123     
37124     /**
37125      * Set this panel's title
37126      * @param {String} title
37127      */
37128     setTitle : function(title){
37129         this.title = title;
37130         if(this.region){
37131             this.region.updatePanelTitle(this, title);
37132         }
37133     },
37134     
37135     /**
37136      * Returns true is this panel was configured to be closable
37137      * @return {Boolean} 
37138      */
37139     isClosable : function(){
37140         return this.closable;
37141     },
37142     
37143     beforeSlide : function(){
37144         this.el.clip();
37145         this.resizeEl.clip();
37146     },
37147     
37148     afterSlide : function(){
37149         this.el.unclip();
37150         this.resizeEl.unclip();
37151     },
37152     
37153     /**
37154      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37155      *   Will fail silently if the {@link #setUrl} method has not been called.
37156      *   This does not activate the panel, just updates its content.
37157      */
37158     refresh : function(){
37159         if(this.refreshDelegate){
37160            this.loaded = false;
37161            this.refreshDelegate();
37162         }
37163     },
37164     
37165     /**
37166      * Destroys this panel
37167      */
37168     destroy : function(){
37169         this.el.removeAllListeners();
37170         var tempEl = document.createElement("span");
37171         tempEl.appendChild(this.el.dom);
37172         tempEl.innerHTML = "";
37173         this.el.remove();
37174         this.el = null;
37175     },
37176     
37177     /**
37178      * form - if the content panel contains a form - this is a reference to it.
37179      * @type {Roo.form.Form}
37180      */
37181     form : false,
37182     /**
37183      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37184      *    This contains a reference to it.
37185      * @type {Roo.View}
37186      */
37187     view : false,
37188     
37189       /**
37190      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37191      * <pre><code>
37192
37193 layout.addxtype({
37194        xtype : 'Form',
37195        items: [ .... ]
37196    }
37197 );
37198
37199 </code></pre>
37200      * @param {Object} cfg Xtype definition of item to add.
37201      */
37202     
37203     
37204     getChildContainer: function () {
37205         return this.getEl();
37206     }
37207     
37208     
37209     /*
37210         var  ret = new Roo.factory(cfg);
37211         return ret;
37212         
37213         
37214         // add form..
37215         if (cfg.xtype.match(/^Form$/)) {
37216             
37217             var el;
37218             //if (this.footer) {
37219             //    el = this.footer.container.insertSibling(false, 'before');
37220             //} else {
37221                 el = this.el.createChild();
37222             //}
37223
37224             this.form = new  Roo.form.Form(cfg);
37225             
37226             
37227             if ( this.form.allItems.length) {
37228                 this.form.render(el.dom);
37229             }
37230             return this.form;
37231         }
37232         // should only have one of theses..
37233         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37234             // views.. should not be just added - used named prop 'view''
37235             
37236             cfg.el = this.el.appendChild(document.createElement("div"));
37237             // factory?
37238             
37239             var ret = new Roo.factory(cfg);
37240              
37241              ret.render && ret.render(false, ''); // render blank..
37242             this.view = ret;
37243             return ret;
37244         }
37245         return false;
37246     }
37247     \*/
37248 });
37249  
37250 /**
37251  * @class Roo.bootstrap.panel.Grid
37252  * @extends Roo.bootstrap.panel.Content
37253  * @constructor
37254  * Create a new GridPanel.
37255  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37256  * @param {Object} config A the config object
37257   
37258  */
37259
37260
37261
37262 Roo.bootstrap.panel.Grid = function(config)
37263 {
37264     
37265       
37266     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37267         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37268
37269     config.el = this.wrapper;
37270     //this.el = this.wrapper;
37271     
37272       if (config.container) {
37273         // ctor'ed from a Border/panel.grid
37274         
37275         
37276         this.wrapper.setStyle("overflow", "hidden");
37277         this.wrapper.addClass('roo-grid-container');
37278
37279     }
37280     
37281     
37282     if(config.toolbar){
37283         var tool_el = this.wrapper.createChild();    
37284         this.toolbar = Roo.factory(config.toolbar);
37285         var ti = [];
37286         if (config.toolbar.items) {
37287             ti = config.toolbar.items ;
37288             delete config.toolbar.items ;
37289         }
37290         
37291         var nitems = [];
37292         this.toolbar.render(tool_el);
37293         for(var i =0;i < ti.length;i++) {
37294           //  Roo.log(['add child', items[i]]);
37295             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37296         }
37297         this.toolbar.items = nitems;
37298         
37299         delete config.toolbar;
37300     }
37301     
37302     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37303     config.grid.scrollBody = true;;
37304     config.grid.monitorWindowResize = false; // turn off autosizing
37305     config.grid.autoHeight = false;
37306     config.grid.autoWidth = false;
37307     
37308     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37309     
37310     if (config.background) {
37311         // render grid on panel activation (if panel background)
37312         this.on('activate', function(gp) {
37313             if (!gp.grid.rendered) {
37314                 gp.grid.render(this.wrapper);
37315                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37316             }
37317         });
37318             
37319     } else {
37320         this.grid.render(this.wrapper);
37321         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37322
37323     }
37324     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37325     // ??? needed ??? config.el = this.wrapper;
37326     
37327     
37328     
37329   
37330     // xtype created footer. - not sure if will work as we normally have to render first..
37331     if (this.footer && !this.footer.el && this.footer.xtype) {
37332         
37333         var ctr = this.grid.getView().getFooterPanel(true);
37334         this.footer.dataSource = this.grid.dataSource;
37335         this.footer = Roo.factory(this.footer, Roo);
37336         this.footer.render(ctr);
37337         
37338     }
37339     
37340     
37341     
37342     
37343      
37344 };
37345
37346 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37347     getId : function(){
37348         return this.grid.id;
37349     },
37350     
37351     /**
37352      * Returns the grid for this panel
37353      * @return {Roo.bootstrap.Table} 
37354      */
37355     getGrid : function(){
37356         return this.grid;    
37357     },
37358     
37359     setSize : function(width, height){
37360         if(!this.ignoreResize(width, height)){
37361             var grid = this.grid;
37362             var size = this.adjustForComponents(width, height);
37363             var gridel = grid.getGridEl();
37364             gridel.setSize(size.width, size.height);
37365             /*
37366             var thd = grid.getGridEl().select('thead',true).first();
37367             var tbd = grid.getGridEl().select('tbody', true).first();
37368             if (tbd) {
37369                 tbd.setSize(width, height - thd.getHeight());
37370             }
37371             */
37372             grid.autoSize();
37373         }
37374     },
37375      
37376     
37377     
37378     beforeSlide : function(){
37379         this.grid.getView().scroller.clip();
37380     },
37381     
37382     afterSlide : function(){
37383         this.grid.getView().scroller.unclip();
37384     },
37385     
37386     destroy : function(){
37387         this.grid.destroy();
37388         delete this.grid;
37389         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37390     }
37391 });
37392
37393 /**
37394  * @class Roo.bootstrap.panel.Nest
37395  * @extends Roo.bootstrap.panel.Content
37396  * @constructor
37397  * Create a new Panel, that can contain a layout.Border.
37398  * 
37399  * 
37400  * @param {Roo.BorderLayout} layout The layout for this panel
37401  * @param {String/Object} config A string to set only the title or a config object
37402  */
37403 Roo.bootstrap.panel.Nest = function(config)
37404 {
37405     // construct with only one argument..
37406     /* FIXME - implement nicer consturctors
37407     if (layout.layout) {
37408         config = layout;
37409         layout = config.layout;
37410         delete config.layout;
37411     }
37412     if (layout.xtype && !layout.getEl) {
37413         // then layout needs constructing..
37414         layout = Roo.factory(layout, Roo);
37415     }
37416     */
37417     
37418     config.el =  config.layout.getEl();
37419     
37420     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37421     
37422     config.layout.monitorWindowResize = false; // turn off autosizing
37423     this.layout = config.layout;
37424     this.layout.getEl().addClass("roo-layout-nested-layout");
37425     
37426     
37427     
37428     
37429 };
37430
37431 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37432
37433     setSize : function(width, height){
37434         if(!this.ignoreResize(width, height)){
37435             var size = this.adjustForComponents(width, height);
37436             var el = this.layout.getEl();
37437             if (size.height < 1) {
37438                 el.setWidth(size.width);   
37439             } else {
37440                 el.setSize(size.width, size.height);
37441             }
37442             var touch = el.dom.offsetWidth;
37443             this.layout.layout();
37444             // ie requires a double layout on the first pass
37445             if(Roo.isIE && !this.initialized){
37446                 this.initialized = true;
37447                 this.layout.layout();
37448             }
37449         }
37450     },
37451     
37452     // activate all subpanels if not currently active..
37453     
37454     setActiveState : function(active){
37455         this.active = active;
37456         this.setActiveClass(active);
37457         
37458         if(!active){
37459             this.fireEvent("deactivate", this);
37460             return;
37461         }
37462         
37463         this.fireEvent("activate", this);
37464         // not sure if this should happen before or after..
37465         if (!this.layout) {
37466             return; // should not happen..
37467         }
37468         var reg = false;
37469         for (var r in this.layout.regions) {
37470             reg = this.layout.getRegion(r);
37471             if (reg.getActivePanel()) {
37472                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37473                 reg.setActivePanel(reg.getActivePanel());
37474                 continue;
37475             }
37476             if (!reg.panels.length) {
37477                 continue;
37478             }
37479             reg.showPanel(reg.getPanel(0));
37480         }
37481         
37482         
37483         
37484         
37485     },
37486     
37487     /**
37488      * Returns the nested BorderLayout for this panel
37489      * @return {Roo.BorderLayout} 
37490      */
37491     getLayout : function(){
37492         return this.layout;
37493     },
37494     
37495      /**
37496      * Adds a xtype elements to the layout of the nested panel
37497      * <pre><code>
37498
37499 panel.addxtype({
37500        xtype : 'ContentPanel',
37501        region: 'west',
37502        items: [ .... ]
37503    }
37504 );
37505
37506 panel.addxtype({
37507         xtype : 'NestedLayoutPanel',
37508         region: 'west',
37509         layout: {
37510            center: { },
37511            west: { }   
37512         },
37513         items : [ ... list of content panels or nested layout panels.. ]
37514    }
37515 );
37516 </code></pre>
37517      * @param {Object} cfg Xtype definition of item to add.
37518      */
37519     addxtype : function(cfg) {
37520         return this.layout.addxtype(cfg);
37521     
37522     }
37523 });        /*
37524  * Based on:
37525  * Ext JS Library 1.1.1
37526  * Copyright(c) 2006-2007, Ext JS, LLC.
37527  *
37528  * Originally Released Under LGPL - original licence link has changed is not relivant.
37529  *
37530  * Fork - LGPL
37531  * <script type="text/javascript">
37532  */
37533 /**
37534  * @class Roo.TabPanel
37535  * @extends Roo.util.Observable
37536  * A lightweight tab container.
37537  * <br><br>
37538  * Usage:
37539  * <pre><code>
37540 // basic tabs 1, built from existing content
37541 var tabs = new Roo.TabPanel("tabs1");
37542 tabs.addTab("script", "View Script");
37543 tabs.addTab("markup", "View Markup");
37544 tabs.activate("script");
37545
37546 // more advanced tabs, built from javascript
37547 var jtabs = new Roo.TabPanel("jtabs");
37548 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37549
37550 // set up the UpdateManager
37551 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37552 var updater = tab2.getUpdateManager();
37553 updater.setDefaultUrl("ajax1.htm");
37554 tab2.on('activate', updater.refresh, updater, true);
37555
37556 // Use setUrl for Ajax loading
37557 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37558 tab3.setUrl("ajax2.htm", null, true);
37559
37560 // Disabled tab
37561 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37562 tab4.disable();
37563
37564 jtabs.activate("jtabs-1");
37565  * </code></pre>
37566  * @constructor
37567  * Create a new TabPanel.
37568  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37569  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37570  */
37571 Roo.bootstrap.panel.Tabs = function(config){
37572     /**
37573     * The container element for this TabPanel.
37574     * @type Roo.Element
37575     */
37576     this.el = Roo.get(config.el);
37577     delete config.el;
37578     if(config){
37579         if(typeof config == "boolean"){
37580             this.tabPosition = config ? "bottom" : "top";
37581         }else{
37582             Roo.apply(this, config);
37583         }
37584     }
37585     
37586     if(this.tabPosition == "bottom"){
37587         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37588         this.el.addClass("roo-tabs-bottom");
37589     }
37590     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37591     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37592     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37593     if(Roo.isIE){
37594         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37595     }
37596     if(this.tabPosition != "bottom"){
37597         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37598          * @type Roo.Element
37599          */
37600         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37601         this.el.addClass("roo-tabs-top");
37602     }
37603     this.items = [];
37604
37605     this.bodyEl.setStyle("position", "relative");
37606
37607     this.active = null;
37608     this.activateDelegate = this.activate.createDelegate(this);
37609
37610     this.addEvents({
37611         /**
37612          * @event tabchange
37613          * Fires when the active tab changes
37614          * @param {Roo.TabPanel} this
37615          * @param {Roo.TabPanelItem} activePanel The new active tab
37616          */
37617         "tabchange": true,
37618         /**
37619          * @event beforetabchange
37620          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37621          * @param {Roo.TabPanel} this
37622          * @param {Object} e Set cancel to true on this object to cancel the tab change
37623          * @param {Roo.TabPanelItem} tab The tab being changed to
37624          */
37625         "beforetabchange" : true
37626     });
37627
37628     Roo.EventManager.onWindowResize(this.onResize, this);
37629     this.cpad = this.el.getPadding("lr");
37630     this.hiddenCount = 0;
37631
37632
37633     // toolbar on the tabbar support...
37634     if (this.toolbar) {
37635         alert("no toolbar support yet");
37636         this.toolbar  = false;
37637         /*
37638         var tcfg = this.toolbar;
37639         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37640         this.toolbar = new Roo.Toolbar(tcfg);
37641         if (Roo.isSafari) {
37642             var tbl = tcfg.container.child('table', true);
37643             tbl.setAttribute('width', '100%');
37644         }
37645         */
37646         
37647     }
37648    
37649
37650
37651     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37652 };
37653
37654 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37655     /*
37656      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37657      */
37658     tabPosition : "top",
37659     /*
37660      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37661      */
37662     currentTabWidth : 0,
37663     /*
37664      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37665      */
37666     minTabWidth : 40,
37667     /*
37668      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37669      */
37670     maxTabWidth : 250,
37671     /*
37672      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37673      */
37674     preferredTabWidth : 175,
37675     /*
37676      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37677      */
37678     resizeTabs : false,
37679     /*
37680      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37681      */
37682     monitorResize : true,
37683     /*
37684      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37685      */
37686     toolbar : false,
37687
37688     /**
37689      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37690      * @param {String} id The id of the div to use <b>or create</b>
37691      * @param {String} text The text for the tab
37692      * @param {String} content (optional) Content to put in the TabPanelItem body
37693      * @param {Boolean} closable (optional) True to create a close icon on the tab
37694      * @return {Roo.TabPanelItem} The created TabPanelItem
37695      */
37696     addTab : function(id, text, content, closable, tpl)
37697     {
37698         var item = new Roo.bootstrap.panel.TabItem({
37699             panel: this,
37700             id : id,
37701             text : text,
37702             closable : closable,
37703             tpl : tpl
37704         });
37705         this.addTabItem(item);
37706         if(content){
37707             item.setContent(content);
37708         }
37709         return item;
37710     },
37711
37712     /**
37713      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37714      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37715      * @return {Roo.TabPanelItem}
37716      */
37717     getTab : function(id){
37718         return this.items[id];
37719     },
37720
37721     /**
37722      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37723      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37724      */
37725     hideTab : function(id){
37726         var t = this.items[id];
37727         if(!t.isHidden()){
37728            t.setHidden(true);
37729            this.hiddenCount++;
37730            this.autoSizeTabs();
37731         }
37732     },
37733
37734     /**
37735      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37736      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37737      */
37738     unhideTab : function(id){
37739         var t = this.items[id];
37740         if(t.isHidden()){
37741            t.setHidden(false);
37742            this.hiddenCount--;
37743            this.autoSizeTabs();
37744         }
37745     },
37746
37747     /**
37748      * Adds an existing {@link Roo.TabPanelItem}.
37749      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37750      */
37751     addTabItem : function(item){
37752         this.items[item.id] = item;
37753         this.items.push(item);
37754       //  if(this.resizeTabs){
37755     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37756   //         this.autoSizeTabs();
37757 //        }else{
37758 //            item.autoSize();
37759        // }
37760     },
37761
37762     /**
37763      * Removes a {@link Roo.TabPanelItem}.
37764      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37765      */
37766     removeTab : function(id){
37767         var items = this.items;
37768         var tab = items[id];
37769         if(!tab) { return; }
37770         var index = items.indexOf(tab);
37771         if(this.active == tab && items.length > 1){
37772             var newTab = this.getNextAvailable(index);
37773             if(newTab) {
37774                 newTab.activate();
37775             }
37776         }
37777         this.stripEl.dom.removeChild(tab.pnode.dom);
37778         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37779             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37780         }
37781         items.splice(index, 1);
37782         delete this.items[tab.id];
37783         tab.fireEvent("close", tab);
37784         tab.purgeListeners();
37785         this.autoSizeTabs();
37786     },
37787
37788     getNextAvailable : function(start){
37789         var items = this.items;
37790         var index = start;
37791         // look for a next tab that will slide over to
37792         // replace the one being removed
37793         while(index < items.length){
37794             var item = items[++index];
37795             if(item && !item.isHidden()){
37796                 return item;
37797             }
37798         }
37799         // if one isn't found select the previous tab (on the left)
37800         index = start;
37801         while(index >= 0){
37802             var item = items[--index];
37803             if(item && !item.isHidden()){
37804                 return item;
37805             }
37806         }
37807         return null;
37808     },
37809
37810     /**
37811      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37812      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37813      */
37814     disableTab : function(id){
37815         var tab = this.items[id];
37816         if(tab && this.active != tab){
37817             tab.disable();
37818         }
37819     },
37820
37821     /**
37822      * Enables a {@link Roo.TabPanelItem} that is disabled.
37823      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37824      */
37825     enableTab : function(id){
37826         var tab = this.items[id];
37827         tab.enable();
37828     },
37829
37830     /**
37831      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37832      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37833      * @return {Roo.TabPanelItem} The TabPanelItem.
37834      */
37835     activate : function(id){
37836         var tab = this.items[id];
37837         if(!tab){
37838             return null;
37839         }
37840         if(tab == this.active || tab.disabled){
37841             return tab;
37842         }
37843         var e = {};
37844         this.fireEvent("beforetabchange", this, e, tab);
37845         if(e.cancel !== true && !tab.disabled){
37846             if(this.active){
37847                 this.active.hide();
37848             }
37849             this.active = this.items[id];
37850             this.active.show();
37851             this.fireEvent("tabchange", this, this.active);
37852         }
37853         return tab;
37854     },
37855
37856     /**
37857      * Gets the active {@link Roo.TabPanelItem}.
37858      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37859      */
37860     getActiveTab : function(){
37861         return this.active;
37862     },
37863
37864     /**
37865      * Updates the tab body element to fit the height of the container element
37866      * for overflow scrolling
37867      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37868      */
37869     syncHeight : function(targetHeight){
37870         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37871         var bm = this.bodyEl.getMargins();
37872         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37873         this.bodyEl.setHeight(newHeight);
37874         return newHeight;
37875     },
37876
37877     onResize : function(){
37878         if(this.monitorResize){
37879             this.autoSizeTabs();
37880         }
37881     },
37882
37883     /**
37884      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37885      */
37886     beginUpdate : function(){
37887         this.updating = true;
37888     },
37889
37890     /**
37891      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37892      */
37893     endUpdate : function(){
37894         this.updating = false;
37895         this.autoSizeTabs();
37896     },
37897
37898     /**
37899      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37900      */
37901     autoSizeTabs : function(){
37902         var count = this.items.length;
37903         var vcount = count - this.hiddenCount;
37904         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37905             return;
37906         }
37907         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37908         var availWidth = Math.floor(w / vcount);
37909         var b = this.stripBody;
37910         if(b.getWidth() > w){
37911             var tabs = this.items;
37912             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37913             if(availWidth < this.minTabWidth){
37914                 /*if(!this.sleft){    // incomplete scrolling code
37915                     this.createScrollButtons();
37916                 }
37917                 this.showScroll();
37918                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37919             }
37920         }else{
37921             if(this.currentTabWidth < this.preferredTabWidth){
37922                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37923             }
37924         }
37925     },
37926
37927     /**
37928      * Returns the number of tabs in this TabPanel.
37929      * @return {Number}
37930      */
37931      getCount : function(){
37932          return this.items.length;
37933      },
37934
37935     /**
37936      * Resizes all the tabs to the passed width
37937      * @param {Number} The new width
37938      */
37939     setTabWidth : function(width){
37940         this.currentTabWidth = width;
37941         for(var i = 0, len = this.items.length; i < len; i++) {
37942                 if(!this.items[i].isHidden()) {
37943                 this.items[i].setWidth(width);
37944             }
37945         }
37946     },
37947
37948     /**
37949      * Destroys this TabPanel
37950      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37951      */
37952     destroy : function(removeEl){
37953         Roo.EventManager.removeResizeListener(this.onResize, this);
37954         for(var i = 0, len = this.items.length; i < len; i++){
37955             this.items[i].purgeListeners();
37956         }
37957         if(removeEl === true){
37958             this.el.update("");
37959             this.el.remove();
37960         }
37961     },
37962     
37963     createStrip : function(container)
37964     {
37965         var strip = document.createElement("nav");
37966         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37967         container.appendChild(strip);
37968         return strip;
37969     },
37970     
37971     createStripList : function(strip)
37972     {
37973         // div wrapper for retard IE
37974         // returns the "tr" element.
37975         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37976         //'<div class="x-tabs-strip-wrap">'+
37977           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37978           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37979         return strip.firstChild; //.firstChild.firstChild.firstChild;
37980     },
37981     createBody : function(container)
37982     {
37983         var body = document.createElement("div");
37984         Roo.id(body, "tab-body");
37985         //Roo.fly(body).addClass("x-tabs-body");
37986         Roo.fly(body).addClass("tab-content");
37987         container.appendChild(body);
37988         return body;
37989     },
37990     createItemBody :function(bodyEl, id){
37991         var body = Roo.getDom(id);
37992         if(!body){
37993             body = document.createElement("div");
37994             body.id = id;
37995         }
37996         //Roo.fly(body).addClass("x-tabs-item-body");
37997         Roo.fly(body).addClass("tab-pane");
37998          bodyEl.insertBefore(body, bodyEl.firstChild);
37999         return body;
38000     },
38001     /** @private */
38002     createStripElements :  function(stripEl, text, closable, tpl)
38003     {
38004         var td = document.createElement("li"); // was td..
38005         
38006         
38007         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38008         
38009         
38010         stripEl.appendChild(td);
38011         /*if(closable){
38012             td.className = "x-tabs-closable";
38013             if(!this.closeTpl){
38014                 this.closeTpl = new Roo.Template(
38015                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38016                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38017                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38018                 );
38019             }
38020             var el = this.closeTpl.overwrite(td, {"text": text});
38021             var close = el.getElementsByTagName("div")[0];
38022             var inner = el.getElementsByTagName("em")[0];
38023             return {"el": el, "close": close, "inner": inner};
38024         } else {
38025         */
38026         // not sure what this is..
38027 //            if(!this.tabTpl){
38028                 //this.tabTpl = new Roo.Template(
38029                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38030                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38031                 //);
38032 //                this.tabTpl = new Roo.Template(
38033 //                   '<a href="#">' +
38034 //                   '<span unselectable="on"' +
38035 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38036 //                            ' >{text}</span></a>'
38037 //                );
38038 //                
38039 //            }
38040
38041
38042             var template = tpl || this.tabTpl || false;
38043             
38044             if(!template){
38045                 
38046                 template = new Roo.Template(
38047                    '<a href="#">' +
38048                    '<span unselectable="on"' +
38049                             (this.disableTooltips ? '' : ' title="{text}"') +
38050                             ' >{text}</span></a>'
38051                 );
38052             }
38053             
38054             switch (typeof(template)) {
38055                 case 'object' :
38056                     break;
38057                 case 'string' :
38058                     template = new Roo.Template(template);
38059                     break;
38060                 default :
38061                     break;
38062             }
38063             
38064             var el = template.overwrite(td, {"text": text});
38065             
38066             var inner = el.getElementsByTagName("span")[0];
38067             
38068             return {"el": el, "inner": inner};
38069             
38070     }
38071         
38072     
38073 });
38074
38075 /**
38076  * @class Roo.TabPanelItem
38077  * @extends Roo.util.Observable
38078  * Represents an individual item (tab plus body) in a TabPanel.
38079  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38080  * @param {String} id The id of this TabPanelItem
38081  * @param {String} text The text for the tab of this TabPanelItem
38082  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38083  */
38084 Roo.bootstrap.panel.TabItem = function(config){
38085     /**
38086      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38087      * @type Roo.TabPanel
38088      */
38089     this.tabPanel = config.panel;
38090     /**
38091      * The id for this TabPanelItem
38092      * @type String
38093      */
38094     this.id = config.id;
38095     /** @private */
38096     this.disabled = false;
38097     /** @private */
38098     this.text = config.text;
38099     /** @private */
38100     this.loaded = false;
38101     this.closable = config.closable;
38102
38103     /**
38104      * The body element for this TabPanelItem.
38105      * @type Roo.Element
38106      */
38107     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38108     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38109     this.bodyEl.setStyle("display", "block");
38110     this.bodyEl.setStyle("zoom", "1");
38111     //this.hideAction();
38112
38113     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38114     /** @private */
38115     this.el = Roo.get(els.el);
38116     this.inner = Roo.get(els.inner, true);
38117     this.textEl = Roo.get(this.el.dom.firstChild, true);
38118     this.pnode = Roo.get(els.el.parentNode, true);
38119 //    this.el.on("mousedown", this.onTabMouseDown, this);
38120     this.el.on("click", this.onTabClick, this);
38121     /** @private */
38122     if(config.closable){
38123         var c = Roo.get(els.close, true);
38124         c.dom.title = this.closeText;
38125         c.addClassOnOver("close-over");
38126         c.on("click", this.closeClick, this);
38127      }
38128
38129     this.addEvents({
38130          /**
38131          * @event activate
38132          * Fires when this tab becomes the active tab.
38133          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38134          * @param {Roo.TabPanelItem} this
38135          */
38136         "activate": true,
38137         /**
38138          * @event beforeclose
38139          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38140          * @param {Roo.TabPanelItem} this
38141          * @param {Object} e Set cancel to true on this object to cancel the close.
38142          */
38143         "beforeclose": true,
38144         /**
38145          * @event close
38146          * Fires when this tab is closed.
38147          * @param {Roo.TabPanelItem} this
38148          */
38149          "close": true,
38150         /**
38151          * @event deactivate
38152          * Fires when this tab is no longer the active tab.
38153          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38154          * @param {Roo.TabPanelItem} this
38155          */
38156          "deactivate" : true
38157     });
38158     this.hidden = false;
38159
38160     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38161 };
38162
38163 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38164            {
38165     purgeListeners : function(){
38166        Roo.util.Observable.prototype.purgeListeners.call(this);
38167        this.el.removeAllListeners();
38168     },
38169     /**
38170      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38171      */
38172     show : function(){
38173         this.pnode.addClass("active");
38174         this.showAction();
38175         if(Roo.isOpera){
38176             this.tabPanel.stripWrap.repaint();
38177         }
38178         this.fireEvent("activate", this.tabPanel, this);
38179     },
38180
38181     /**
38182      * Returns true if this tab is the active tab.
38183      * @return {Boolean}
38184      */
38185     isActive : function(){
38186         return this.tabPanel.getActiveTab() == this;
38187     },
38188
38189     /**
38190      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38191      */
38192     hide : function(){
38193         this.pnode.removeClass("active");
38194         this.hideAction();
38195         this.fireEvent("deactivate", this.tabPanel, this);
38196     },
38197
38198     hideAction : function(){
38199         this.bodyEl.hide();
38200         this.bodyEl.setStyle("position", "absolute");
38201         this.bodyEl.setLeft("-20000px");
38202         this.bodyEl.setTop("-20000px");
38203     },
38204
38205     showAction : function(){
38206         this.bodyEl.setStyle("position", "relative");
38207         this.bodyEl.setTop("");
38208         this.bodyEl.setLeft("");
38209         this.bodyEl.show();
38210     },
38211
38212     /**
38213      * Set the tooltip for the tab.
38214      * @param {String} tooltip The tab's tooltip
38215      */
38216     setTooltip : function(text){
38217         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38218             this.textEl.dom.qtip = text;
38219             this.textEl.dom.removeAttribute('title');
38220         }else{
38221             this.textEl.dom.title = text;
38222         }
38223     },
38224
38225     onTabClick : function(e){
38226         e.preventDefault();
38227         this.tabPanel.activate(this.id);
38228     },
38229
38230     onTabMouseDown : function(e){
38231         e.preventDefault();
38232         this.tabPanel.activate(this.id);
38233     },
38234 /*
38235     getWidth : function(){
38236         return this.inner.getWidth();
38237     },
38238
38239     setWidth : function(width){
38240         var iwidth = width - this.pnode.getPadding("lr");
38241         this.inner.setWidth(iwidth);
38242         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38243         this.pnode.setWidth(width);
38244     },
38245 */
38246     /**
38247      * Show or hide the tab
38248      * @param {Boolean} hidden True to hide or false to show.
38249      */
38250     setHidden : function(hidden){
38251         this.hidden = hidden;
38252         this.pnode.setStyle("display", hidden ? "none" : "");
38253     },
38254
38255     /**
38256      * Returns true if this tab is "hidden"
38257      * @return {Boolean}
38258      */
38259     isHidden : function(){
38260         return this.hidden;
38261     },
38262
38263     /**
38264      * Returns the text for this tab
38265      * @return {String}
38266      */
38267     getText : function(){
38268         return this.text;
38269     },
38270     /*
38271     autoSize : function(){
38272         //this.el.beginMeasure();
38273         this.textEl.setWidth(1);
38274         /*
38275          *  #2804 [new] Tabs in Roojs
38276          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38277          */
38278         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38279         //this.el.endMeasure();
38280     //},
38281
38282     /**
38283      * Sets the text for the tab (Note: this also sets the tooltip text)
38284      * @param {String} text The tab's text and tooltip
38285      */
38286     setText : function(text){
38287         this.text = text;
38288         this.textEl.update(text);
38289         this.setTooltip(text);
38290         //if(!this.tabPanel.resizeTabs){
38291         //    this.autoSize();
38292         //}
38293     },
38294     /**
38295      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38296      */
38297     activate : function(){
38298         this.tabPanel.activate(this.id);
38299     },
38300
38301     /**
38302      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38303      */
38304     disable : function(){
38305         if(this.tabPanel.active != this){
38306             this.disabled = true;
38307             this.pnode.addClass("disabled");
38308         }
38309     },
38310
38311     /**
38312      * Enables this TabPanelItem if it was previously disabled.
38313      */
38314     enable : function(){
38315         this.disabled = false;
38316         this.pnode.removeClass("disabled");
38317     },
38318
38319     /**
38320      * Sets the content for this TabPanelItem.
38321      * @param {String} content The content
38322      * @param {Boolean} loadScripts true to look for and load scripts
38323      */
38324     setContent : function(content, loadScripts){
38325         this.bodyEl.update(content, loadScripts);
38326     },
38327
38328     /**
38329      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38330      * @return {Roo.UpdateManager} The UpdateManager
38331      */
38332     getUpdateManager : function(){
38333         return this.bodyEl.getUpdateManager();
38334     },
38335
38336     /**
38337      * Set a URL to be used to load the content for this TabPanelItem.
38338      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38339      * @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)
38340      * @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)
38341      * @return {Roo.UpdateManager} The UpdateManager
38342      */
38343     setUrl : function(url, params, loadOnce){
38344         if(this.refreshDelegate){
38345             this.un('activate', this.refreshDelegate);
38346         }
38347         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38348         this.on("activate", this.refreshDelegate);
38349         return this.bodyEl.getUpdateManager();
38350     },
38351
38352     /** @private */
38353     _handleRefresh : function(url, params, loadOnce){
38354         if(!loadOnce || !this.loaded){
38355             var updater = this.bodyEl.getUpdateManager();
38356             updater.update(url, params, this._setLoaded.createDelegate(this));
38357         }
38358     },
38359
38360     /**
38361      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38362      *   Will fail silently if the setUrl method has not been called.
38363      *   This does not activate the panel, just updates its content.
38364      */
38365     refresh : function(){
38366         if(this.refreshDelegate){
38367            this.loaded = false;
38368            this.refreshDelegate();
38369         }
38370     },
38371
38372     /** @private */
38373     _setLoaded : function(){
38374         this.loaded = true;
38375     },
38376
38377     /** @private */
38378     closeClick : function(e){
38379         var o = {};
38380         e.stopEvent();
38381         this.fireEvent("beforeclose", this, o);
38382         if(o.cancel !== true){
38383             this.tabPanel.removeTab(this.id);
38384         }
38385     },
38386     /**
38387      * The text displayed in the tooltip for the close icon.
38388      * @type String
38389      */
38390     closeText : "Close this tab"
38391 });
38392 /**
38393 *    This script refer to:
38394 *    Title: International Telephone Input
38395 *    Author: Jack O'Connor
38396 *    Code version:  v12.1.12
38397 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38398 **/
38399
38400 Roo.bootstrap.PhoneInputData = function() {
38401     var d = [
38402       [
38403         "Afghanistan (‫افغانستان‬‎)",
38404         "af",
38405         "93"
38406       ],
38407       [
38408         "Albania (Shqipëri)",
38409         "al",
38410         "355"
38411       ],
38412       [
38413         "Algeria (‫الجزائر‬‎)",
38414         "dz",
38415         "213"
38416       ],
38417       [
38418         "American Samoa",
38419         "as",
38420         "1684"
38421       ],
38422       [
38423         "Andorra",
38424         "ad",
38425         "376"
38426       ],
38427       [
38428         "Angola",
38429         "ao",
38430         "244"
38431       ],
38432       [
38433         "Anguilla",
38434         "ai",
38435         "1264"
38436       ],
38437       [
38438         "Antigua and Barbuda",
38439         "ag",
38440         "1268"
38441       ],
38442       [
38443         "Argentina",
38444         "ar",
38445         "54"
38446       ],
38447       [
38448         "Armenia (Հայաստան)",
38449         "am",
38450         "374"
38451       ],
38452       [
38453         "Aruba",
38454         "aw",
38455         "297"
38456       ],
38457       [
38458         "Australia",
38459         "au",
38460         "61",
38461         0
38462       ],
38463       [
38464         "Austria (Österreich)",
38465         "at",
38466         "43"
38467       ],
38468       [
38469         "Azerbaijan (Azərbaycan)",
38470         "az",
38471         "994"
38472       ],
38473       [
38474         "Bahamas",
38475         "bs",
38476         "1242"
38477       ],
38478       [
38479         "Bahrain (‫البحرين‬‎)",
38480         "bh",
38481         "973"
38482       ],
38483       [
38484         "Bangladesh (বাংলাদেশ)",
38485         "bd",
38486         "880"
38487       ],
38488       [
38489         "Barbados",
38490         "bb",
38491         "1246"
38492       ],
38493       [
38494         "Belarus (Беларусь)",
38495         "by",
38496         "375"
38497       ],
38498       [
38499         "Belgium (België)",
38500         "be",
38501         "32"
38502       ],
38503       [
38504         "Belize",
38505         "bz",
38506         "501"
38507       ],
38508       [
38509         "Benin (Bénin)",
38510         "bj",
38511         "229"
38512       ],
38513       [
38514         "Bermuda",
38515         "bm",
38516         "1441"
38517       ],
38518       [
38519         "Bhutan (འབྲུག)",
38520         "bt",
38521         "975"
38522       ],
38523       [
38524         "Bolivia",
38525         "bo",
38526         "591"
38527       ],
38528       [
38529         "Bosnia and Herzegovina (Босна и Херцеговина)",
38530         "ba",
38531         "387"
38532       ],
38533       [
38534         "Botswana",
38535         "bw",
38536         "267"
38537       ],
38538       [
38539         "Brazil (Brasil)",
38540         "br",
38541         "55"
38542       ],
38543       [
38544         "British Indian Ocean Territory",
38545         "io",
38546         "246"
38547       ],
38548       [
38549         "British Virgin Islands",
38550         "vg",
38551         "1284"
38552       ],
38553       [
38554         "Brunei",
38555         "bn",
38556         "673"
38557       ],
38558       [
38559         "Bulgaria (България)",
38560         "bg",
38561         "359"
38562       ],
38563       [
38564         "Burkina Faso",
38565         "bf",
38566         "226"
38567       ],
38568       [
38569         "Burundi (Uburundi)",
38570         "bi",
38571         "257"
38572       ],
38573       [
38574         "Cambodia (កម្ពុជា)",
38575         "kh",
38576         "855"
38577       ],
38578       [
38579         "Cameroon (Cameroun)",
38580         "cm",
38581         "237"
38582       ],
38583       [
38584         "Canada",
38585         "ca",
38586         "1",
38587         1,
38588         ["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"]
38589       ],
38590       [
38591         "Cape Verde (Kabu Verdi)",
38592         "cv",
38593         "238"
38594       ],
38595       [
38596         "Caribbean Netherlands",
38597         "bq",
38598         "599",
38599         1
38600       ],
38601       [
38602         "Cayman Islands",
38603         "ky",
38604         "1345"
38605       ],
38606       [
38607         "Central African Republic (République centrafricaine)",
38608         "cf",
38609         "236"
38610       ],
38611       [
38612         "Chad (Tchad)",
38613         "td",
38614         "235"
38615       ],
38616       [
38617         "Chile",
38618         "cl",
38619         "56"
38620       ],
38621       [
38622         "China (中国)",
38623         "cn",
38624         "86"
38625       ],
38626       [
38627         "Christmas Island",
38628         "cx",
38629         "61",
38630         2
38631       ],
38632       [
38633         "Cocos (Keeling) Islands",
38634         "cc",
38635         "61",
38636         1
38637       ],
38638       [
38639         "Colombia",
38640         "co",
38641         "57"
38642       ],
38643       [
38644         "Comoros (‫جزر القمر‬‎)",
38645         "km",
38646         "269"
38647       ],
38648       [
38649         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38650         "cd",
38651         "243"
38652       ],
38653       [
38654         "Congo (Republic) (Congo-Brazzaville)",
38655         "cg",
38656         "242"
38657       ],
38658       [
38659         "Cook Islands",
38660         "ck",
38661         "682"
38662       ],
38663       [
38664         "Costa Rica",
38665         "cr",
38666         "506"
38667       ],
38668       [
38669         "Côte d’Ivoire",
38670         "ci",
38671         "225"
38672       ],
38673       [
38674         "Croatia (Hrvatska)",
38675         "hr",
38676         "385"
38677       ],
38678       [
38679         "Cuba",
38680         "cu",
38681         "53"
38682       ],
38683       [
38684         "Curaçao",
38685         "cw",
38686         "599",
38687         0
38688       ],
38689       [
38690         "Cyprus (Κύπρος)",
38691         "cy",
38692         "357"
38693       ],
38694       [
38695         "Czech Republic (Česká republika)",
38696         "cz",
38697         "420"
38698       ],
38699       [
38700         "Denmark (Danmark)",
38701         "dk",
38702         "45"
38703       ],
38704       [
38705         "Djibouti",
38706         "dj",
38707         "253"
38708       ],
38709       [
38710         "Dominica",
38711         "dm",
38712         "1767"
38713       ],
38714       [
38715         "Dominican Republic (República Dominicana)",
38716         "do",
38717         "1",
38718         2,
38719         ["809", "829", "849"]
38720       ],
38721       [
38722         "Ecuador",
38723         "ec",
38724         "593"
38725       ],
38726       [
38727         "Egypt (‫مصر‬‎)",
38728         "eg",
38729         "20"
38730       ],
38731       [
38732         "El Salvador",
38733         "sv",
38734         "503"
38735       ],
38736       [
38737         "Equatorial Guinea (Guinea Ecuatorial)",
38738         "gq",
38739         "240"
38740       ],
38741       [
38742         "Eritrea",
38743         "er",
38744         "291"
38745       ],
38746       [
38747         "Estonia (Eesti)",
38748         "ee",
38749         "372"
38750       ],
38751       [
38752         "Ethiopia",
38753         "et",
38754         "251"
38755       ],
38756       [
38757         "Falkland Islands (Islas Malvinas)",
38758         "fk",
38759         "500"
38760       ],
38761       [
38762         "Faroe Islands (Føroyar)",
38763         "fo",
38764         "298"
38765       ],
38766       [
38767         "Fiji",
38768         "fj",
38769         "679"
38770       ],
38771       [
38772         "Finland (Suomi)",
38773         "fi",
38774         "358",
38775         0
38776       ],
38777       [
38778         "France",
38779         "fr",
38780         "33"
38781       ],
38782       [
38783         "French Guiana (Guyane française)",
38784         "gf",
38785         "594"
38786       ],
38787       [
38788         "French Polynesia (Polynésie française)",
38789         "pf",
38790         "689"
38791       ],
38792       [
38793         "Gabon",
38794         "ga",
38795         "241"
38796       ],
38797       [
38798         "Gambia",
38799         "gm",
38800         "220"
38801       ],
38802       [
38803         "Georgia (საქართველო)",
38804         "ge",
38805         "995"
38806       ],
38807       [
38808         "Germany (Deutschland)",
38809         "de",
38810         "49"
38811       ],
38812       [
38813         "Ghana (Gaana)",
38814         "gh",
38815         "233"
38816       ],
38817       [
38818         "Gibraltar",
38819         "gi",
38820         "350"
38821       ],
38822       [
38823         "Greece (Ελλάδα)",
38824         "gr",
38825         "30"
38826       ],
38827       [
38828         "Greenland (Kalaallit Nunaat)",
38829         "gl",
38830         "299"
38831       ],
38832       [
38833         "Grenada",
38834         "gd",
38835         "1473"
38836       ],
38837       [
38838         "Guadeloupe",
38839         "gp",
38840         "590",
38841         0
38842       ],
38843       [
38844         "Guam",
38845         "gu",
38846         "1671"
38847       ],
38848       [
38849         "Guatemala",
38850         "gt",
38851         "502"
38852       ],
38853       [
38854         "Guernsey",
38855         "gg",
38856         "44",
38857         1
38858       ],
38859       [
38860         "Guinea (Guinée)",
38861         "gn",
38862         "224"
38863       ],
38864       [
38865         "Guinea-Bissau (Guiné Bissau)",
38866         "gw",
38867         "245"
38868       ],
38869       [
38870         "Guyana",
38871         "gy",
38872         "592"
38873       ],
38874       [
38875         "Haiti",
38876         "ht",
38877         "509"
38878       ],
38879       [
38880         "Honduras",
38881         "hn",
38882         "504"
38883       ],
38884       [
38885         "Hong Kong (香港)",
38886         "hk",
38887         "852"
38888       ],
38889       [
38890         "Hungary (Magyarország)",
38891         "hu",
38892         "36"
38893       ],
38894       [
38895         "Iceland (Ísland)",
38896         "is",
38897         "354"
38898       ],
38899       [
38900         "India (भारत)",
38901         "in",
38902         "91"
38903       ],
38904       [
38905         "Indonesia",
38906         "id",
38907         "62"
38908       ],
38909       [
38910         "Iran (‫ایران‬‎)",
38911         "ir",
38912         "98"
38913       ],
38914       [
38915         "Iraq (‫العراق‬‎)",
38916         "iq",
38917         "964"
38918       ],
38919       [
38920         "Ireland",
38921         "ie",
38922         "353"
38923       ],
38924       [
38925         "Isle of Man",
38926         "im",
38927         "44",
38928         2
38929       ],
38930       [
38931         "Israel (‫ישראל‬‎)",
38932         "il",
38933         "972"
38934       ],
38935       [
38936         "Italy (Italia)",
38937         "it",
38938         "39",
38939         0
38940       ],
38941       [
38942         "Jamaica",
38943         "jm",
38944         "1876"
38945       ],
38946       [
38947         "Japan (日本)",
38948         "jp",
38949         "81"
38950       ],
38951       [
38952         "Jersey",
38953         "je",
38954         "44",
38955         3
38956       ],
38957       [
38958         "Jordan (‫الأردن‬‎)",
38959         "jo",
38960         "962"
38961       ],
38962       [
38963         "Kazakhstan (Казахстан)",
38964         "kz",
38965         "7",
38966         1
38967       ],
38968       [
38969         "Kenya",
38970         "ke",
38971         "254"
38972       ],
38973       [
38974         "Kiribati",
38975         "ki",
38976         "686"
38977       ],
38978       [
38979         "Kosovo",
38980         "xk",
38981         "383"
38982       ],
38983       [
38984         "Kuwait (‫الكويت‬‎)",
38985         "kw",
38986         "965"
38987       ],
38988       [
38989         "Kyrgyzstan (Кыргызстан)",
38990         "kg",
38991         "996"
38992       ],
38993       [
38994         "Laos (ລາວ)",
38995         "la",
38996         "856"
38997       ],
38998       [
38999         "Latvia (Latvija)",
39000         "lv",
39001         "371"
39002       ],
39003       [
39004         "Lebanon (‫لبنان‬‎)",
39005         "lb",
39006         "961"
39007       ],
39008       [
39009         "Lesotho",
39010         "ls",
39011         "266"
39012       ],
39013       [
39014         "Liberia",
39015         "lr",
39016         "231"
39017       ],
39018       [
39019         "Libya (‫ليبيا‬‎)",
39020         "ly",
39021         "218"
39022       ],
39023       [
39024         "Liechtenstein",
39025         "li",
39026         "423"
39027       ],
39028       [
39029         "Lithuania (Lietuva)",
39030         "lt",
39031         "370"
39032       ],
39033       [
39034         "Luxembourg",
39035         "lu",
39036         "352"
39037       ],
39038       [
39039         "Macau (澳門)",
39040         "mo",
39041         "853"
39042       ],
39043       [
39044         "Macedonia (FYROM) (Македонија)",
39045         "mk",
39046         "389"
39047       ],
39048       [
39049         "Madagascar (Madagasikara)",
39050         "mg",
39051         "261"
39052       ],
39053       [
39054         "Malawi",
39055         "mw",
39056         "265"
39057       ],
39058       [
39059         "Malaysia",
39060         "my",
39061         "60"
39062       ],
39063       [
39064         "Maldives",
39065         "mv",
39066         "960"
39067       ],
39068       [
39069         "Mali",
39070         "ml",
39071         "223"
39072       ],
39073       [
39074         "Malta",
39075         "mt",
39076         "356"
39077       ],
39078       [
39079         "Marshall Islands",
39080         "mh",
39081         "692"
39082       ],
39083       [
39084         "Martinique",
39085         "mq",
39086         "596"
39087       ],
39088       [
39089         "Mauritania (‫موريتانيا‬‎)",
39090         "mr",
39091         "222"
39092       ],
39093       [
39094         "Mauritius (Moris)",
39095         "mu",
39096         "230"
39097       ],
39098       [
39099         "Mayotte",
39100         "yt",
39101         "262",
39102         1
39103       ],
39104       [
39105         "Mexico (México)",
39106         "mx",
39107         "52"
39108       ],
39109       [
39110         "Micronesia",
39111         "fm",
39112         "691"
39113       ],
39114       [
39115         "Moldova (Republica Moldova)",
39116         "md",
39117         "373"
39118       ],
39119       [
39120         "Monaco",
39121         "mc",
39122         "377"
39123       ],
39124       [
39125         "Mongolia (Монгол)",
39126         "mn",
39127         "976"
39128       ],
39129       [
39130         "Montenegro (Crna Gora)",
39131         "me",
39132         "382"
39133       ],
39134       [
39135         "Montserrat",
39136         "ms",
39137         "1664"
39138       ],
39139       [
39140         "Morocco (‫المغرب‬‎)",
39141         "ma",
39142         "212",
39143         0
39144       ],
39145       [
39146         "Mozambique (Moçambique)",
39147         "mz",
39148         "258"
39149       ],
39150       [
39151         "Myanmar (Burma) (မြန်မာ)",
39152         "mm",
39153         "95"
39154       ],
39155       [
39156         "Namibia (Namibië)",
39157         "na",
39158         "264"
39159       ],
39160       [
39161         "Nauru",
39162         "nr",
39163         "674"
39164       ],
39165       [
39166         "Nepal (नेपाल)",
39167         "np",
39168         "977"
39169       ],
39170       [
39171         "Netherlands (Nederland)",
39172         "nl",
39173         "31"
39174       ],
39175       [
39176         "New Caledonia (Nouvelle-Calédonie)",
39177         "nc",
39178         "687"
39179       ],
39180       [
39181         "New Zealand",
39182         "nz",
39183         "64"
39184       ],
39185       [
39186         "Nicaragua",
39187         "ni",
39188         "505"
39189       ],
39190       [
39191         "Niger (Nijar)",
39192         "ne",
39193         "227"
39194       ],
39195       [
39196         "Nigeria",
39197         "ng",
39198         "234"
39199       ],
39200       [
39201         "Niue",
39202         "nu",
39203         "683"
39204       ],
39205       [
39206         "Norfolk Island",
39207         "nf",
39208         "672"
39209       ],
39210       [
39211         "North Korea (조선 민주주의 인민 공화국)",
39212         "kp",
39213         "850"
39214       ],
39215       [
39216         "Northern Mariana Islands",
39217         "mp",
39218         "1670"
39219       ],
39220       [
39221         "Norway (Norge)",
39222         "no",
39223         "47",
39224         0
39225       ],
39226       [
39227         "Oman (‫عُمان‬‎)",
39228         "om",
39229         "968"
39230       ],
39231       [
39232         "Pakistan (‫پاکستان‬‎)",
39233         "pk",
39234         "92"
39235       ],
39236       [
39237         "Palau",
39238         "pw",
39239         "680"
39240       ],
39241       [
39242         "Palestine (‫فلسطين‬‎)",
39243         "ps",
39244         "970"
39245       ],
39246       [
39247         "Panama (Panamá)",
39248         "pa",
39249         "507"
39250       ],
39251       [
39252         "Papua New Guinea",
39253         "pg",
39254         "675"
39255       ],
39256       [
39257         "Paraguay",
39258         "py",
39259         "595"
39260       ],
39261       [
39262         "Peru (Perú)",
39263         "pe",
39264         "51"
39265       ],
39266       [
39267         "Philippines",
39268         "ph",
39269         "63"
39270       ],
39271       [
39272         "Poland (Polska)",
39273         "pl",
39274         "48"
39275       ],
39276       [
39277         "Portugal",
39278         "pt",
39279         "351"
39280       ],
39281       [
39282         "Puerto Rico",
39283         "pr",
39284         "1",
39285         3,
39286         ["787", "939"]
39287       ],
39288       [
39289         "Qatar (‫قطر‬‎)",
39290         "qa",
39291         "974"
39292       ],
39293       [
39294         "Réunion (La Réunion)",
39295         "re",
39296         "262",
39297         0
39298       ],
39299       [
39300         "Romania (România)",
39301         "ro",
39302         "40"
39303       ],
39304       [
39305         "Russia (Россия)",
39306         "ru",
39307         "7",
39308         0
39309       ],
39310       [
39311         "Rwanda",
39312         "rw",
39313         "250"
39314       ],
39315       [
39316         "Saint Barthélemy",
39317         "bl",
39318         "590",
39319         1
39320       ],
39321       [
39322         "Saint Helena",
39323         "sh",
39324         "290"
39325       ],
39326       [
39327         "Saint Kitts and Nevis",
39328         "kn",
39329         "1869"
39330       ],
39331       [
39332         "Saint Lucia",
39333         "lc",
39334         "1758"
39335       ],
39336       [
39337         "Saint Martin (Saint-Martin (partie française))",
39338         "mf",
39339         "590",
39340         2
39341       ],
39342       [
39343         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39344         "pm",
39345         "508"
39346       ],
39347       [
39348         "Saint Vincent and the Grenadines",
39349         "vc",
39350         "1784"
39351       ],
39352       [
39353         "Samoa",
39354         "ws",
39355         "685"
39356       ],
39357       [
39358         "San Marino",
39359         "sm",
39360         "378"
39361       ],
39362       [
39363         "São Tomé and Príncipe (São Tomé e Príncipe)",
39364         "st",
39365         "239"
39366       ],
39367       [
39368         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39369         "sa",
39370         "966"
39371       ],
39372       [
39373         "Senegal (Sénégal)",
39374         "sn",
39375         "221"
39376       ],
39377       [
39378         "Serbia (Србија)",
39379         "rs",
39380         "381"
39381       ],
39382       [
39383         "Seychelles",
39384         "sc",
39385         "248"
39386       ],
39387       [
39388         "Sierra Leone",
39389         "sl",
39390         "232"
39391       ],
39392       [
39393         "Singapore",
39394         "sg",
39395         "65"
39396       ],
39397       [
39398         "Sint Maarten",
39399         "sx",
39400         "1721"
39401       ],
39402       [
39403         "Slovakia (Slovensko)",
39404         "sk",
39405         "421"
39406       ],
39407       [
39408         "Slovenia (Slovenija)",
39409         "si",
39410         "386"
39411       ],
39412       [
39413         "Solomon Islands",
39414         "sb",
39415         "677"
39416       ],
39417       [
39418         "Somalia (Soomaaliya)",
39419         "so",
39420         "252"
39421       ],
39422       [
39423         "South Africa",
39424         "za",
39425         "27"
39426       ],
39427       [
39428         "South Korea (대한민국)",
39429         "kr",
39430         "82"
39431       ],
39432       [
39433         "South Sudan (‫جنوب السودان‬‎)",
39434         "ss",
39435         "211"
39436       ],
39437       [
39438         "Spain (España)",
39439         "es",
39440         "34"
39441       ],
39442       [
39443         "Sri Lanka (ශ්‍රී ලංකාව)",
39444         "lk",
39445         "94"
39446       ],
39447       [
39448         "Sudan (‫السودان‬‎)",
39449         "sd",
39450         "249"
39451       ],
39452       [
39453         "Suriname",
39454         "sr",
39455         "597"
39456       ],
39457       [
39458         "Svalbard and Jan Mayen",
39459         "sj",
39460         "47",
39461         1
39462       ],
39463       [
39464         "Swaziland",
39465         "sz",
39466         "268"
39467       ],
39468       [
39469         "Sweden (Sverige)",
39470         "se",
39471         "46"
39472       ],
39473       [
39474         "Switzerland (Schweiz)",
39475         "ch",
39476         "41"
39477       ],
39478       [
39479         "Syria (‫سوريا‬‎)",
39480         "sy",
39481         "963"
39482       ],
39483       [
39484         "Taiwan (台灣)",
39485         "tw",
39486         "886"
39487       ],
39488       [
39489         "Tajikistan",
39490         "tj",
39491         "992"
39492       ],
39493       [
39494         "Tanzania",
39495         "tz",
39496         "255"
39497       ],
39498       [
39499         "Thailand (ไทย)",
39500         "th",
39501         "66"
39502       ],
39503       [
39504         "Timor-Leste",
39505         "tl",
39506         "670"
39507       ],
39508       [
39509         "Togo",
39510         "tg",
39511         "228"
39512       ],
39513       [
39514         "Tokelau",
39515         "tk",
39516         "690"
39517       ],
39518       [
39519         "Tonga",
39520         "to",
39521         "676"
39522       ],
39523       [
39524         "Trinidad and Tobago",
39525         "tt",
39526         "1868"
39527       ],
39528       [
39529         "Tunisia (‫تونس‬‎)",
39530         "tn",
39531         "216"
39532       ],
39533       [
39534         "Turkey (Türkiye)",
39535         "tr",
39536         "90"
39537       ],
39538       [
39539         "Turkmenistan",
39540         "tm",
39541         "993"
39542       ],
39543       [
39544         "Turks and Caicos Islands",
39545         "tc",
39546         "1649"
39547       ],
39548       [
39549         "Tuvalu",
39550         "tv",
39551         "688"
39552       ],
39553       [
39554         "U.S. Virgin Islands",
39555         "vi",
39556         "1340"
39557       ],
39558       [
39559         "Uganda",
39560         "ug",
39561         "256"
39562       ],
39563       [
39564         "Ukraine (Україна)",
39565         "ua",
39566         "380"
39567       ],
39568       [
39569         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39570         "ae",
39571         "971"
39572       ],
39573       [
39574         "United Kingdom",
39575         "gb",
39576         "44",
39577         0
39578       ],
39579       [
39580         "United States",
39581         "us",
39582         "1",
39583         0
39584       ],
39585       [
39586         "Uruguay",
39587         "uy",
39588         "598"
39589       ],
39590       [
39591         "Uzbekistan (Oʻzbekiston)",
39592         "uz",
39593         "998"
39594       ],
39595       [
39596         "Vanuatu",
39597         "vu",
39598         "678"
39599       ],
39600       [
39601         "Vatican City (Città del Vaticano)",
39602         "va",
39603         "39",
39604         1
39605       ],
39606       [
39607         "Venezuela",
39608         "ve",
39609         "58"
39610       ],
39611       [
39612         "Vietnam (Việt Nam)",
39613         "vn",
39614         "84"
39615       ],
39616       [
39617         "Wallis and Futuna (Wallis-et-Futuna)",
39618         "wf",
39619         "681"
39620       ],
39621       [
39622         "Western Sahara (‫الصحراء الغربية‬‎)",
39623         "eh",
39624         "212",
39625         1
39626       ],
39627       [
39628         "Yemen (‫اليمن‬‎)",
39629         "ye",
39630         "967"
39631       ],
39632       [
39633         "Zambia",
39634         "zm",
39635         "260"
39636       ],
39637       [
39638         "Zimbabwe",
39639         "zw",
39640         "263"
39641       ],
39642       [
39643         "Åland Islands",
39644         "ax",
39645         "358",
39646         1
39647       ]
39648   ];
39649   
39650   return d;
39651 }/**
39652 *    This script refer to:
39653 *    Title: International Telephone Input
39654 *    Author: Jack O'Connor
39655 *    Code version:  v12.1.12
39656 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39657 **/
39658
39659 /**
39660  * @class Roo.bootstrap.PhoneInput
39661  * @extends Roo.bootstrap.TriggerField
39662  * An input with International dial-code selection
39663  
39664  * @cfg {String} defaultDialCode default '+852'
39665  * @cfg {Array} preferedCountries default []
39666   
39667  * @constructor
39668  * Create a new PhoneInput.
39669  * @param {Object} config Configuration options
39670  */
39671
39672 Roo.bootstrap.PhoneInput = function(config) {
39673     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39674 };
39675
39676 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39677         
39678         listWidth: undefined,
39679         
39680         selectedClass: 'active',
39681         
39682         invalidClass : "has-warning",
39683         
39684         validClass: 'has-success',
39685         
39686         allowed: '0123456789',
39687         
39688         /**
39689          * @cfg {String} defaultDialCode The default dial code when initializing the input
39690          */
39691         defaultDialCode: '+852',
39692         
39693         /**
39694          * @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
39695          */
39696         preferedCountries: false,
39697         
39698         getAutoCreate : function()
39699         {
39700             var data = Roo.bootstrap.PhoneInputData();
39701             var align = this.labelAlign || this.parentLabelAlign();
39702             var id = Roo.id();
39703             
39704             this.allCountries = [];
39705             this.dialCodeMapping = [];
39706             
39707             for (var i = 0; i < data.length; i++) {
39708               var c = data[i];
39709               this.allCountries[i] = {
39710                 name: c[0],
39711                 iso2: c[1],
39712                 dialCode: c[2],
39713                 priority: c[3] || 0,
39714                 areaCodes: c[4] || null
39715               };
39716               this.dialCodeMapping[c[2]] = {
39717                   name: c[0],
39718                   iso2: c[1],
39719                   priority: c[3] || 0,
39720                   areaCodes: c[4] || null
39721               };
39722             }
39723             
39724             var cfg = {
39725                 cls: 'form-group',
39726                 cn: []
39727             };
39728             
39729             var input =  {
39730                 tag: 'input',
39731                 id : id,
39732                 cls : 'form-control tel-input',
39733                 autocomplete: 'new-password'
39734             };
39735             
39736             var hiddenInput = {
39737                 tag: 'input',
39738                 type: 'hidden',
39739                 cls: 'hidden-tel-input'
39740             };
39741             
39742             if (this.name) {
39743                 hiddenInput.name = this.name;
39744             }
39745             
39746             if (this.disabled) {
39747                 input.disabled = true;
39748             }
39749             
39750             var flag_container = {
39751                 tag: 'div',
39752                 cls: 'flag-box',
39753                 cn: [
39754                     {
39755                         tag: 'div',
39756                         cls: 'flag'
39757                     },
39758                     {
39759                         tag: 'div',
39760                         cls: 'caret'
39761                     }
39762                 ]
39763             };
39764             
39765             var box = {
39766                 tag: 'div',
39767                 cls: this.hasFeedback ? 'has-feedback' : '',
39768                 cn: [
39769                     hiddenInput,
39770                     input,
39771                     {
39772                         tag: 'input',
39773                         cls: 'dial-code-holder',
39774                         disabled: true
39775                     }
39776                 ]
39777             };
39778             
39779             var container = {
39780                 cls: 'roo-select2-container input-group',
39781                 cn: [
39782                     flag_container,
39783                     box
39784                 ]
39785             };
39786             
39787             if (this.fieldLabel.length) {
39788                 var indicator = {
39789                     tag: 'i',
39790                     tooltip: 'This field is required'
39791                 };
39792                 
39793                 var label = {
39794                     tag: 'label',
39795                     'for':  id,
39796                     cls: 'control-label',
39797                     cn: []
39798                 };
39799                 
39800                 var label_text = {
39801                     tag: 'span',
39802                     html: this.fieldLabel
39803                 };
39804                 
39805                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39806                 label.cn = [
39807                     indicator,
39808                     label_text
39809                 ];
39810                 
39811                 if(this.indicatorpos == 'right') {
39812                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39813                     label.cn = [
39814                         label_text,
39815                         indicator
39816                     ];
39817                 }
39818                 
39819                 if(align == 'left') {
39820                     container = {
39821                         tag: 'div',
39822                         cn: [
39823                             container
39824                         ]
39825                     };
39826                     
39827                     if(this.labelWidth > 12){
39828                         label.style = "width: " + this.labelWidth + 'px';
39829                     }
39830                     if(this.labelWidth < 13 && this.labelmd == 0){
39831                         this.labelmd = this.labelWidth;
39832                     }
39833                     if(this.labellg > 0){
39834                         label.cls += ' col-lg-' + this.labellg;
39835                         input.cls += ' col-lg-' + (12 - this.labellg);
39836                     }
39837                     if(this.labelmd > 0){
39838                         label.cls += ' col-md-' + this.labelmd;
39839                         container.cls += ' col-md-' + (12 - this.labelmd);
39840                     }
39841                     if(this.labelsm > 0){
39842                         label.cls += ' col-sm-' + this.labelsm;
39843                         container.cls += ' col-sm-' + (12 - this.labelsm);
39844                     }
39845                     if(this.labelxs > 0){
39846                         label.cls += ' col-xs-' + this.labelxs;
39847                         container.cls += ' col-xs-' + (12 - this.labelxs);
39848                     }
39849                 }
39850             }
39851             
39852             cfg.cn = [
39853                 label,
39854                 container
39855             ];
39856             
39857             var settings = this;
39858             
39859             ['xs','sm','md','lg'].map(function(size){
39860                 if (settings[size]) {
39861                     cfg.cls += ' col-' + size + '-' + settings[size];
39862                 }
39863             });
39864             
39865             this.store = new Roo.data.Store({
39866                 proxy : new Roo.data.MemoryProxy({}),
39867                 reader : new Roo.data.JsonReader({
39868                     fields : [
39869                         {
39870                             'name' : 'name',
39871                             'type' : 'string'
39872                         },
39873                         {
39874                             'name' : 'iso2',
39875                             'type' : 'string'
39876                         },
39877                         {
39878                             'name' : 'dialCode',
39879                             'type' : 'string'
39880                         },
39881                         {
39882                             'name' : 'priority',
39883                             'type' : 'string'
39884                         },
39885                         {
39886                             'name' : 'areaCodes',
39887                             'type' : 'string'
39888                         }
39889                     ]
39890                 })
39891             });
39892             
39893             if(!this.preferedCountries) {
39894                 this.preferedCountries = [
39895                     'hk',
39896                     'gb',
39897                     'us'
39898                 ];
39899             }
39900             
39901             var p = this.preferedCountries.reverse();
39902             
39903             if(p) {
39904                 for (var i = 0; i < p.length; i++) {
39905                     for (var j = 0; j < this.allCountries.length; j++) {
39906                         if(this.allCountries[j].iso2 == p[i]) {
39907                             var t = this.allCountries[j];
39908                             this.allCountries.splice(j,1);
39909                             this.allCountries.unshift(t);
39910                         }
39911                     } 
39912                 }
39913             }
39914             
39915             this.store.proxy.data = {
39916                 success: true,
39917                 data: this.allCountries
39918             };
39919             
39920             return cfg;
39921         },
39922         
39923         initEvents : function()
39924         {
39925             this.createList();
39926             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39927             
39928             this.indicator = this.indicatorEl();
39929             this.flag = this.flagEl();
39930             this.dialCodeHolder = this.dialCodeHolderEl();
39931             
39932             this.trigger = this.el.select('div.flag-box',true).first();
39933             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39934             
39935             var _this = this;
39936             
39937             (function(){
39938                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39939                 _this.list.setWidth(lw);
39940             }).defer(100);
39941             
39942             this.list.on('mouseover', this.onViewOver, this);
39943             this.list.on('mousemove', this.onViewMove, this);
39944             this.inputEl().on("keyup", this.onKeyUp, this);
39945             
39946             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39947
39948             this.view = new Roo.View(this.list, this.tpl, {
39949                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39950             });
39951             
39952             this.view.on('click', this.onViewClick, this);
39953             this.setValue(this.defaultDialCode);
39954         },
39955         
39956         onTriggerClick : function(e)
39957         {
39958             Roo.log('trigger click');
39959             if(this.disabled){
39960                 return;
39961             }
39962             
39963             if(this.isExpanded()){
39964                 this.collapse();
39965                 this.hasFocus = false;
39966             }else {
39967                 this.store.load({});
39968                 this.hasFocus = true;
39969                 this.expand();
39970             }
39971         },
39972         
39973         isExpanded : function()
39974         {
39975             return this.list.isVisible();
39976         },
39977         
39978         collapse : function()
39979         {
39980             if(!this.isExpanded()){
39981                 return;
39982             }
39983             this.list.hide();
39984             Roo.get(document).un('mousedown', this.collapseIf, this);
39985             Roo.get(document).un('mousewheel', this.collapseIf, this);
39986             this.fireEvent('collapse', this);
39987             this.validate();
39988         },
39989         
39990         expand : function()
39991         {
39992             Roo.log('expand');
39993
39994             if(this.isExpanded() || !this.hasFocus){
39995                 return;
39996             }
39997             
39998             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39999             this.list.setWidth(lw);
40000             
40001             this.list.show();
40002             this.restrictHeight();
40003             
40004             Roo.get(document).on('mousedown', this.collapseIf, this);
40005             Roo.get(document).on('mousewheel', this.collapseIf, this);
40006             
40007             this.fireEvent('expand', this);
40008         },
40009         
40010         restrictHeight : function()
40011         {
40012             this.list.alignTo(this.inputEl(), this.listAlign);
40013             this.list.alignTo(this.inputEl(), this.listAlign);
40014         },
40015         
40016         onViewOver : function(e, t)
40017         {
40018             if(this.inKeyMode){
40019                 return;
40020             }
40021             var item = this.view.findItemFromChild(t);
40022             
40023             if(item){
40024                 var index = this.view.indexOf(item);
40025                 this.select(index, false);
40026             }
40027         },
40028
40029         // private
40030         onViewClick : function(view, doFocus, el, e)
40031         {
40032             var index = this.view.getSelectedIndexes()[0];
40033             
40034             var r = this.store.getAt(index);
40035             
40036             if(r){
40037                 this.onSelect(r, index);
40038             }
40039             if(doFocus !== false && !this.blockFocus){
40040                 this.inputEl().focus();
40041             }
40042         },
40043         
40044         onViewMove : function(e, t)
40045         {
40046             this.inKeyMode = false;
40047         },
40048         
40049         select : function(index, scrollIntoView)
40050         {
40051             this.selectedIndex = index;
40052             this.view.select(index);
40053             if(scrollIntoView !== false){
40054                 var el = this.view.getNode(index);
40055                 if(el){
40056                     this.list.scrollChildIntoView(el, false);
40057                 }
40058             }
40059         },
40060         
40061         createList : function()
40062         {
40063             this.list = Roo.get(document.body).createChild({
40064                 tag: 'ul',
40065                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40066                 style: 'display:none'
40067             });
40068             
40069             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40070         },
40071         
40072         collapseIf : function(e)
40073         {
40074             var in_combo  = e.within(this.el);
40075             var in_list =  e.within(this.list);
40076             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40077             
40078             if (in_combo || in_list || is_list) {
40079                 return;
40080             }
40081             this.collapse();
40082         },
40083         
40084         onSelect : function(record, index)
40085         {
40086             if(this.fireEvent('beforeselect', this, record, index) !== false){
40087                 
40088                 this.setFlagClass(record.data.iso2);
40089                 this.setDialCode(record.data.dialCode);
40090                 this.hasFocus = false;
40091                 this.collapse();
40092                 this.fireEvent('select', this, record, index);
40093             }
40094         },
40095         
40096         flagEl : function()
40097         {
40098             var flag = this.el.select('div.flag',true).first();
40099             if(!flag){
40100                 return false;
40101             }
40102             return flag;
40103         },
40104         
40105         dialCodeHolderEl : function()
40106         {
40107             var d = this.el.select('input.dial-code-holder',true).first();
40108             if(!d){
40109                 return false;
40110             }
40111             return d;
40112         },
40113         
40114         setDialCode : function(v)
40115         {
40116             this.dialCodeHolder.dom.value = '+'+v;
40117         },
40118         
40119         setFlagClass : function(n)
40120         {
40121             this.flag.dom.className = 'flag '+n;
40122         },
40123         
40124         getValue : function()
40125         {
40126             var v = this.inputEl().getValue();
40127             if(this.dialCodeHolder) {
40128                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40129             }
40130             return v;
40131         },
40132         
40133         setValue : function(v)
40134         {
40135             var d = this.getDialCode(v);
40136             
40137             //invalid dial code
40138             if(v.length == 0 || !d || d.length == 0) {
40139                 if(this.rendered){
40140                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40141                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40142                 }
40143                 return;
40144             }
40145             
40146             //valid dial code
40147             this.setFlagClass(this.dialCodeMapping[d].iso2);
40148             this.setDialCode(d);
40149             this.inputEl().dom.value = v.replace('+'+d,'');
40150             this.hiddenEl().dom.value = this.getValue();
40151             
40152             this.validate();
40153         },
40154         
40155         getDialCode : function(v)
40156         {
40157             v = v ||  '';
40158             
40159             if (v.length == 0) {
40160                 return this.dialCodeHolder.dom.value;
40161             }
40162             
40163             var dialCode = "";
40164             if (v.charAt(0) != "+") {
40165                 return false;
40166             }
40167             var numericChars = "";
40168             for (var i = 1; i < v.length; i++) {
40169               var c = v.charAt(i);
40170               if (!isNaN(c)) {
40171                 numericChars += c;
40172                 if (this.dialCodeMapping[numericChars]) {
40173                   dialCode = v.substr(1, i);
40174                 }
40175                 if (numericChars.length == 4) {
40176                   break;
40177                 }
40178               }
40179             }
40180             return dialCode;
40181         },
40182         
40183         reset : function()
40184         {
40185             this.setValue(this.defaultDialCode);
40186             this.validate();
40187         },
40188         
40189         hiddenEl : function()
40190         {
40191             return this.el.select('input.hidden-tel-input',true).first();
40192         },
40193         
40194         onKeyUp : function(e){
40195             
40196             var k = e.getKey();
40197             var c = e.getCharCode();
40198             
40199             if(
40200                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40201                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40202             ){
40203                 e.stopEvent();
40204             }
40205             
40206             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40207             //     return;
40208             // }
40209             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40210                 e.stopEvent();
40211             }
40212             
40213             this.setValue(this.getValue());
40214         }
40215         
40216 });
40217 /**
40218  * @class Roo.bootstrap.MoneyField
40219  * @extends Roo.bootstrap.ComboBox
40220  * Bootstrap MoneyField class
40221  * 
40222  * @constructor
40223  * Create a new MoneyField.
40224  * @param {Object} config Configuration options
40225  */
40226
40227 Roo.bootstrap.MoneyField = function(config) {
40228     
40229     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40230     
40231 };
40232
40233 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40234     
40235     /**
40236      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40237      */
40238     allowDecimals : true,
40239     /**
40240      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40241      */
40242     decimalSeparator : ".",
40243     /**
40244      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40245      */
40246     decimalPrecision : 0,
40247     /**
40248      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40249      */
40250     allowNegative : true,
40251     /**
40252      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40253      */
40254     allowZero: true,
40255     /**
40256      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40257      */
40258     minValue : Number.NEGATIVE_INFINITY,
40259     /**
40260      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40261      */
40262     maxValue : Number.MAX_VALUE,
40263     /**
40264      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40265      */
40266     minText : "The minimum value for this field is {0}",
40267     /**
40268      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40269      */
40270     maxText : "The maximum value for this field is {0}",
40271     /**
40272      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40273      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40274      */
40275     nanText : "{0} is not a valid number",
40276     /**
40277      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40278      */
40279     castInt : true,
40280     /**
40281      * @cfg {String} defaults currency of the MoneyField
40282      * value should be in lkey
40283      */
40284     defaultCurrency : false,
40285     /**
40286      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40287      */
40288     thousandsDelimiter : false,
40289     
40290     
40291     inputlg : 9,
40292     inputmd : 9,
40293     inputsm : 9,
40294     inputxs : 6,
40295     
40296     store : false,
40297     
40298     getAutoCreate : function()
40299     {
40300         var align = this.labelAlign || this.parentLabelAlign();
40301         
40302         var id = Roo.id();
40303
40304         var cfg = {
40305             cls: 'form-group',
40306             cn: []
40307         };
40308
40309         var input =  {
40310             tag: 'input',
40311             id : id,
40312             cls : 'form-control roo-money-amount-input',
40313             autocomplete: 'new-password'
40314         };
40315         
40316         var hiddenInput = {
40317             tag: 'input',
40318             type: 'hidden',
40319             id: Roo.id(),
40320             cls: 'hidden-number-input'
40321         };
40322         
40323         if (this.name) {
40324             hiddenInput.name = this.name;
40325         }
40326
40327         if (this.disabled) {
40328             input.disabled = true;
40329         }
40330
40331         var clg = 12 - this.inputlg;
40332         var cmd = 12 - this.inputmd;
40333         var csm = 12 - this.inputsm;
40334         var cxs = 12 - this.inputxs;
40335         
40336         var container = {
40337             tag : 'div',
40338             cls : 'row roo-money-field',
40339             cn : [
40340                 {
40341                     tag : 'div',
40342                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40343                     cn : [
40344                         {
40345                             tag : 'div',
40346                             cls: 'roo-select2-container input-group',
40347                             cn: [
40348                                 {
40349                                     tag : 'input',
40350                                     cls : 'form-control roo-money-currency-input',
40351                                     autocomplete: 'new-password',
40352                                     readOnly : 1,
40353                                     name : this.currencyName
40354                                 },
40355                                 {
40356                                     tag :'span',
40357                                     cls : 'input-group-addon',
40358                                     cn : [
40359                                         {
40360                                             tag: 'span',
40361                                             cls: 'caret'
40362                                         }
40363                                     ]
40364                                 }
40365                             ]
40366                         }
40367                     ]
40368                 },
40369                 {
40370                     tag : 'div',
40371                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40372                     cn : [
40373                         {
40374                             tag: 'div',
40375                             cls: this.hasFeedback ? 'has-feedback' : '',
40376                             cn: [
40377                                 input
40378                             ]
40379                         }
40380                     ]
40381                 }
40382             ]
40383             
40384         };
40385         
40386         if (this.fieldLabel.length) {
40387             var indicator = {
40388                 tag: 'i',
40389                 tooltip: 'This field is required'
40390             };
40391
40392             var label = {
40393                 tag: 'label',
40394                 'for':  id,
40395                 cls: 'control-label',
40396                 cn: []
40397             };
40398
40399             var label_text = {
40400                 tag: 'span',
40401                 html: this.fieldLabel
40402             };
40403
40404             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40405             label.cn = [
40406                 indicator,
40407                 label_text
40408             ];
40409
40410             if(this.indicatorpos == 'right') {
40411                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40412                 label.cn = [
40413                     label_text,
40414                     indicator
40415                 ];
40416             }
40417
40418             if(align == 'left') {
40419                 container = {
40420                     tag: 'div',
40421                     cn: [
40422                         container
40423                     ]
40424                 };
40425
40426                 if(this.labelWidth > 12){
40427                     label.style = "width: " + this.labelWidth + 'px';
40428                 }
40429                 if(this.labelWidth < 13 && this.labelmd == 0){
40430                     this.labelmd = this.labelWidth;
40431                 }
40432                 if(this.labellg > 0){
40433                     label.cls += ' col-lg-' + this.labellg;
40434                     input.cls += ' col-lg-' + (12 - this.labellg);
40435                 }
40436                 if(this.labelmd > 0){
40437                     label.cls += ' col-md-' + this.labelmd;
40438                     container.cls += ' col-md-' + (12 - this.labelmd);
40439                 }
40440                 if(this.labelsm > 0){
40441                     label.cls += ' col-sm-' + this.labelsm;
40442                     container.cls += ' col-sm-' + (12 - this.labelsm);
40443                 }
40444                 if(this.labelxs > 0){
40445                     label.cls += ' col-xs-' + this.labelxs;
40446                     container.cls += ' col-xs-' + (12 - this.labelxs);
40447                 }
40448             }
40449         }
40450
40451         cfg.cn = [
40452             label,
40453             container,
40454             hiddenInput
40455         ];
40456         
40457         var settings = this;
40458
40459         ['xs','sm','md','lg'].map(function(size){
40460             if (settings[size]) {
40461                 cfg.cls += ' col-' + size + '-' + settings[size];
40462             }
40463         });
40464         
40465         return cfg;
40466     },
40467     
40468     initEvents : function()
40469     {
40470         this.indicator = this.indicatorEl();
40471         
40472         this.initCurrencyEvent();
40473         
40474         this.initNumberEvent();
40475     },
40476     
40477     initCurrencyEvent : function()
40478     {
40479         if (!this.store) {
40480             throw "can not find store for combo";
40481         }
40482         
40483         this.store = Roo.factory(this.store, Roo.data);
40484         this.store.parent = this;
40485         
40486         this.createList();
40487         
40488         this.triggerEl = this.el.select('.input-group-addon', true).first();
40489         
40490         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40491         
40492         var _this = this;
40493         
40494         (function(){
40495             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40496             _this.list.setWidth(lw);
40497         }).defer(100);
40498         
40499         this.list.on('mouseover', this.onViewOver, this);
40500         this.list.on('mousemove', this.onViewMove, this);
40501         this.list.on('scroll', this.onViewScroll, this);
40502         
40503         if(!this.tpl){
40504             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40505         }
40506         
40507         this.view = new Roo.View(this.list, this.tpl, {
40508             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40509         });
40510         
40511         this.view.on('click', this.onViewClick, this);
40512         
40513         this.store.on('beforeload', this.onBeforeLoad, this);
40514         this.store.on('load', this.onLoad, this);
40515         this.store.on('loadexception', this.onLoadException, this);
40516         
40517         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40518             "up" : function(e){
40519                 this.inKeyMode = true;
40520                 this.selectPrev();
40521             },
40522
40523             "down" : function(e){
40524                 if(!this.isExpanded()){
40525                     this.onTriggerClick();
40526                 }else{
40527                     this.inKeyMode = true;
40528                     this.selectNext();
40529                 }
40530             },
40531
40532             "enter" : function(e){
40533                 this.collapse();
40534                 
40535                 if(this.fireEvent("specialkey", this, e)){
40536                     this.onViewClick(false);
40537                 }
40538                 
40539                 return true;
40540             },
40541
40542             "esc" : function(e){
40543                 this.collapse();
40544             },
40545
40546             "tab" : function(e){
40547                 this.collapse();
40548                 
40549                 if(this.fireEvent("specialkey", this, e)){
40550                     this.onViewClick(false);
40551                 }
40552                 
40553                 return true;
40554             },
40555
40556             scope : this,
40557
40558             doRelay : function(foo, bar, hname){
40559                 if(hname == 'down' || this.scope.isExpanded()){
40560                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40561                 }
40562                 return true;
40563             },
40564
40565             forceKeyDown: true
40566         });
40567         
40568         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40569         
40570     },
40571     
40572     initNumberEvent : function(e)
40573     {
40574         this.inputEl().on("keydown" , this.fireKey,  this);
40575         this.inputEl().on("focus", this.onFocus,  this);
40576         this.inputEl().on("blur", this.onBlur,  this);
40577         
40578         this.inputEl().relayEvent('keyup', this);
40579         
40580         if(this.indicator){
40581             this.indicator.addClass('invisible');
40582         }
40583  
40584         this.originalValue = this.getValue();
40585         
40586         if(this.validationEvent == 'keyup'){
40587             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40588             this.inputEl().on('keyup', this.filterValidation, this);
40589         }
40590         else if(this.validationEvent !== false){
40591             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40592         }
40593         
40594         if(this.selectOnFocus){
40595             this.on("focus", this.preFocus, this);
40596             
40597         }
40598         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40599             this.inputEl().on("keypress", this.filterKeys, this);
40600         } else {
40601             this.inputEl().relayEvent('keypress', this);
40602         }
40603         
40604         var allowed = "0123456789";
40605         
40606         if(this.allowDecimals){
40607             allowed += this.decimalSeparator;
40608         }
40609         
40610         if(this.allowNegative){
40611             allowed += "-";
40612         }
40613         
40614         if(this.thousandsDelimiter) {
40615             allowed += ",";
40616         }
40617         
40618         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40619         
40620         var keyPress = function(e){
40621             
40622             var k = e.getKey();
40623             
40624             var c = e.getCharCode();
40625             
40626             if(
40627                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40628                     allowed.indexOf(String.fromCharCode(c)) === -1
40629             ){
40630                 e.stopEvent();
40631                 return;
40632             }
40633             
40634             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40635                 return;
40636             }
40637             
40638             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40639                 e.stopEvent();
40640             }
40641         };
40642         
40643         this.inputEl().on("keypress", keyPress, this);
40644         
40645     },
40646     
40647     onTriggerClick : function(e)
40648     {   
40649         if(this.disabled){
40650             return;
40651         }
40652         
40653         this.page = 0;
40654         this.loadNext = false;
40655         
40656         if(this.isExpanded()){
40657             this.collapse();
40658             return;
40659         }
40660         
40661         this.hasFocus = true;
40662         
40663         if(this.triggerAction == 'all') {
40664             this.doQuery(this.allQuery, true);
40665             return;
40666         }
40667         
40668         this.doQuery(this.getRawValue());
40669     },
40670     
40671     getCurrency : function()
40672     {   
40673         var v = this.currencyEl().getValue();
40674         
40675         return v;
40676     },
40677     
40678     restrictHeight : function()
40679     {
40680         this.list.alignTo(this.currencyEl(), this.listAlign);
40681         this.list.alignTo(this.currencyEl(), this.listAlign);
40682     },
40683     
40684     onViewClick : function(view, doFocus, el, e)
40685     {
40686         var index = this.view.getSelectedIndexes()[0];
40687         
40688         var r = this.store.getAt(index);
40689         
40690         if(r){
40691             this.onSelect(r, index);
40692         }
40693     },
40694     
40695     onSelect : function(record, index){
40696         
40697         if(this.fireEvent('beforeselect', this, record, index) !== false){
40698         
40699             this.setFromCurrencyData(index > -1 ? record.data : false);
40700             
40701             this.collapse();
40702             
40703             this.fireEvent('select', this, record, index);
40704         }
40705     },
40706     
40707     setFromCurrencyData : function(o)
40708     {
40709         var currency = '';
40710         
40711         this.lastCurrency = o;
40712         
40713         if (this.currencyField) {
40714             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40715         } else {
40716             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40717         }
40718         
40719         this.lastSelectionText = currency;
40720         
40721         //setting default currency
40722         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40723             this.setCurrency(this.defaultCurrency);
40724             return;
40725         }
40726         
40727         this.setCurrency(currency);
40728     },
40729     
40730     setFromData : function(o)
40731     {
40732         var c = {};
40733         
40734         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40735         
40736         this.setFromCurrencyData(c);
40737         
40738         var value = '';
40739         
40740         if (this.name) {
40741             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40742         } else {
40743             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40744         }
40745         
40746         this.setValue(value);
40747         
40748     },
40749     
40750     setCurrency : function(v)
40751     {   
40752         this.currencyValue = v;
40753         
40754         if(this.rendered){
40755             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40756             this.validate();
40757         }
40758     },
40759     
40760     setValue : function(v)
40761     {
40762         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40763         
40764         this.value = v;
40765         
40766         if(this.rendered){
40767             
40768             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40769             
40770             this.inputEl().dom.value = (v == '') ? '' :
40771                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40772             
40773             if(!this.allowZero && v === '0') {
40774                 this.hiddenEl().dom.value = '';
40775                 this.inputEl().dom.value = '';
40776             }
40777             
40778             this.validate();
40779         }
40780     },
40781     
40782     getRawValue : function()
40783     {
40784         var v = this.inputEl().getValue();
40785         
40786         return v;
40787     },
40788     
40789     getValue : function()
40790     {
40791         return this.fixPrecision(this.parseValue(this.getRawValue()));
40792     },
40793     
40794     parseValue : function(value)
40795     {
40796         if(this.thousandsDelimiter) {
40797             value += "";
40798             r = new RegExp(",", "g");
40799             value = value.replace(r, "");
40800         }
40801         
40802         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40803         return isNaN(value) ? '' : value;
40804         
40805     },
40806     
40807     fixPrecision : function(value)
40808     {
40809         if(this.thousandsDelimiter) {
40810             value += "";
40811             r = new RegExp(",", "g");
40812             value = value.replace(r, "");
40813         }
40814         
40815         var nan = isNaN(value);
40816         
40817         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40818             return nan ? '' : value;
40819         }
40820         return parseFloat(value).toFixed(this.decimalPrecision);
40821     },
40822     
40823     decimalPrecisionFcn : function(v)
40824     {
40825         return Math.floor(v);
40826     },
40827     
40828     validateValue : function(value)
40829     {
40830         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40831             return false;
40832         }
40833         
40834         var num = this.parseValue(value);
40835         
40836         if(isNaN(num)){
40837             this.markInvalid(String.format(this.nanText, value));
40838             return false;
40839         }
40840         
40841         if(num < this.minValue){
40842             this.markInvalid(String.format(this.minText, this.minValue));
40843             return false;
40844         }
40845         
40846         if(num > this.maxValue){
40847             this.markInvalid(String.format(this.maxText, this.maxValue));
40848             return false;
40849         }
40850         
40851         return true;
40852     },
40853     
40854     validate : function()
40855     {
40856         if(this.disabled || this.allowBlank){
40857             this.markValid();
40858             return true;
40859         }
40860         
40861         var currency = this.getCurrency();
40862         
40863         if(this.validateValue(this.getRawValue()) && currency.length){
40864             this.markValid();
40865             return true;
40866         }
40867         
40868         this.markInvalid();
40869         return false;
40870     },
40871     
40872     getName: function()
40873     {
40874         return this.name;
40875     },
40876     
40877     beforeBlur : function()
40878     {
40879         if(!this.castInt){
40880             return;
40881         }
40882         
40883         var v = this.parseValue(this.getRawValue());
40884         
40885         if(v || v == 0){
40886             this.setValue(v);
40887         }
40888     },
40889     
40890     onBlur : function()
40891     {
40892         this.beforeBlur();
40893         
40894         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40895             //this.el.removeClass(this.focusClass);
40896         }
40897         
40898         this.hasFocus = false;
40899         
40900         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40901             this.validate();
40902         }
40903         
40904         var v = this.getValue();
40905         
40906         if(String(v) !== String(this.startValue)){
40907             this.fireEvent('change', this, v, this.startValue);
40908         }
40909         
40910         this.fireEvent("blur", this);
40911     },
40912     
40913     inputEl : function()
40914     {
40915         return this.el.select('.roo-money-amount-input', true).first();
40916     },
40917     
40918     currencyEl : function()
40919     {
40920         return this.el.select('.roo-money-currency-input', true).first();
40921     },
40922     
40923     hiddenEl : function()
40924     {
40925         return this.el.select('input.hidden-number-input',true).first();
40926     }
40927     
40928 });