roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         
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         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2250             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2251         }
2252         
2253         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2254             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2255         }
2256         
2257         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2258             this.el.setXY(xy);
2259         }
2260         
2261         this.focus();
2262         this.fireEvent("show", this);
2263     },
2264     
2265     focus : function(){
2266         return;
2267         if(!this.hidden){
2268             this.doFocus.defer(50, this);
2269         }
2270     },
2271
2272     doFocus : function(){
2273         if(!this.hidden){
2274             this.focusEl.focus();
2275         }
2276     },
2277
2278     /**
2279      * Hides this menu and optionally all parent menus
2280      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2281      */
2282     hide : function(deep)
2283     {
2284         
2285         this.hideMenuItems();
2286         if(this.el && this.isVisible()){
2287             this.fireEvent("beforehide", this);
2288             if(this.activeItem){
2289                 this.activeItem.deactivate();
2290                 this.activeItem = null;
2291             }
2292             this.triggerEl.removeClass('open');;
2293             this.hidden = true;
2294             this.fireEvent("hide", this);
2295         }
2296         if(deep === true && this.parentMenu){
2297             this.parentMenu.hide(true);
2298         }
2299     },
2300     
2301     onTriggerClick : function(e)
2302     {
2303         Roo.log('trigger click');
2304         
2305         var target = e.getTarget();
2306         
2307         Roo.log(target.nodeName.toLowerCase());
2308         
2309         if(target.nodeName.toLowerCase() === 'i'){
2310             e.preventDefault();
2311         }
2312         
2313     },
2314     
2315     onTriggerPress  : function(e)
2316     {
2317         Roo.log('trigger press');
2318         //Roo.log(e.getTarget());
2319        // Roo.log(this.triggerEl.dom);
2320        
2321         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2322         var pel = Roo.get(e.getTarget());
2323         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2324             Roo.log('is treeview or dropdown?');
2325             return;
2326         }
2327         
2328         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2329             return;
2330         }
2331         
2332         if (this.isVisible()) {
2333             Roo.log('hide');
2334             this.hide();
2335         } else {
2336             Roo.log('show');
2337             this.show(this.triggerEl, false, false);
2338         }
2339         
2340         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2341             e.stopEvent();
2342         }
2343         
2344     },
2345        
2346     
2347     hideMenuItems : function()
2348     {
2349         Roo.log("hide Menu Items");
2350         if (!this.el) { 
2351             return;
2352         }
2353         //$(backdrop).remove()
2354         this.el.select('.open',true).each(function(aa) {
2355             
2356             aa.removeClass('open');
2357           //var parent = getParent($(this))
2358           //var relatedTarget = { relatedTarget: this }
2359           
2360            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2361           //if (e.isDefaultPrevented()) return
2362            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2363         });
2364     },
2365     addxtypeChild : function (tree, cntr) {
2366         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2367           
2368         this.menuitems.add(comp);
2369         return comp;
2370
2371     },
2372     getEl : function()
2373     {
2374         Roo.log(this.el);
2375         return this.el;
2376     },
2377     
2378     clear : function()
2379     {
2380         this.getEl().dom.innerHTML = '';
2381         this.menuitems.clear();
2382     }
2383 });
2384
2385  
2386  /*
2387  * - LGPL
2388  *
2389  * menu item
2390  * 
2391  */
2392
2393
2394 /**
2395  * @class Roo.bootstrap.MenuItem
2396  * @extends Roo.bootstrap.Component
2397  * Bootstrap MenuItem class
2398  * @cfg {String} html the menu label
2399  * @cfg {String} href the link
2400  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2401  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2402  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2403  * @cfg {String} fa favicon to show on left of menu item.
2404  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2405  * 
2406  * 
2407  * @constructor
2408  * Create a new MenuItem
2409  * @param {Object} config The config object
2410  */
2411
2412
2413 Roo.bootstrap.MenuItem = function(config){
2414     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2415     this.addEvents({
2416         // raw events
2417         /**
2418          * @event click
2419          * The raw click event for the entire grid.
2420          * @param {Roo.bootstrap.MenuItem} this
2421          * @param {Roo.EventObject} e
2422          */
2423         "click" : true
2424     });
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2428     
2429     href : false,
2430     html : false,
2431     preventDefault: false,
2432     isContainer : false,
2433     active : false,
2434     fa: false,
2435     
2436     getAutoCreate : function(){
2437         
2438         if(this.isContainer){
2439             return {
2440                 tag: 'li',
2441                 cls: 'dropdown-menu-item'
2442             };
2443         }
2444         var ctag = {
2445             tag: 'span',
2446             html: 'Link'
2447         };
2448         
2449         var anc = {
2450             tag : 'a',
2451             href : '#',
2452             cn : [  ]
2453         };
2454         
2455         if (this.fa !== false) {
2456             anc.cn.push({
2457                 tag : 'i',
2458                 cls : 'fa fa-' + this.fa
2459             });
2460         }
2461         
2462         anc.cn.push(ctag);
2463         
2464         
2465         var cfg= {
2466             tag: 'li',
2467             cls: 'dropdown-menu-item',
2468             cn: [ anc ]
2469         };
2470         if (this.parent().type == 'treeview') {
2471             cfg.cls = 'treeview-menu';
2472         }
2473         if (this.active) {
2474             cfg.cls += ' active';
2475         }
2476         
2477         
2478         
2479         anc.href = this.href || cfg.cn[0].href ;
2480         ctag.html = this.html || cfg.cn[0].html ;
2481         return cfg;
2482     },
2483     
2484     initEvents: function()
2485     {
2486         if (this.parent().type == 'treeview') {
2487             this.el.select('a').on('click', this.onClick, this);
2488         }
2489         
2490         if (this.menu) {
2491             this.menu.parentType = this.xtype;
2492             this.menu.triggerEl = this.el;
2493             this.menu = this.addxtype(Roo.apply({}, this.menu));
2494         }
2495         
2496     },
2497     onClick : function(e)
2498     {
2499         Roo.log('item on click ');
2500         
2501         if(this.preventDefault){
2502             e.preventDefault();
2503         }
2504         //this.parent().hideMenuItems();
2505         
2506         this.fireEvent('click', this, e);
2507     },
2508     getEl : function()
2509     {
2510         return this.el;
2511     } 
2512 });
2513
2514  
2515
2516  /*
2517  * - LGPL
2518  *
2519  * menu separator
2520  * 
2521  */
2522
2523
2524 /**
2525  * @class Roo.bootstrap.MenuSeparator
2526  * @extends Roo.bootstrap.Component
2527  * Bootstrap MenuSeparator class
2528  * 
2529  * @constructor
2530  * Create a new MenuItem
2531  * @param {Object} config The config object
2532  */
2533
2534
2535 Roo.bootstrap.MenuSeparator = function(config){
2536     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2537 };
2538
2539 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2540     
2541     getAutoCreate : function(){
2542         var cfg = {
2543             cls: 'divider',
2544             tag : 'li'
2545         };
2546         
2547         return cfg;
2548     }
2549    
2550 });
2551
2552  
2553
2554  
2555 /*
2556 * Licence: LGPL
2557 */
2558
2559 /**
2560  * @class Roo.bootstrap.Modal
2561  * @extends Roo.bootstrap.Component
2562  * Bootstrap Modal class
2563  * @cfg {String} title Title of dialog
2564  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2565  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2566  * @cfg {Boolean} specificTitle default false
2567  * @cfg {Array} buttons Array of buttons or standard button set..
2568  * @cfg {String} buttonPosition (left|right|center) default right
2569  * @cfg {Boolean} animate default true
2570  * @cfg {Boolean} allow_close default true
2571  * @cfg {Boolean} fitwindow default false
2572  * @cfg {String} size (sm|lg) default empty
2573  *
2574  *
2575  * @constructor
2576  * Create a new Modal Dialog
2577  * @param {Object} config The config object
2578  */
2579
2580 Roo.bootstrap.Modal = function(config){
2581     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2582     this.addEvents({
2583         // raw events
2584         /**
2585          * @event btnclick
2586          * The raw btnclick event for the button
2587          * @param {Roo.EventObject} e
2588          */
2589         "btnclick" : true,
2590         /**
2591          * @event resize
2592          * Fire when dialog resize
2593          * @param {Roo.bootstrap.Modal} this
2594          * @param {Roo.EventObject} e
2595          */
2596         "resize" : true
2597     });
2598     this.buttons = this.buttons || [];
2599
2600     if (this.tmpl) {
2601         this.tmpl = Roo.factory(this.tmpl);
2602     }
2603
2604 };
2605
2606 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2607
2608     title : 'test dialog',
2609
2610     buttons : false,
2611
2612     // set on load...
2613
2614     html: false,
2615
2616     tmp: false,
2617
2618     specificTitle: false,
2619
2620     buttonPosition: 'right',
2621
2622     allow_close : true,
2623
2624     animate : true,
2625
2626     fitwindow: false,
2627
2628
2629      // private
2630     dialogEl: false,
2631     bodyEl:  false,
2632     footerEl:  false,
2633     titleEl:  false,
2634     closeEl:  false,
2635
2636     size: '',
2637
2638
2639     onRender : function(ct, position)
2640     {
2641         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2642
2643         if(!this.el){
2644             var cfg = Roo.apply({},  this.getAutoCreate());
2645             cfg.id = Roo.id();
2646             //if(!cfg.name){
2647             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2648             //}
2649             //if (!cfg.name.length) {
2650             //    delete cfg.name;
2651            // }
2652             if (this.cls) {
2653                 cfg.cls += ' ' + this.cls;
2654             }
2655             if (this.style) {
2656                 cfg.style = this.style;
2657             }
2658             this.el = Roo.get(document.body).createChild(cfg, position);
2659         }
2660         //var type = this.el.dom.type;
2661
2662
2663         if(this.tabIndex !== undefined){
2664             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2665         }
2666
2667         this.dialogEl = this.el.select('.modal-dialog',true).first();
2668         this.bodyEl = this.el.select('.modal-body',true).first();
2669         this.closeEl = this.el.select('.modal-header .close', true).first();
2670         this.headerEl = this.el.select('.modal-header',true).first();
2671         this.titleEl = this.el.select('.modal-title',true).first();
2672         this.footerEl = this.el.select('.modal-footer',true).first();
2673
2674         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2675         
2676         //this.el.addClass("x-dlg-modal");
2677
2678         if (this.buttons.length) {
2679             Roo.each(this.buttons, function(bb) {
2680                 var b = Roo.apply({}, bb);
2681                 b.xns = b.xns || Roo.bootstrap;
2682                 b.xtype = b.xtype || 'Button';
2683                 if (typeof(b.listeners) == 'undefined') {
2684                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2685                 }
2686
2687                 var btn = Roo.factory(b);
2688
2689                 btn.render(this.el.select('.modal-footer div').first());
2690
2691             },this);
2692         }
2693         // render the children.
2694         var nitems = [];
2695
2696         if(typeof(this.items) != 'undefined'){
2697             var items = this.items;
2698             delete this.items;
2699
2700             for(var i =0;i < items.length;i++) {
2701                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2702             }
2703         }
2704
2705         this.items = nitems;
2706
2707         // where are these used - they used to be body/close/footer
2708
2709
2710         this.initEvents();
2711         //this.el.addClass([this.fieldClass, this.cls]);
2712
2713     },
2714
2715     getAutoCreate : function(){
2716
2717
2718         var bdy = {
2719                 cls : 'modal-body',
2720                 html : this.html || ''
2721         };
2722
2723         var title = {
2724             tag: 'h4',
2725             cls : 'modal-title',
2726             html : this.title
2727         };
2728
2729         if(this.specificTitle){
2730             title = this.title;
2731
2732         };
2733
2734         var header = [];
2735         if (this.allow_close) {
2736             header.push({
2737                 tag: 'button',
2738                 cls : 'close',
2739                 html : '&times'
2740             });
2741         }
2742
2743         header.push(title);
2744
2745         var size = '';
2746
2747         if(this.size.length){
2748             size = 'modal-' + this.size;
2749         }
2750
2751         var modal = {
2752             cls: "modal",
2753              cn : [
2754                 {
2755                     cls: "modal-dialog " + size,
2756                     cn : [
2757                         {
2758                             cls : "modal-content",
2759                             cn : [
2760                                 {
2761                                     cls : 'modal-header',
2762                                     cn : header
2763                                 },
2764                                 bdy,
2765                                 {
2766                                     cls : 'modal-footer',
2767                                     cn : [
2768                                         {
2769                                             tag: 'div',
2770                                             cls: 'btn-' + this.buttonPosition
2771                                         }
2772                                     ]
2773
2774                                 }
2775
2776
2777                             ]
2778
2779                         }
2780                     ]
2781
2782                 }
2783             ]
2784         };
2785
2786         if(this.animate){
2787             modal.cls += ' fade';
2788         }
2789
2790         return modal;
2791
2792     },
2793     getChildContainer : function() {
2794
2795          return this.bodyEl;
2796
2797     },
2798     getButtonContainer : function() {
2799          return this.el.select('.modal-footer div',true).first();
2800
2801     },
2802     initEvents : function()
2803     {
2804         if (this.allow_close) {
2805             this.closeEl.on('click', this.hide, this);
2806         }
2807         Roo.EventManager.onWindowResize(this.resize, this, true);
2808
2809
2810     },
2811
2812     resize : function()
2813     {
2814         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2815         if (this.fitwindow) {
2816             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2817             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2818             this.setSize(w,h);
2819         }
2820     },
2821
2822     setSize : function(w,h)
2823     {
2824         if (!w && !h) {
2825             return;
2826         }
2827         this.resizeTo(w,h);
2828     },
2829
2830     show : function() {
2831
2832         if (!this.rendered) {
2833             this.render();
2834         }
2835
2836         //this.el.setStyle('display', 'block');
2837         this.el.removeClass('hideing');        
2838         this.el.addClass('show');
2839  
2840         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2841             var _this = this;
2842             (function(){
2843                 this.el.addClass('in');
2844             }).defer(50, this);
2845         }else{
2846             this.el.addClass('in');
2847
2848         }
2849
2850         // not sure how we can show data in here..
2851         //if (this.tmpl) {
2852         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2853         //}
2854
2855         Roo.get(document.body).addClass("x-body-masked");
2856         
2857         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2858         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2859         this.maskEl.addClass('show');
2860         
2861         this.resize();
2862         
2863         this.fireEvent('show', this);
2864
2865         // set zindex here - otherwise it appears to be ignored...
2866         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2867
2868         (function () {
2869             this.items.forEach( function(e) {
2870                 e.layout ? e.layout() : false;
2871
2872             });
2873         }).defer(100,this);
2874
2875     },
2876     hide : function()
2877     {
2878         if(this.fireEvent("beforehide", this) !== false){
2879             this.maskEl.removeClass('show');
2880             Roo.get(document.body).removeClass("x-body-masked");
2881             this.el.removeClass('in');
2882             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2883
2884             if(this.animate){ // why
2885                 this.el.addClass('hideing');
2886                 (function(){
2887                     if (!this.el.hasClass('hideing')) {
2888                         return; // it's been shown again...
2889                     }
2890                     this.el.removeClass('show');
2891                     this.el.removeClass('hideing');
2892                 }).defer(150,this);
2893                 
2894             }else{
2895                  this.el.removeClass('show');
2896             }
2897             this.fireEvent('hide', this);
2898         }
2899     },
2900     isVisible : function()
2901     {
2902         
2903         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2904         
2905     },
2906
2907     addButton : function(str, cb)
2908     {
2909
2910
2911         var b = Roo.apply({}, { html : str } );
2912         b.xns = b.xns || Roo.bootstrap;
2913         b.xtype = b.xtype || 'Button';
2914         if (typeof(b.listeners) == 'undefined') {
2915             b.listeners = { click : cb.createDelegate(this)  };
2916         }
2917
2918         var btn = Roo.factory(b);
2919
2920         btn.render(this.el.select('.modal-footer div').first());
2921
2922         return btn;
2923
2924     },
2925
2926     setDefaultButton : function(btn)
2927     {
2928         //this.el.select('.modal-footer').()
2929     },
2930     diff : false,
2931
2932     resizeTo: function(w,h)
2933     {
2934         // skip.. ?? why??
2935
2936         this.dialogEl.setWidth(w);
2937         if (this.diff === false) {
2938             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2939         }
2940
2941         this.bodyEl.setHeight(h-this.diff);
2942
2943         this.fireEvent('resize', this);
2944
2945     },
2946     setContentSize  : function(w, h)
2947     {
2948
2949     },
2950     onButtonClick: function(btn,e)
2951     {
2952         //Roo.log([a,b,c]);
2953         this.fireEvent('btnclick', btn.name, e);
2954     },
2955      /**
2956      * Set the title of the Dialog
2957      * @param {String} str new Title
2958      */
2959     setTitle: function(str) {
2960         this.titleEl.dom.innerHTML = str;
2961     },
2962     /**
2963      * Set the body of the Dialog
2964      * @param {String} str new Title
2965      */
2966     setBody: function(str) {
2967         this.bodyEl.dom.innerHTML = str;
2968     },
2969     /**
2970      * Set the body of the Dialog using the template
2971      * @param {Obj} data - apply this data to the template and replace the body contents.
2972      */
2973     applyBody: function(obj)
2974     {
2975         if (!this.tmpl) {
2976             Roo.log("Error - using apply Body without a template");
2977             //code
2978         }
2979         this.tmpl.overwrite(this.bodyEl, obj);
2980     }
2981
2982 });
2983
2984
2985 Roo.apply(Roo.bootstrap.Modal,  {
2986     /**
2987          * Button config that displays a single OK button
2988          * @type Object
2989          */
2990         OK :  [{
2991             name : 'ok',
2992             weight : 'primary',
2993             html : 'OK'
2994         }],
2995         /**
2996          * Button config that displays Yes and No buttons
2997          * @type Object
2998          */
2999         YESNO : [
3000             {
3001                 name  : 'no',
3002                 html : 'No'
3003             },
3004             {
3005                 name  :'yes',
3006                 weight : 'primary',
3007                 html : 'Yes'
3008             }
3009         ],
3010
3011         /**
3012          * Button config that displays OK and Cancel buttons
3013          * @type Object
3014          */
3015         OKCANCEL : [
3016             {
3017                name : 'cancel',
3018                 html : 'Cancel'
3019             },
3020             {
3021                 name : 'ok',
3022                 weight : 'primary',
3023                 html : 'OK'
3024             }
3025         ],
3026         /**
3027          * Button config that displays Yes, No and Cancel buttons
3028          * @type Object
3029          */
3030         YESNOCANCEL : [
3031             {
3032                 name : 'yes',
3033                 weight : 'primary',
3034                 html : 'Yes'
3035             },
3036             {
3037                 name : 'no',
3038                 html : 'No'
3039             },
3040             {
3041                 name : 'cancel',
3042                 html : 'Cancel'
3043             }
3044         ],
3045         
3046         zIndex : 10001
3047 });
3048 /*
3049  * - LGPL
3050  *
3051  * messagebox - can be used as a replace
3052  * 
3053  */
3054 /**
3055  * @class Roo.MessageBox
3056  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3057  * Example usage:
3058  *<pre><code>
3059 // Basic alert:
3060 Roo.Msg.alert('Status', 'Changes saved successfully.');
3061
3062 // Prompt for user data:
3063 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3064     if (btn == 'ok'){
3065         // process text value...
3066     }
3067 });
3068
3069 // Show a dialog using config options:
3070 Roo.Msg.show({
3071    title:'Save Changes?',
3072    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3073    buttons: Roo.Msg.YESNOCANCEL,
3074    fn: processResult,
3075    animEl: 'elId'
3076 });
3077 </code></pre>
3078  * @singleton
3079  */
3080 Roo.bootstrap.MessageBox = function(){
3081     var dlg, opt, mask, waitTimer;
3082     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3083     var buttons, activeTextEl, bwidth;
3084
3085     
3086     // private
3087     var handleButton = function(button){
3088         dlg.hide();
3089         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3090     };
3091
3092     // private
3093     var handleHide = function(){
3094         if(opt && opt.cls){
3095             dlg.el.removeClass(opt.cls);
3096         }
3097         //if(waitTimer){
3098         //    Roo.TaskMgr.stop(waitTimer);
3099         //    waitTimer = null;
3100         //}
3101     };
3102
3103     // private
3104     var updateButtons = function(b){
3105         var width = 0;
3106         if(!b){
3107             buttons["ok"].hide();
3108             buttons["cancel"].hide();
3109             buttons["yes"].hide();
3110             buttons["no"].hide();
3111             //dlg.footer.dom.style.display = 'none';
3112             return width;
3113         }
3114         dlg.footerEl.dom.style.display = '';
3115         for(var k in buttons){
3116             if(typeof buttons[k] != "function"){
3117                 if(b[k]){
3118                     buttons[k].show();
3119                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3120                     width += buttons[k].el.getWidth()+15;
3121                 }else{
3122                     buttons[k].hide();
3123                 }
3124             }
3125         }
3126         return width;
3127     };
3128
3129     // private
3130     var handleEsc = function(d, k, e){
3131         if(opt && opt.closable !== false){
3132             dlg.hide();
3133         }
3134         if(e){
3135             e.stopEvent();
3136         }
3137     };
3138
3139     return {
3140         /**
3141          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3142          * @return {Roo.BasicDialog} The BasicDialog element
3143          */
3144         getDialog : function(){
3145            if(!dlg){
3146                 dlg = new Roo.bootstrap.Modal( {
3147                     //draggable: true,
3148                     //resizable:false,
3149                     //constraintoviewport:false,
3150                     //fixedcenter:true,
3151                     //collapsible : false,
3152                     //shim:true,
3153                     //modal: true,
3154                 //    width: 'auto',
3155                   //  height:100,
3156                     //buttonAlign:"center",
3157                     closeClick : function(){
3158                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3159                             handleButton("no");
3160                         }else{
3161                             handleButton("cancel");
3162                         }
3163                     }
3164                 });
3165                 dlg.render();
3166                 dlg.on("hide", handleHide);
3167                 mask = dlg.mask;
3168                 //dlg.addKeyListener(27, handleEsc);
3169                 buttons = {};
3170                 this.buttons = buttons;
3171                 var bt = this.buttonText;
3172                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3173                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3174                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3175                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3176                 //Roo.log(buttons);
3177                 bodyEl = dlg.bodyEl.createChild({
3178
3179                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3180                         '<textarea class="roo-mb-textarea"></textarea>' +
3181                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3182                 });
3183                 msgEl = bodyEl.dom.firstChild;
3184                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3185                 textboxEl.enableDisplayMode();
3186                 textboxEl.addKeyListener([10,13], function(){
3187                     if(dlg.isVisible() && opt && opt.buttons){
3188                         if(opt.buttons.ok){
3189                             handleButton("ok");
3190                         }else if(opt.buttons.yes){
3191                             handleButton("yes");
3192                         }
3193                     }
3194                 });
3195                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3196                 textareaEl.enableDisplayMode();
3197                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3198                 progressEl.enableDisplayMode();
3199                 
3200                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3201                 var pf = progressEl.dom.firstChild;
3202                 if (pf) {
3203                     pp = Roo.get(pf.firstChild);
3204                     pp.setHeight(pf.offsetHeight);
3205                 }
3206                 
3207             }
3208             return dlg;
3209         },
3210
3211         /**
3212          * Updates the message box body text
3213          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3214          * the XHTML-compliant non-breaking space character '&amp;#160;')
3215          * @return {Roo.MessageBox} This message box
3216          */
3217         updateText : function(text)
3218         {
3219             if(!dlg.isVisible() && !opt.width){
3220                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3221                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3222             }
3223             msgEl.innerHTML = text || '&#160;';
3224       
3225             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3226             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3227             var w = Math.max(
3228                     Math.min(opt.width || cw , this.maxWidth), 
3229                     Math.max(opt.minWidth || this.minWidth, bwidth)
3230             );
3231             if(opt.prompt){
3232                 activeTextEl.setWidth(w);
3233             }
3234             if(dlg.isVisible()){
3235                 dlg.fixedcenter = false;
3236             }
3237             // to big, make it scroll. = But as usual stupid IE does not support
3238             // !important..
3239             
3240             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3241                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3242                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3243             } else {
3244                 bodyEl.dom.style.height = '';
3245                 bodyEl.dom.style.overflowY = '';
3246             }
3247             if (cw > w) {
3248                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3249             } else {
3250                 bodyEl.dom.style.overflowX = '';
3251             }
3252             
3253             dlg.setContentSize(w, bodyEl.getHeight());
3254             if(dlg.isVisible()){
3255                 dlg.fixedcenter = true;
3256             }
3257             return this;
3258         },
3259
3260         /**
3261          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3262          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3263          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3264          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3265          * @return {Roo.MessageBox} This message box
3266          */
3267         updateProgress : function(value, text){
3268             if(text){
3269                 this.updateText(text);
3270             }
3271             
3272             if (pp) { // weird bug on my firefox - for some reason this is not defined
3273                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3274                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3275             }
3276             return this;
3277         },        
3278
3279         /**
3280          * Returns true if the message box is currently displayed
3281          * @return {Boolean} True if the message box is visible, else false
3282          */
3283         isVisible : function(){
3284             return dlg && dlg.isVisible();  
3285         },
3286
3287         /**
3288          * Hides the message box if it is displayed
3289          */
3290         hide : function(){
3291             if(this.isVisible()){
3292                 dlg.hide();
3293             }  
3294         },
3295
3296         /**
3297          * Displays a new message box, or reinitializes an existing message box, based on the config options
3298          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3299          * The following config object properties are supported:
3300          * <pre>
3301 Property    Type             Description
3302 ----------  ---------------  ------------------------------------------------------------------------------------
3303 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3304                                    closes (defaults to undefined)
3305 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3306                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3307 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3308                                    progress and wait dialogs will ignore this property and always hide the
3309                                    close button as they can only be closed programmatically.
3310 cls               String           A custom CSS class to apply to the message box element
3311 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3312                                    displayed (defaults to 75)
3313 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3314                                    function will be btn (the name of the button that was clicked, if applicable,
3315                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3316                                    Progress and wait dialogs will ignore this option since they do not respond to
3317                                    user actions and can only be closed programmatically, so any required function
3318                                    should be called by the same code after it closes the dialog.
3319 icon              String           A CSS class that provides a background image to be used as an icon for
3320                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3321 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3322 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3323 modal             Boolean          False to allow user interaction with the page while the message box is
3324                                    displayed (defaults to true)
3325 msg               String           A string that will replace the existing message box body text (defaults
3326                                    to the XHTML-compliant non-breaking space character '&#160;')
3327 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3328 progress          Boolean          True to display a progress bar (defaults to false)
3329 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3330 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3331 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3332 title             String           The title text
3333 value             String           The string value to set into the active textbox element if displayed
3334 wait              Boolean          True to display a progress bar (defaults to false)
3335 width             Number           The width of the dialog in pixels
3336 </pre>
3337          *
3338          * Example usage:
3339          * <pre><code>
3340 Roo.Msg.show({
3341    title: 'Address',
3342    msg: 'Please enter your address:',
3343    width: 300,
3344    buttons: Roo.MessageBox.OKCANCEL,
3345    multiline: true,
3346    fn: saveAddress,
3347    animEl: 'addAddressBtn'
3348 });
3349 </code></pre>
3350          * @param {Object} config Configuration options
3351          * @return {Roo.MessageBox} This message box
3352          */
3353         show : function(options)
3354         {
3355             
3356             // this causes nightmares if you show one dialog after another
3357             // especially on callbacks..
3358              
3359             if(this.isVisible()){
3360                 
3361                 this.hide();
3362                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3363                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3364                 Roo.log("New Dialog Message:" +  options.msg )
3365                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3366                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3367                 
3368             }
3369             var d = this.getDialog();
3370             opt = options;
3371             d.setTitle(opt.title || "&#160;");
3372             d.closeEl.setDisplayed(opt.closable !== false);
3373             activeTextEl = textboxEl;
3374             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3375             if(opt.prompt){
3376                 if(opt.multiline){
3377                     textboxEl.hide();
3378                     textareaEl.show();
3379                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3380                         opt.multiline : this.defaultTextHeight);
3381                     activeTextEl = textareaEl;
3382                 }else{
3383                     textboxEl.show();
3384                     textareaEl.hide();
3385                 }
3386             }else{
3387                 textboxEl.hide();
3388                 textareaEl.hide();
3389             }
3390             progressEl.setDisplayed(opt.progress === true);
3391             this.updateProgress(0);
3392             activeTextEl.dom.value = opt.value || "";
3393             if(opt.prompt){
3394                 dlg.setDefaultButton(activeTextEl);
3395             }else{
3396                 var bs = opt.buttons;
3397                 var db = null;
3398                 if(bs && bs.ok){
3399                     db = buttons["ok"];
3400                 }else if(bs && bs.yes){
3401                     db = buttons["yes"];
3402                 }
3403                 dlg.setDefaultButton(db);
3404             }
3405             bwidth = updateButtons(opt.buttons);
3406             this.updateText(opt.msg);
3407             if(opt.cls){
3408                 d.el.addClass(opt.cls);
3409             }
3410             d.proxyDrag = opt.proxyDrag === true;
3411             d.modal = opt.modal !== false;
3412             d.mask = opt.modal !== false ? mask : false;
3413             if(!d.isVisible()){
3414                 // force it to the end of the z-index stack so it gets a cursor in FF
3415                 document.body.appendChild(dlg.el.dom);
3416                 d.animateTarget = null;
3417                 d.show(options.animEl);
3418             }
3419             return this;
3420         },
3421
3422         /**
3423          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3424          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3425          * and closing the message box when the process is complete.
3426          * @param {String} title The title bar text
3427          * @param {String} msg The message box body text
3428          * @return {Roo.MessageBox} This message box
3429          */
3430         progress : function(title, msg){
3431             this.show({
3432                 title : title,
3433                 msg : msg,
3434                 buttons: false,
3435                 progress:true,
3436                 closable:false,
3437                 minWidth: this.minProgressWidth,
3438                 modal : true
3439             });
3440             return this;
3441         },
3442
3443         /**
3444          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3445          * If a callback function is passed it will be called after the user clicks the button, and the
3446          * id of the button that was clicked will be passed as the only parameter to the callback
3447          * (could also be the top-right close button).
3448          * @param {String} title The title bar text
3449          * @param {String} msg The message box body text
3450          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3451          * @param {Object} scope (optional) The scope of the callback function
3452          * @return {Roo.MessageBox} This message box
3453          */
3454         alert : function(title, msg, fn, scope)
3455         {
3456             this.show({
3457                 title : title,
3458                 msg : msg,
3459                 buttons: this.OK,
3460                 fn: fn,
3461                 closable : false,
3462                 scope : scope,
3463                 modal : true
3464             });
3465             return this;
3466         },
3467
3468         /**
3469          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3470          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3471          * You are responsible for closing the message box when the process is complete.
3472          * @param {String} msg The message box body text
3473          * @param {String} title (optional) The title bar text
3474          * @return {Roo.MessageBox} This message box
3475          */
3476         wait : function(msg, title){
3477             this.show({
3478                 title : title,
3479                 msg : msg,
3480                 buttons: false,
3481                 closable:false,
3482                 progress:true,
3483                 modal:true,
3484                 width:300,
3485                 wait:true
3486             });
3487             waitTimer = Roo.TaskMgr.start({
3488                 run: function(i){
3489                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3490                 },
3491                 interval: 1000
3492             });
3493             return this;
3494         },
3495
3496         /**
3497          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3498          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3499          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3500          * @param {String} title The title bar text
3501          * @param {String} msg The message box body text
3502          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3503          * @param {Object} scope (optional) The scope of the callback function
3504          * @return {Roo.MessageBox} This message box
3505          */
3506         confirm : function(title, msg, fn, scope){
3507             this.show({
3508                 title : title,
3509                 msg : msg,
3510                 buttons: this.YESNO,
3511                 fn: fn,
3512                 scope : scope,
3513                 modal : true
3514             });
3515             return this;
3516         },
3517
3518         /**
3519          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3520          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3521          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3522          * (could also be the top-right close button) and the text that was entered will be passed as the two
3523          * parameters to the callback.
3524          * @param {String} title The title bar text
3525          * @param {String} msg The message box body text
3526          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3527          * @param {Object} scope (optional) The scope of the callback function
3528          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3529          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3530          * @return {Roo.MessageBox} This message box
3531          */
3532         prompt : function(title, msg, fn, scope, multiline){
3533             this.show({
3534                 title : title,
3535                 msg : msg,
3536                 buttons: this.OKCANCEL,
3537                 fn: fn,
3538                 minWidth:250,
3539                 scope : scope,
3540                 prompt:true,
3541                 multiline: multiline,
3542                 modal : true
3543             });
3544             return this;
3545         },
3546
3547         /**
3548          * Button config that displays a single OK button
3549          * @type Object
3550          */
3551         OK : {ok:true},
3552         /**
3553          * Button config that displays Yes and No buttons
3554          * @type Object
3555          */
3556         YESNO : {yes:true, no:true},
3557         /**
3558          * Button config that displays OK and Cancel buttons
3559          * @type Object
3560          */
3561         OKCANCEL : {ok:true, cancel:true},
3562         /**
3563          * Button config that displays Yes, No and Cancel buttons
3564          * @type Object
3565          */
3566         YESNOCANCEL : {yes:true, no:true, cancel:true},
3567
3568         /**
3569          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3570          * @type Number
3571          */
3572         defaultTextHeight : 75,
3573         /**
3574          * The maximum width in pixels of the message box (defaults to 600)
3575          * @type Number
3576          */
3577         maxWidth : 600,
3578         /**
3579          * The minimum width in pixels of the message box (defaults to 100)
3580          * @type Number
3581          */
3582         minWidth : 100,
3583         /**
3584          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3585          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3586          * @type Number
3587          */
3588         minProgressWidth : 250,
3589         /**
3590          * An object containing the default button text strings that can be overriden for localized language support.
3591          * Supported properties are: ok, cancel, yes and no.
3592          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3593          * @type Object
3594          */
3595         buttonText : {
3596             ok : "OK",
3597             cancel : "Cancel",
3598             yes : "Yes",
3599             no : "No"
3600         }
3601     };
3602 }();
3603
3604 /**
3605  * Shorthand for {@link Roo.MessageBox}
3606  */
3607 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3608 Roo.Msg = Roo.Msg || Roo.MessageBox;
3609 /*
3610  * - LGPL
3611  *
3612  * navbar
3613  * 
3614  */
3615
3616 /**
3617  * @class Roo.bootstrap.Navbar
3618  * @extends Roo.bootstrap.Component
3619  * Bootstrap Navbar class
3620
3621  * @constructor
3622  * Create a new Navbar
3623  * @param {Object} config The config object
3624  */
3625
3626
3627 Roo.bootstrap.Navbar = function(config){
3628     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3629     this.addEvents({
3630         // raw events
3631         /**
3632          * @event beforetoggle
3633          * Fire before toggle the menu
3634          * @param {Roo.EventObject} e
3635          */
3636         "beforetoggle" : true
3637     });
3638 };
3639
3640 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3641     
3642     
3643    
3644     // private
3645     navItems : false,
3646     loadMask : false,
3647     
3648     
3649     getAutoCreate : function(){
3650         
3651         
3652         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3653         
3654     },
3655     
3656     initEvents :function ()
3657     {
3658         //Roo.log(this.el.select('.navbar-toggle',true));
3659         this.el.select('.navbar-toggle',true).on('click', function() {
3660             if(this.fireEvent('beforetoggle', this) !== false){
3661                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3662             }
3663             
3664         }, this);
3665         
3666         var mark = {
3667             tag: "div",
3668             cls:"x-dlg-mask"
3669         };
3670         
3671         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3672         
3673         var size = this.el.getSize();
3674         this.maskEl.setSize(size.width, size.height);
3675         this.maskEl.enableDisplayMode("block");
3676         this.maskEl.hide();
3677         
3678         if(this.loadMask){
3679             this.maskEl.show();
3680         }
3681     },
3682     
3683     
3684     getChildContainer : function()
3685     {
3686         if (this.el.select('.collapse').getCount()) {
3687             return this.el.select('.collapse',true).first();
3688         }
3689         
3690         return this.el;
3691     },
3692     
3693     mask : function()
3694     {
3695         this.maskEl.show();
3696     },
3697     
3698     unmask : function()
3699     {
3700         this.maskEl.hide();
3701     } 
3702     
3703     
3704     
3705     
3706 });
3707
3708
3709
3710  
3711
3712  /*
3713  * - LGPL
3714  *
3715  * navbar
3716  * 
3717  */
3718
3719 /**
3720  * @class Roo.bootstrap.NavSimplebar
3721  * @extends Roo.bootstrap.Navbar
3722  * Bootstrap Sidebar class
3723  *
3724  * @cfg {Boolean} inverse is inverted color
3725  * 
3726  * @cfg {String} type (nav | pills | tabs)
3727  * @cfg {Boolean} arrangement stacked | justified
3728  * @cfg {String} align (left | right) alignment
3729  * 
3730  * @cfg {Boolean} main (true|false) main nav bar? default false
3731  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3732  * 
3733  * @cfg {String} tag (header|footer|nav|div) default is nav 
3734
3735  * 
3736  * 
3737  * 
3738  * @constructor
3739  * Create a new Sidebar
3740  * @param {Object} config The config object
3741  */
3742
3743
3744 Roo.bootstrap.NavSimplebar = function(config){
3745     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3746 };
3747
3748 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3749     
3750     inverse: false,
3751     
3752     type: false,
3753     arrangement: '',
3754     align : false,
3755     
3756     
3757     
3758     main : false,
3759     
3760     
3761     tag : false,
3762     
3763     
3764     getAutoCreate : function(){
3765         
3766         
3767         var cfg = {
3768             tag : this.tag || 'div',
3769             cls : 'navbar'
3770         };
3771           
3772         
3773         cfg.cn = [
3774             {
3775                 cls: 'nav',
3776                 tag : 'ul'
3777             }
3778         ];
3779         
3780          
3781         this.type = this.type || 'nav';
3782         if (['tabs','pills'].indexOf(this.type)!==-1) {
3783             cfg.cn[0].cls += ' nav-' + this.type
3784         
3785         
3786         } else {
3787             if (this.type!=='nav') {
3788                 Roo.log('nav type must be nav/tabs/pills')
3789             }
3790             cfg.cn[0].cls += ' navbar-nav'
3791         }
3792         
3793         
3794         
3795         
3796         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3797             cfg.cn[0].cls += ' nav-' + this.arrangement;
3798         }
3799         
3800         
3801         if (this.align === 'right') {
3802             cfg.cn[0].cls += ' navbar-right';
3803         }
3804         
3805         if (this.inverse) {
3806             cfg.cls += ' navbar-inverse';
3807             
3808         }
3809         
3810         
3811         return cfg;
3812     
3813         
3814     }
3815     
3816     
3817     
3818 });
3819
3820
3821
3822  
3823
3824  
3825        /*
3826  * - LGPL
3827  *
3828  * navbar
3829  * 
3830  */
3831
3832 /**
3833  * @class Roo.bootstrap.NavHeaderbar
3834  * @extends Roo.bootstrap.NavSimplebar
3835  * Bootstrap Sidebar class
3836  *
3837  * @cfg {String} brand what is brand
3838  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3839  * @cfg {String} brand_href href of the brand
3840  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3841  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3842  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3843  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3844  * 
3845  * @constructor
3846  * Create a new Sidebar
3847  * @param {Object} config The config object
3848  */
3849
3850
3851 Roo.bootstrap.NavHeaderbar = function(config){
3852     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3853       
3854 };
3855
3856 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3857     
3858     position: '',
3859     brand: '',
3860     brand_href: false,
3861     srButton : true,
3862     autohide : false,
3863     desktopCenter : false,
3864    
3865     
3866     getAutoCreate : function(){
3867         
3868         var   cfg = {
3869             tag: this.nav || 'nav',
3870             cls: 'navbar',
3871             role: 'navigation',
3872             cn: []
3873         };
3874         
3875         var cn = cfg.cn;
3876         if (this.desktopCenter) {
3877             cn.push({cls : 'container', cn : []});
3878             cn = cn[0].cn;
3879         }
3880         
3881         if(this.srButton){
3882             cn.push({
3883                 tag: 'div',
3884                 cls: 'navbar-header',
3885                 cn: [
3886                     {
3887                         tag: 'button',
3888                         type: 'button',
3889                         cls: 'navbar-toggle',
3890                         'data-toggle': 'collapse',
3891                         cn: [
3892                             {
3893                                 tag: 'span',
3894                                 cls: 'sr-only',
3895                                 html: 'Toggle navigation'
3896                             },
3897                             {
3898                                 tag: 'span',
3899                                 cls: 'icon-bar'
3900                             },
3901                             {
3902                                 tag: 'span',
3903                                 cls: 'icon-bar'
3904                             },
3905                             {
3906                                 tag: 'span',
3907                                 cls: 'icon-bar'
3908                             }
3909                         ]
3910                     }
3911                 ]
3912             });
3913         }
3914         
3915         cn.push({
3916             tag: 'div',
3917             cls: 'collapse navbar-collapse',
3918             cn : []
3919         });
3920         
3921         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3922         
3923         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3924             cfg.cls += ' navbar-' + this.position;
3925             
3926             // tag can override this..
3927             
3928             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3929         }
3930         
3931         if (this.brand !== '') {
3932             cn[0].cn.push({
3933                 tag: 'a',
3934                 href: this.brand_href ? this.brand_href : '#',
3935                 cls: 'navbar-brand',
3936                 cn: [
3937                 this.brand
3938                 ]
3939             });
3940         }
3941         
3942         if(this.main){
3943             cfg.cls += ' main-nav';
3944         }
3945         
3946         
3947         return cfg;
3948
3949         
3950     },
3951     getHeaderChildContainer : function()
3952     {
3953         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3954             return this.el.select('.navbar-header',true).first();
3955         }
3956         
3957         return this.getChildContainer();
3958     },
3959     
3960     
3961     initEvents : function()
3962     {
3963         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3964         
3965         if (this.autohide) {
3966             
3967             var prevScroll = 0;
3968             var ft = this.el;
3969             
3970             Roo.get(document).on('scroll',function(e) {
3971                 var ns = Roo.get(document).getScroll().top;
3972                 var os = prevScroll;
3973                 prevScroll = ns;
3974                 
3975                 if(ns > os){
3976                     ft.removeClass('slideDown');
3977                     ft.addClass('slideUp');
3978                     return;
3979                 }
3980                 ft.removeClass('slideUp');
3981                 ft.addClass('slideDown');
3982                  
3983               
3984           },this);
3985         }
3986     }    
3987     
3988 });
3989
3990
3991
3992  
3993
3994  /*
3995  * - LGPL
3996  *
3997  * navbar
3998  * 
3999  */
4000
4001 /**
4002  * @class Roo.bootstrap.NavSidebar
4003  * @extends Roo.bootstrap.Navbar
4004  * Bootstrap Sidebar class
4005  * 
4006  * @constructor
4007  * Create a new Sidebar
4008  * @param {Object} config The config object
4009  */
4010
4011
4012 Roo.bootstrap.NavSidebar = function(config){
4013     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4014 };
4015
4016 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4017     
4018     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4019     
4020     getAutoCreate : function(){
4021         
4022         
4023         return  {
4024             tag: 'div',
4025             cls: 'sidebar sidebar-nav'
4026         };
4027     
4028         
4029     }
4030     
4031     
4032     
4033 });
4034
4035
4036
4037  
4038
4039  /*
4040  * - LGPL
4041  *
4042  * nav group
4043  * 
4044  */
4045
4046 /**
4047  * @class Roo.bootstrap.NavGroup
4048  * @extends Roo.bootstrap.Component
4049  * Bootstrap NavGroup class
4050  * @cfg {String} align (left|right)
4051  * @cfg {Boolean} inverse
4052  * @cfg {String} type (nav|pills|tab) default nav
4053  * @cfg {String} navId - reference Id for navbar.
4054
4055  * 
4056  * @constructor
4057  * Create a new nav group
4058  * @param {Object} config The config object
4059  */
4060
4061 Roo.bootstrap.NavGroup = function(config){
4062     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4063     this.navItems = [];
4064    
4065     Roo.bootstrap.NavGroup.register(this);
4066      this.addEvents({
4067         /**
4068              * @event changed
4069              * Fires when the active item changes
4070              * @param {Roo.bootstrap.NavGroup} this
4071              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4072              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4073          */
4074         'changed': true
4075      });
4076     
4077 };
4078
4079 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4080     
4081     align: '',
4082     inverse: false,
4083     form: false,
4084     type: 'nav',
4085     navId : '',
4086     // private
4087     
4088     navItems : false, 
4089     
4090     getAutoCreate : function()
4091     {
4092         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4093         
4094         cfg = {
4095             tag : 'ul',
4096             cls: 'nav' 
4097         };
4098         
4099         if (['tabs','pills'].indexOf(this.type)!==-1) {
4100             cfg.cls += ' nav-' + this.type
4101         } else {
4102             if (this.type!=='nav') {
4103                 Roo.log('nav type must be nav/tabs/pills')
4104             }
4105             cfg.cls += ' navbar-nav'
4106         }
4107         
4108         if (this.parent() && this.parent().sidebar) {
4109             cfg = {
4110                 tag: 'ul',
4111                 cls: 'dashboard-menu sidebar-menu'
4112             };
4113             
4114             return cfg;
4115         }
4116         
4117         if (this.form === true) {
4118             cfg = {
4119                 tag: 'form',
4120                 cls: 'navbar-form'
4121             };
4122             
4123             if (this.align === 'right') {
4124                 cfg.cls += ' navbar-right';
4125             } else {
4126                 cfg.cls += ' navbar-left';
4127             }
4128         }
4129         
4130         if (this.align === 'right') {
4131             cfg.cls += ' navbar-right';
4132         }
4133         
4134         if (this.inverse) {
4135             cfg.cls += ' navbar-inverse';
4136             
4137         }
4138         
4139         
4140         return cfg;
4141     },
4142     /**
4143     * sets the active Navigation item
4144     * @param {Roo.bootstrap.NavItem} the new current navitem
4145     */
4146     setActiveItem : function(item)
4147     {
4148         var prev = false;
4149         Roo.each(this.navItems, function(v){
4150             if (v == item) {
4151                 return ;
4152             }
4153             if (v.isActive()) {
4154                 v.setActive(false, true);
4155                 prev = v;
4156                 
4157             }
4158             
4159         });
4160
4161         item.setActive(true, true);
4162         this.fireEvent('changed', this, item, prev);
4163         
4164         
4165     },
4166     /**
4167     * gets the active Navigation item
4168     * @return {Roo.bootstrap.NavItem} the current navitem
4169     */
4170     getActive : function()
4171     {
4172         
4173         var prev = false;
4174         Roo.each(this.navItems, function(v){
4175             
4176             if (v.isActive()) {
4177                 prev = v;
4178                 
4179             }
4180             
4181         });
4182         return prev;
4183     },
4184     
4185     indexOfNav : function()
4186     {
4187         
4188         var prev = false;
4189         Roo.each(this.navItems, function(v,i){
4190             
4191             if (v.isActive()) {
4192                 prev = i;
4193                 
4194             }
4195             
4196         });
4197         return prev;
4198     },
4199     /**
4200     * adds a Navigation item
4201     * @param {Roo.bootstrap.NavItem} the navitem to add
4202     */
4203     addItem : function(cfg)
4204     {
4205         var cn = new Roo.bootstrap.NavItem(cfg);
4206         this.register(cn);
4207         cn.parentId = this.id;
4208         cn.onRender(this.el, null);
4209         return cn;
4210     },
4211     /**
4212     * register a Navigation item
4213     * @param {Roo.bootstrap.NavItem} the navitem to add
4214     */
4215     register : function(item)
4216     {
4217         this.navItems.push( item);
4218         item.navId = this.navId;
4219     
4220     },
4221     
4222     /**
4223     * clear all the Navigation item
4224     */
4225    
4226     clearAll : function()
4227     {
4228         this.navItems = [];
4229         this.el.dom.innerHTML = '';
4230     },
4231     
4232     getNavItem: function(tabId)
4233     {
4234         var ret = false;
4235         Roo.each(this.navItems, function(e) {
4236             if (e.tabId == tabId) {
4237                ret =  e;
4238                return false;
4239             }
4240             return true;
4241             
4242         });
4243         return ret;
4244     },
4245     
4246     setActiveNext : function()
4247     {
4248         var i = this.indexOfNav(this.getActive());
4249         if (i > this.navItems.length) {
4250             return;
4251         }
4252         this.setActiveItem(this.navItems[i+1]);
4253     },
4254     setActivePrev : function()
4255     {
4256         var i = this.indexOfNav(this.getActive());
4257         if (i  < 1) {
4258             return;
4259         }
4260         this.setActiveItem(this.navItems[i-1]);
4261     },
4262     clearWasActive : function(except) {
4263         Roo.each(this.navItems, function(e) {
4264             if (e.tabId != except.tabId && e.was_active) {
4265                e.was_active = false;
4266                return false;
4267             }
4268             return true;
4269             
4270         });
4271     },
4272     getWasActive : function ()
4273     {
4274         var r = false;
4275         Roo.each(this.navItems, function(e) {
4276             if (e.was_active) {
4277                r = e;
4278                return false;
4279             }
4280             return true;
4281             
4282         });
4283         return r;
4284     }
4285     
4286     
4287 });
4288
4289  
4290 Roo.apply(Roo.bootstrap.NavGroup, {
4291     
4292     groups: {},
4293      /**
4294     * register a Navigation Group
4295     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4296     */
4297     register : function(navgrp)
4298     {
4299         this.groups[navgrp.navId] = navgrp;
4300         
4301     },
4302     /**
4303     * fetch a Navigation Group based on the navigation ID
4304     * @param {string} the navgroup to add
4305     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4306     */
4307     get: function(navId) {
4308         if (typeof(this.groups[navId]) == 'undefined') {
4309             return false;
4310             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4311         }
4312         return this.groups[navId] ;
4313     }
4314     
4315     
4316     
4317 });
4318
4319  /*
4320  * - LGPL
4321  *
4322  * row
4323  * 
4324  */
4325
4326 /**
4327  * @class Roo.bootstrap.NavItem
4328  * @extends Roo.bootstrap.Component
4329  * Bootstrap Navbar.NavItem class
4330  * @cfg {String} href  link to
4331  * @cfg {String} html content of button
4332  * @cfg {String} badge text inside badge
4333  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4334  * @cfg {String} glyphicon name of glyphicon
4335  * @cfg {String} icon name of font awesome icon
4336  * @cfg {Boolean} active Is item active
4337  * @cfg {Boolean} disabled Is item disabled
4338  
4339  * @cfg {Boolean} preventDefault (true | false) default false
4340  * @cfg {String} tabId the tab that this item activates.
4341  * @cfg {String} tagtype (a|span) render as a href or span?
4342  * @cfg {Boolean} animateRef (true|false) link to element default false  
4343   
4344  * @constructor
4345  * Create a new Navbar Item
4346  * @param {Object} config The config object
4347  */
4348 Roo.bootstrap.NavItem = function(config){
4349     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4350     this.addEvents({
4351         // raw events
4352         /**
4353          * @event click
4354          * The raw click event for the entire grid.
4355          * @param {Roo.EventObject} e
4356          */
4357         "click" : true,
4358          /**
4359             * @event changed
4360             * Fires when the active item active state changes
4361             * @param {Roo.bootstrap.NavItem} this
4362             * @param {boolean} state the new state
4363              
4364          */
4365         'changed': true,
4366         /**
4367             * @event scrollto
4368             * Fires when scroll to element
4369             * @param {Roo.bootstrap.NavItem} this
4370             * @param {Object} options
4371             * @param {Roo.EventObject} e
4372              
4373          */
4374         'scrollto': true
4375     });
4376    
4377 };
4378
4379 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4380     
4381     href: false,
4382     html: '',
4383     badge: '',
4384     icon: false,
4385     glyphicon: false,
4386     active: false,
4387     preventDefault : false,
4388     tabId : false,
4389     tagtype : 'a',
4390     disabled : false,
4391     animateRef : false,
4392     was_active : false,
4393     
4394     getAutoCreate : function(){
4395          
4396         var cfg = {
4397             tag: 'li',
4398             cls: 'nav-item'
4399             
4400         };
4401         
4402         if (this.active) {
4403             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4404         }
4405         if (this.disabled) {
4406             cfg.cls += ' disabled';
4407         }
4408         
4409         if (this.href || this.html || this.glyphicon || this.icon) {
4410             cfg.cn = [
4411                 {
4412                     tag: this.tagtype,
4413                     href : this.href || "#",
4414                     html: this.html || ''
4415                 }
4416             ];
4417             
4418             if (this.icon) {
4419                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4420             }
4421
4422             if(this.glyphicon) {
4423                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4424             }
4425             
4426             if (this.menu) {
4427                 
4428                 cfg.cn[0].html += " <span class='caret'></span>";
4429              
4430             }
4431             
4432             if (this.badge !== '') {
4433                  
4434                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4435             }
4436         }
4437         
4438         
4439         
4440         return cfg;
4441     },
4442     initEvents: function() 
4443     {
4444         if (typeof (this.menu) != 'undefined') {
4445             this.menu.parentType = this.xtype;
4446             this.menu.triggerEl = this.el;
4447             this.menu = this.addxtype(Roo.apply({}, this.menu));
4448         }
4449         
4450         this.el.select('a',true).on('click', this.onClick, this);
4451         
4452         if(this.tagtype == 'span'){
4453             this.el.select('span',true).on('click', this.onClick, this);
4454         }
4455        
4456         // at this point parent should be available..
4457         this.parent().register(this);
4458     },
4459     
4460     onClick : function(e)
4461     {
4462         if (e.getTarget('.dropdown-menu-item')) {
4463             // did you click on a menu itemm.... - then don't trigger onclick..
4464             return;
4465         }
4466         
4467         if(
4468                 this.preventDefault || 
4469                 this.href == '#' 
4470         ){
4471             Roo.log("NavItem - prevent Default?");
4472             e.preventDefault();
4473         }
4474         
4475         if (this.disabled) {
4476             return;
4477         }
4478         
4479         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4480         if (tg && tg.transition) {
4481             Roo.log("waiting for the transitionend");
4482             return;
4483         }
4484         
4485         
4486         
4487         //Roo.log("fire event clicked");
4488         if(this.fireEvent('click', this, e) === false){
4489             return;
4490         };
4491         
4492         if(this.tagtype == 'span'){
4493             return;
4494         }
4495         
4496         //Roo.log(this.href);
4497         var ael = this.el.select('a',true).first();
4498         //Roo.log(ael);
4499         
4500         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4501             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4502             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4503                 return; // ignore... - it's a 'hash' to another page.
4504             }
4505             Roo.log("NavItem - prevent Default?");
4506             e.preventDefault();
4507             this.scrollToElement(e);
4508         }
4509         
4510         
4511         var p =  this.parent();
4512    
4513         if (['tabs','pills'].indexOf(p.type)!==-1) {
4514             if (typeof(p.setActiveItem) !== 'undefined') {
4515                 p.setActiveItem(this);
4516             }
4517         }
4518         
4519         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4520         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4521             // remove the collapsed menu expand...
4522             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4523         }
4524     },
4525     
4526     isActive: function () {
4527         return this.active
4528     },
4529     setActive : function(state, fire, is_was_active)
4530     {
4531         if (this.active && !state && this.navId) {
4532             this.was_active = true;
4533             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4534             if (nv) {
4535                 nv.clearWasActive(this);
4536             }
4537             
4538         }
4539         this.active = state;
4540         
4541         if (!state ) {
4542             this.el.removeClass('active');
4543         } else if (!this.el.hasClass('active')) {
4544             this.el.addClass('active');
4545         }
4546         if (fire) {
4547             this.fireEvent('changed', this, state);
4548         }
4549         
4550         // show a panel if it's registered and related..
4551         
4552         if (!this.navId || !this.tabId || !state || is_was_active) {
4553             return;
4554         }
4555         
4556         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4557         if (!tg) {
4558             return;
4559         }
4560         var pan = tg.getPanelByName(this.tabId);
4561         if (!pan) {
4562             return;
4563         }
4564         // if we can not flip to new panel - go back to old nav highlight..
4565         if (false == tg.showPanel(pan)) {
4566             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4567             if (nv) {
4568                 var onav = nv.getWasActive();
4569                 if (onav) {
4570                     onav.setActive(true, false, true);
4571                 }
4572             }
4573             
4574         }
4575         
4576         
4577         
4578     },
4579      // this should not be here...
4580     setDisabled : function(state)
4581     {
4582         this.disabled = state;
4583         if (!state ) {
4584             this.el.removeClass('disabled');
4585         } else if (!this.el.hasClass('disabled')) {
4586             this.el.addClass('disabled');
4587         }
4588         
4589     },
4590     
4591     /**
4592      * Fetch the element to display the tooltip on.
4593      * @return {Roo.Element} defaults to this.el
4594      */
4595     tooltipEl : function()
4596     {
4597         return this.el.select('' + this.tagtype + '', true).first();
4598     },
4599     
4600     scrollToElement : function(e)
4601     {
4602         var c = document.body;
4603         
4604         /*
4605          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4606          */
4607         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4608             c = document.documentElement;
4609         }
4610         
4611         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4612         
4613         if(!target){
4614             return;
4615         }
4616
4617         var o = target.calcOffsetsTo(c);
4618         
4619         var options = {
4620             target : target,
4621             value : o[1]
4622         };
4623         
4624         this.fireEvent('scrollto', this, options, e);
4625         
4626         Roo.get(c).scrollTo('top', options.value, true);
4627         
4628         return;
4629     }
4630 });
4631  
4632
4633  /*
4634  * - LGPL
4635  *
4636  * sidebar item
4637  *
4638  *  li
4639  *    <span> icon </span>
4640  *    <span> text </span>
4641  *    <span>badge </span>
4642  */
4643
4644 /**
4645  * @class Roo.bootstrap.NavSidebarItem
4646  * @extends Roo.bootstrap.NavItem
4647  * Bootstrap Navbar.NavSidebarItem class
4648  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4649  * {Boolean} open is the menu open
4650  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4651  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4652  * {String} buttonSize (sm|md|lg)the extra classes for the button
4653  * {Boolean} showArrow show arrow next to the text (default true)
4654  * @constructor
4655  * Create a new Navbar Button
4656  * @param {Object} config The config object
4657  */
4658 Roo.bootstrap.NavSidebarItem = function(config){
4659     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4660     this.addEvents({
4661         // raw events
4662         /**
4663          * @event click
4664          * The raw click event for the entire grid.
4665          * @param {Roo.EventObject} e
4666          */
4667         "click" : true,
4668          /**
4669             * @event changed
4670             * Fires when the active item active state changes
4671             * @param {Roo.bootstrap.NavSidebarItem} this
4672             * @param {boolean} state the new state
4673              
4674          */
4675         'changed': true
4676     });
4677    
4678 };
4679
4680 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4681     
4682     badgeWeight : 'default',
4683     
4684     open: false,
4685     
4686     buttonView : false,
4687     
4688     buttonWeight : 'default',
4689     
4690     buttonSize : 'md',
4691     
4692     showArrow : true,
4693     
4694     getAutoCreate : function(){
4695         
4696         
4697         var a = {
4698                 tag: 'a',
4699                 href : this.href || '#',
4700                 cls: '',
4701                 html : '',
4702                 cn : []
4703         };
4704         
4705         if(this.buttonView){
4706             a = {
4707                 tag: 'button',
4708                 href : this.href || '#',
4709                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4710                 html : this.html,
4711                 cn : []
4712             };
4713         }
4714         
4715         var cfg = {
4716             tag: 'li',
4717             cls: '',
4718             cn: [ a ]
4719         };
4720         
4721         if (this.active) {
4722             cfg.cls += ' active';
4723         }
4724         
4725         if (this.disabled) {
4726             cfg.cls += ' disabled';
4727         }
4728         if (this.open) {
4729             cfg.cls += ' open x-open';
4730         }
4731         // left icon..
4732         if (this.glyphicon || this.icon) {
4733             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4734             a.cn.push({ tag : 'i', cls : c }) ;
4735         }
4736         
4737         if(!this.buttonView){
4738             var span = {
4739                 tag: 'span',
4740                 html : this.html || ''
4741             };
4742
4743             a.cn.push(span);
4744             
4745         }
4746         
4747         if (this.badge !== '') {
4748             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4749         }
4750         
4751         if (this.menu) {
4752             
4753             if(this.showArrow){
4754                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4755             }
4756             
4757             a.cls += ' dropdown-toggle treeview' ;
4758         }
4759         
4760         return cfg;
4761     },
4762     
4763     initEvents : function()
4764     { 
4765         if (typeof (this.menu) != 'undefined') {
4766             this.menu.parentType = this.xtype;
4767             this.menu.triggerEl = this.el;
4768             this.menu = this.addxtype(Roo.apply({}, this.menu));
4769         }
4770         
4771         this.el.on('click', this.onClick, this);
4772         
4773         if(this.badge !== ''){
4774             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4775         }
4776         
4777     },
4778     
4779     onClick : function(e)
4780     {
4781         if(this.disabled){
4782             e.preventDefault();
4783             return;
4784         }
4785         
4786         if(this.preventDefault){
4787             e.preventDefault();
4788         }
4789         
4790         this.fireEvent('click', this);
4791     },
4792     
4793     disable : function()
4794     {
4795         this.setDisabled(true);
4796     },
4797     
4798     enable : function()
4799     {
4800         this.setDisabled(false);
4801     },
4802     
4803     setDisabled : function(state)
4804     {
4805         if(this.disabled == state){
4806             return;
4807         }
4808         
4809         this.disabled = state;
4810         
4811         if (state) {
4812             this.el.addClass('disabled');
4813             return;
4814         }
4815         
4816         this.el.removeClass('disabled');
4817         
4818         return;
4819     },
4820     
4821     setActive : function(state)
4822     {
4823         if(this.active == state){
4824             return;
4825         }
4826         
4827         this.active = state;
4828         
4829         if (state) {
4830             this.el.addClass('active');
4831             return;
4832         }
4833         
4834         this.el.removeClass('active');
4835         
4836         return;
4837     },
4838     
4839     isActive: function () 
4840     {
4841         return this.active;
4842     },
4843     
4844     setBadge : function(str)
4845     {
4846         if(!this.badgeEl){
4847             return;
4848         }
4849         
4850         this.badgeEl.dom.innerHTML = str;
4851     }
4852     
4853    
4854      
4855  
4856 });
4857  
4858
4859  /*
4860  * - LGPL
4861  *
4862  * row
4863  * 
4864  */
4865
4866 /**
4867  * @class Roo.bootstrap.Row
4868  * @extends Roo.bootstrap.Component
4869  * Bootstrap Row class (contains columns...)
4870  * 
4871  * @constructor
4872  * Create a new Row
4873  * @param {Object} config The config object
4874  */
4875
4876 Roo.bootstrap.Row = function(config){
4877     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4878 };
4879
4880 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4881     
4882     getAutoCreate : function(){
4883        return {
4884             cls: 'row clearfix'
4885        };
4886     }
4887     
4888     
4889 });
4890
4891  
4892
4893  /*
4894  * - LGPL
4895  *
4896  * element
4897  * 
4898  */
4899
4900 /**
4901  * @class Roo.bootstrap.Element
4902  * @extends Roo.bootstrap.Component
4903  * Bootstrap Element class
4904  * @cfg {String} html contents of the element
4905  * @cfg {String} tag tag of the element
4906  * @cfg {String} cls class of the element
4907  * @cfg {Boolean} preventDefault (true|false) default false
4908  * @cfg {Boolean} clickable (true|false) default false
4909  * 
4910  * @constructor
4911  * Create a new Element
4912  * @param {Object} config The config object
4913  */
4914
4915 Roo.bootstrap.Element = function(config){
4916     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4917     
4918     this.addEvents({
4919         // raw events
4920         /**
4921          * @event click
4922          * When a element is chick
4923          * @param {Roo.bootstrap.Element} this
4924          * @param {Roo.EventObject} e
4925          */
4926         "click" : true
4927     });
4928 };
4929
4930 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4931     
4932     tag: 'div',
4933     cls: '',
4934     html: '',
4935     preventDefault: false, 
4936     clickable: false,
4937     
4938     getAutoCreate : function(){
4939         
4940         var cfg = {
4941             tag: this.tag,
4942             // cls: this.cls, double assign in parent class Component.js :: onRender
4943             html: this.html
4944         };
4945         
4946         return cfg;
4947     },
4948     
4949     initEvents: function() 
4950     {
4951         Roo.bootstrap.Element.superclass.initEvents.call(this);
4952         
4953         if(this.clickable){
4954             this.el.on('click', this.onClick, this);
4955         }
4956         
4957     },
4958     
4959     onClick : function(e)
4960     {
4961         if(this.preventDefault){
4962             e.preventDefault();
4963         }
4964         
4965         this.fireEvent('click', this, e);
4966     },
4967     
4968     getValue : function()
4969     {
4970         return this.el.dom.innerHTML;
4971     },
4972     
4973     setValue : function(value)
4974     {
4975         this.el.dom.innerHTML = value;
4976     }
4977    
4978 });
4979
4980  
4981
4982  /*
4983  * - LGPL
4984  *
4985  * pagination
4986  * 
4987  */
4988
4989 /**
4990  * @class Roo.bootstrap.Pagination
4991  * @extends Roo.bootstrap.Component
4992  * Bootstrap Pagination class
4993  * @cfg {String} size xs | sm | md | lg
4994  * @cfg {Boolean} inverse false | true
4995  * 
4996  * @constructor
4997  * Create a new Pagination
4998  * @param {Object} config The config object
4999  */
5000
5001 Roo.bootstrap.Pagination = function(config){
5002     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5003 };
5004
5005 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5006     
5007     cls: false,
5008     size: false,
5009     inverse: false,
5010     
5011     getAutoCreate : function(){
5012         var cfg = {
5013             tag: 'ul',
5014                 cls: 'pagination'
5015         };
5016         if (this.inverse) {
5017             cfg.cls += ' inverse';
5018         }
5019         if (this.html) {
5020             cfg.html=this.html;
5021         }
5022         if (this.cls) {
5023             cfg.cls += " " + this.cls;
5024         }
5025         return cfg;
5026     }
5027    
5028 });
5029
5030  
5031
5032  /*
5033  * - LGPL
5034  *
5035  * Pagination item
5036  * 
5037  */
5038
5039
5040 /**
5041  * @class Roo.bootstrap.PaginationItem
5042  * @extends Roo.bootstrap.Component
5043  * Bootstrap PaginationItem class
5044  * @cfg {String} html text
5045  * @cfg {String} href the link
5046  * @cfg {Boolean} preventDefault (true | false) default true
5047  * @cfg {Boolean} active (true | false) default false
5048  * @cfg {Boolean} disabled default false
5049  * 
5050  * 
5051  * @constructor
5052  * Create a new PaginationItem
5053  * @param {Object} config The config object
5054  */
5055
5056
5057 Roo.bootstrap.PaginationItem = function(config){
5058     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5059     this.addEvents({
5060         // raw events
5061         /**
5062          * @event click
5063          * The raw click event for the entire grid.
5064          * @param {Roo.EventObject} e
5065          */
5066         "click" : true
5067     });
5068 };
5069
5070 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5071     
5072     href : false,
5073     html : false,
5074     preventDefault: true,
5075     active : false,
5076     cls : false,
5077     disabled: false,
5078     
5079     getAutoCreate : function(){
5080         var cfg= {
5081             tag: 'li',
5082             cn: [
5083                 {
5084                     tag : 'a',
5085                     href : this.href ? this.href : '#',
5086                     html : this.html ? this.html : ''
5087                 }
5088             ]
5089         };
5090         
5091         if(this.cls){
5092             cfg.cls = this.cls;
5093         }
5094         
5095         if(this.disabled){
5096             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5097         }
5098         
5099         if(this.active){
5100             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5101         }
5102         
5103         return cfg;
5104     },
5105     
5106     initEvents: function() {
5107         
5108         this.el.on('click', this.onClick, this);
5109         
5110     },
5111     onClick : function(e)
5112     {
5113         Roo.log('PaginationItem on click ');
5114         if(this.preventDefault){
5115             e.preventDefault();
5116         }
5117         
5118         if(this.disabled){
5119             return;
5120         }
5121         
5122         this.fireEvent('click', this, e);
5123     }
5124    
5125 });
5126
5127  
5128
5129  /*
5130  * - LGPL
5131  *
5132  * slider
5133  * 
5134  */
5135
5136
5137 /**
5138  * @class Roo.bootstrap.Slider
5139  * @extends Roo.bootstrap.Component
5140  * Bootstrap Slider class
5141  *    
5142  * @constructor
5143  * Create a new Slider
5144  * @param {Object} config The config object
5145  */
5146
5147 Roo.bootstrap.Slider = function(config){
5148     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5149 };
5150
5151 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5152     
5153     getAutoCreate : function(){
5154         
5155         var cfg = {
5156             tag: 'div',
5157             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5158             cn: [
5159                 {
5160                     tag: 'a',
5161                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5162                 }
5163             ]
5164         };
5165         
5166         return cfg;
5167     }
5168    
5169 });
5170
5171  /*
5172  * Based on:
5173  * Ext JS Library 1.1.1
5174  * Copyright(c) 2006-2007, Ext JS, LLC.
5175  *
5176  * Originally Released Under LGPL - original licence link has changed is not relivant.
5177  *
5178  * Fork - LGPL
5179  * <script type="text/javascript">
5180  */
5181  
5182
5183 /**
5184  * @class Roo.grid.ColumnModel
5185  * @extends Roo.util.Observable
5186  * This is the default implementation of a ColumnModel used by the Grid. It defines
5187  * the columns in the grid.
5188  * <br>Usage:<br>
5189  <pre><code>
5190  var colModel = new Roo.grid.ColumnModel([
5191         {header: "Ticker", width: 60, sortable: true, locked: true},
5192         {header: "Company Name", width: 150, sortable: true},
5193         {header: "Market Cap.", width: 100, sortable: true},
5194         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5195         {header: "Employees", width: 100, sortable: true, resizable: false}
5196  ]);
5197  </code></pre>
5198  * <p>
5199  
5200  * The config options listed for this class are options which may appear in each
5201  * individual column definition.
5202  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5203  * @constructor
5204  * @param {Object} config An Array of column config objects. See this class's
5205  * config objects for details.
5206 */
5207 Roo.grid.ColumnModel = function(config){
5208         /**
5209      * The config passed into the constructor
5210      */
5211     this.config = config;
5212     this.lookup = {};
5213
5214     // if no id, create one
5215     // if the column does not have a dataIndex mapping,
5216     // map it to the order it is in the config
5217     for(var i = 0, len = config.length; i < len; i++){
5218         var c = config[i];
5219         if(typeof c.dataIndex == "undefined"){
5220             c.dataIndex = i;
5221         }
5222         if(typeof c.renderer == "string"){
5223             c.renderer = Roo.util.Format[c.renderer];
5224         }
5225         if(typeof c.id == "undefined"){
5226             c.id = Roo.id();
5227         }
5228         if(c.editor && c.editor.xtype){
5229             c.editor  = Roo.factory(c.editor, Roo.grid);
5230         }
5231         if(c.editor && c.editor.isFormField){
5232             c.editor = new Roo.grid.GridEditor(c.editor);
5233         }
5234         this.lookup[c.id] = c;
5235     }
5236
5237     /**
5238      * The width of columns which have no width specified (defaults to 100)
5239      * @type Number
5240      */
5241     this.defaultWidth = 100;
5242
5243     /**
5244      * Default sortable of columns which have no sortable specified (defaults to false)
5245      * @type Boolean
5246      */
5247     this.defaultSortable = false;
5248
5249     this.addEvents({
5250         /**
5251              * @event widthchange
5252              * Fires when the width of a column changes.
5253              * @param {ColumnModel} this
5254              * @param {Number} columnIndex The column index
5255              * @param {Number} newWidth The new width
5256              */
5257             "widthchange": true,
5258         /**
5259              * @event headerchange
5260              * Fires when the text of a header changes.
5261              * @param {ColumnModel} this
5262              * @param {Number} columnIndex The column index
5263              * @param {Number} newText The new header text
5264              */
5265             "headerchange": true,
5266         /**
5267              * @event hiddenchange
5268              * Fires when a column is hidden or "unhidden".
5269              * @param {ColumnModel} this
5270              * @param {Number} columnIndex The column index
5271              * @param {Boolean} hidden true if hidden, false otherwise
5272              */
5273             "hiddenchange": true,
5274             /**
5275          * @event columnmoved
5276          * Fires when a column is moved.
5277          * @param {ColumnModel} this
5278          * @param {Number} oldIndex
5279          * @param {Number} newIndex
5280          */
5281         "columnmoved" : true,
5282         /**
5283          * @event columlockchange
5284          * Fires when a column's locked state is changed
5285          * @param {ColumnModel} this
5286          * @param {Number} colIndex
5287          * @param {Boolean} locked true if locked
5288          */
5289         "columnlockchange" : true
5290     });
5291     Roo.grid.ColumnModel.superclass.constructor.call(this);
5292 };
5293 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5294     /**
5295      * @cfg {String} header The header text to display in the Grid view.
5296      */
5297     /**
5298      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5299      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5300      * specified, the column's index is used as an index into the Record's data Array.
5301      */
5302     /**
5303      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5304      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5305      */
5306     /**
5307      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5308      * Defaults to the value of the {@link #defaultSortable} property.
5309      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5310      */
5311     /**
5312      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5313      */
5314     /**
5315      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5316      */
5317     /**
5318      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5319      */
5320     /**
5321      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5322      */
5323     /**
5324      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5325      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5326      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5327      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5328      */
5329        /**
5330      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5331      */
5332     /**
5333      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5334      */
5335     /**
5336      * @cfg {String} cursor (Optional)
5337      */
5338     /**
5339      * @cfg {String} tooltip (Optional)
5340      */
5341     /**
5342      * @cfg {Number} xs (Optional)
5343      */
5344     /**
5345      * @cfg {Number} sm (Optional)
5346      */
5347     /**
5348      * @cfg {Number} md (Optional)
5349      */
5350     /**
5351      * @cfg {Number} lg (Optional)
5352      */
5353     /**
5354      * Returns the id of the column at the specified index.
5355      * @param {Number} index The column index
5356      * @return {String} the id
5357      */
5358     getColumnId : function(index){
5359         return this.config[index].id;
5360     },
5361
5362     /**
5363      * Returns the column for a specified id.
5364      * @param {String} id The column id
5365      * @return {Object} the column
5366      */
5367     getColumnById : function(id){
5368         return this.lookup[id];
5369     },
5370
5371     
5372     /**
5373      * Returns the column for a specified dataIndex.
5374      * @param {String} dataIndex The column dataIndex
5375      * @return {Object|Boolean} the column or false if not found
5376      */
5377     getColumnByDataIndex: function(dataIndex){
5378         var index = this.findColumnIndex(dataIndex);
5379         return index > -1 ? this.config[index] : false;
5380     },
5381     
5382     /**
5383      * Returns the index for a specified column id.
5384      * @param {String} id The column id
5385      * @return {Number} the index, or -1 if not found
5386      */
5387     getIndexById : function(id){
5388         for(var i = 0, len = this.config.length; i < len; i++){
5389             if(this.config[i].id == id){
5390                 return i;
5391             }
5392         }
5393         return -1;
5394     },
5395     
5396     /**
5397      * Returns the index for a specified column dataIndex.
5398      * @param {String} dataIndex The column dataIndex
5399      * @return {Number} the index, or -1 if not found
5400      */
5401     
5402     findColumnIndex : function(dataIndex){
5403         for(var i = 0, len = this.config.length; i < len; i++){
5404             if(this.config[i].dataIndex == dataIndex){
5405                 return i;
5406             }
5407         }
5408         return -1;
5409     },
5410     
5411     
5412     moveColumn : function(oldIndex, newIndex){
5413         var c = this.config[oldIndex];
5414         this.config.splice(oldIndex, 1);
5415         this.config.splice(newIndex, 0, c);
5416         this.dataMap = null;
5417         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5418     },
5419
5420     isLocked : function(colIndex){
5421         return this.config[colIndex].locked === true;
5422     },
5423
5424     setLocked : function(colIndex, value, suppressEvent){
5425         if(this.isLocked(colIndex) == value){
5426             return;
5427         }
5428         this.config[colIndex].locked = value;
5429         if(!suppressEvent){
5430             this.fireEvent("columnlockchange", this, colIndex, value);
5431         }
5432     },
5433
5434     getTotalLockedWidth : function(){
5435         var totalWidth = 0;
5436         for(var i = 0; i < this.config.length; i++){
5437             if(this.isLocked(i) && !this.isHidden(i)){
5438                 this.totalWidth += this.getColumnWidth(i);
5439             }
5440         }
5441         return totalWidth;
5442     },
5443
5444     getLockedCount : function(){
5445         for(var i = 0, len = this.config.length; i < len; i++){
5446             if(!this.isLocked(i)){
5447                 return i;
5448             }
5449         }
5450         
5451         return this.config.length;
5452     },
5453
5454     /**
5455      * Returns the number of columns.
5456      * @return {Number}
5457      */
5458     getColumnCount : function(visibleOnly){
5459         if(visibleOnly === true){
5460             var c = 0;
5461             for(var i = 0, len = this.config.length; i < len; i++){
5462                 if(!this.isHidden(i)){
5463                     c++;
5464                 }
5465             }
5466             return c;
5467         }
5468         return this.config.length;
5469     },
5470
5471     /**
5472      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5473      * @param {Function} fn
5474      * @param {Object} scope (optional)
5475      * @return {Array} result
5476      */
5477     getColumnsBy : function(fn, scope){
5478         var r = [];
5479         for(var i = 0, len = this.config.length; i < len; i++){
5480             var c = this.config[i];
5481             if(fn.call(scope||this, c, i) === true){
5482                 r[r.length] = c;
5483             }
5484         }
5485         return r;
5486     },
5487
5488     /**
5489      * Returns true if the specified column is sortable.
5490      * @param {Number} col The column index
5491      * @return {Boolean}
5492      */
5493     isSortable : function(col){
5494         if(typeof this.config[col].sortable == "undefined"){
5495             return this.defaultSortable;
5496         }
5497         return this.config[col].sortable;
5498     },
5499
5500     /**
5501      * Returns the rendering (formatting) function defined for the column.
5502      * @param {Number} col The column index.
5503      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5504      */
5505     getRenderer : function(col){
5506         if(!this.config[col].renderer){
5507             return Roo.grid.ColumnModel.defaultRenderer;
5508         }
5509         return this.config[col].renderer;
5510     },
5511
5512     /**
5513      * Sets the rendering (formatting) function for a column.
5514      * @param {Number} col The column index
5515      * @param {Function} fn The function to use to process the cell's raw data
5516      * to return HTML markup for the grid view. The render function is called with
5517      * the following parameters:<ul>
5518      * <li>Data value.</li>
5519      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5520      * <li>css A CSS style string to apply to the table cell.</li>
5521      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5522      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5523      * <li>Row index</li>
5524      * <li>Column index</li>
5525      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5526      */
5527     setRenderer : function(col, fn){
5528         this.config[col].renderer = fn;
5529     },
5530
5531     /**
5532      * Returns the width for the specified column.
5533      * @param {Number} col The column index
5534      * @return {Number}
5535      */
5536     getColumnWidth : function(col){
5537         return this.config[col].width * 1 || this.defaultWidth;
5538     },
5539
5540     /**
5541      * Sets the width for a column.
5542      * @param {Number} col The column index
5543      * @param {Number} width The new width
5544      */
5545     setColumnWidth : function(col, width, suppressEvent){
5546         this.config[col].width = width;
5547         this.totalWidth = null;
5548         if(!suppressEvent){
5549              this.fireEvent("widthchange", this, col, width);
5550         }
5551     },
5552
5553     /**
5554      * Returns the total width of all columns.
5555      * @param {Boolean} includeHidden True to include hidden column widths
5556      * @return {Number}
5557      */
5558     getTotalWidth : function(includeHidden){
5559         if(!this.totalWidth){
5560             this.totalWidth = 0;
5561             for(var i = 0, len = this.config.length; i < len; i++){
5562                 if(includeHidden || !this.isHidden(i)){
5563                     this.totalWidth += this.getColumnWidth(i);
5564                 }
5565             }
5566         }
5567         return this.totalWidth;
5568     },
5569
5570     /**
5571      * Returns the header for the specified column.
5572      * @param {Number} col The column index
5573      * @return {String}
5574      */
5575     getColumnHeader : function(col){
5576         return this.config[col].header;
5577     },
5578
5579     /**
5580      * Sets the header for a column.
5581      * @param {Number} col The column index
5582      * @param {String} header The new header
5583      */
5584     setColumnHeader : function(col, header){
5585         this.config[col].header = header;
5586         this.fireEvent("headerchange", this, col, header);
5587     },
5588
5589     /**
5590      * Returns the tooltip for the specified column.
5591      * @param {Number} col The column index
5592      * @return {String}
5593      */
5594     getColumnTooltip : function(col){
5595             return this.config[col].tooltip;
5596     },
5597     /**
5598      * Sets the tooltip for a column.
5599      * @param {Number} col The column index
5600      * @param {String} tooltip The new tooltip
5601      */
5602     setColumnTooltip : function(col, tooltip){
5603             this.config[col].tooltip = tooltip;
5604     },
5605
5606     /**
5607      * Returns the dataIndex for the specified column.
5608      * @param {Number} col The column index
5609      * @return {Number}
5610      */
5611     getDataIndex : function(col){
5612         return this.config[col].dataIndex;
5613     },
5614
5615     /**
5616      * Sets the dataIndex for a column.
5617      * @param {Number} col The column index
5618      * @param {Number} dataIndex The new dataIndex
5619      */
5620     setDataIndex : function(col, dataIndex){
5621         this.config[col].dataIndex = dataIndex;
5622     },
5623
5624     
5625     
5626     /**
5627      * Returns true if the cell is editable.
5628      * @param {Number} colIndex The column index
5629      * @param {Number} rowIndex The row index - this is nto actually used..?
5630      * @return {Boolean}
5631      */
5632     isCellEditable : function(colIndex, rowIndex){
5633         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5634     },
5635
5636     /**
5637      * Returns the editor defined for the cell/column.
5638      * return false or null to disable editing.
5639      * @param {Number} colIndex The column index
5640      * @param {Number} rowIndex The row index
5641      * @return {Object}
5642      */
5643     getCellEditor : function(colIndex, rowIndex){
5644         return this.config[colIndex].editor;
5645     },
5646
5647     /**
5648      * Sets if a column is editable.
5649      * @param {Number} col The column index
5650      * @param {Boolean} editable True if the column is editable
5651      */
5652     setEditable : function(col, editable){
5653         this.config[col].editable = editable;
5654     },
5655
5656
5657     /**
5658      * Returns true if the column is hidden.
5659      * @param {Number} colIndex The column index
5660      * @return {Boolean}
5661      */
5662     isHidden : function(colIndex){
5663         return this.config[colIndex].hidden;
5664     },
5665
5666
5667     /**
5668      * Returns true if the column width cannot be changed
5669      */
5670     isFixed : function(colIndex){
5671         return this.config[colIndex].fixed;
5672     },
5673
5674     /**
5675      * Returns true if the column can be resized
5676      * @return {Boolean}
5677      */
5678     isResizable : function(colIndex){
5679         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5680     },
5681     /**
5682      * Sets if a column is hidden.
5683      * @param {Number} colIndex The column index
5684      * @param {Boolean} hidden True if the column is hidden
5685      */
5686     setHidden : function(colIndex, hidden){
5687         this.config[colIndex].hidden = hidden;
5688         this.totalWidth = null;
5689         this.fireEvent("hiddenchange", this, colIndex, hidden);
5690     },
5691
5692     /**
5693      * Sets the editor for a column.
5694      * @param {Number} col The column index
5695      * @param {Object} editor The editor object
5696      */
5697     setEditor : function(col, editor){
5698         this.config[col].editor = editor;
5699     }
5700 });
5701
5702 Roo.grid.ColumnModel.defaultRenderer = function(value)
5703 {
5704     if(typeof value == "object") {
5705         return value;
5706     }
5707         if(typeof value == "string" && value.length < 1){
5708             return "&#160;";
5709         }
5710     
5711         return String.format("{0}", value);
5712 };
5713
5714 // Alias for backwards compatibility
5715 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5716 /*
5717  * Based on:
5718  * Ext JS Library 1.1.1
5719  * Copyright(c) 2006-2007, Ext JS, LLC.
5720  *
5721  * Originally Released Under LGPL - original licence link has changed is not relivant.
5722  *
5723  * Fork - LGPL
5724  * <script type="text/javascript">
5725  */
5726  
5727 /**
5728  * @class Roo.LoadMask
5729  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5730  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5731  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5732  * element's UpdateManager load indicator and will be destroyed after the initial load.
5733  * @constructor
5734  * Create a new LoadMask
5735  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5736  * @param {Object} config The config object
5737  */
5738 Roo.LoadMask = function(el, config){
5739     this.el = Roo.get(el);
5740     Roo.apply(this, config);
5741     if(this.store){
5742         this.store.on('beforeload', this.onBeforeLoad, this);
5743         this.store.on('load', this.onLoad, this);
5744         this.store.on('loadexception', this.onLoadException, this);
5745         this.removeMask = false;
5746     }else{
5747         var um = this.el.getUpdateManager();
5748         um.showLoadIndicator = false; // disable the default indicator
5749         um.on('beforeupdate', this.onBeforeLoad, this);
5750         um.on('update', this.onLoad, this);
5751         um.on('failure', this.onLoad, this);
5752         this.removeMask = true;
5753     }
5754 };
5755
5756 Roo.LoadMask.prototype = {
5757     /**
5758      * @cfg {Boolean} removeMask
5759      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5760      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5761      */
5762     /**
5763      * @cfg {String} msg
5764      * The text to display in a centered loading message box (defaults to 'Loading...')
5765      */
5766     msg : 'Loading...',
5767     /**
5768      * @cfg {String} msgCls
5769      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5770      */
5771     msgCls : 'x-mask-loading',
5772
5773     /**
5774      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5775      * @type Boolean
5776      */
5777     disabled: false,
5778
5779     /**
5780      * Disables the mask to prevent it from being displayed
5781      */
5782     disable : function(){
5783        this.disabled = true;
5784     },
5785
5786     /**
5787      * Enables the mask so that it can be displayed
5788      */
5789     enable : function(){
5790         this.disabled = false;
5791     },
5792     
5793     onLoadException : function()
5794     {
5795         Roo.log(arguments);
5796         
5797         if (typeof(arguments[3]) != 'undefined') {
5798             Roo.MessageBox.alert("Error loading",arguments[3]);
5799         } 
5800         /*
5801         try {
5802             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5803                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5804             }   
5805         } catch(e) {
5806             
5807         }
5808         */
5809     
5810         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5811     },
5812     // private
5813     onLoad : function()
5814     {
5815         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5816     },
5817
5818     // private
5819     onBeforeLoad : function(){
5820         if(!this.disabled){
5821             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5822         }
5823     },
5824
5825     // private
5826     destroy : function(){
5827         if(this.store){
5828             this.store.un('beforeload', this.onBeforeLoad, this);
5829             this.store.un('load', this.onLoad, this);
5830             this.store.un('loadexception', this.onLoadException, this);
5831         }else{
5832             var um = this.el.getUpdateManager();
5833             um.un('beforeupdate', this.onBeforeLoad, this);
5834             um.un('update', this.onLoad, this);
5835             um.un('failure', this.onLoad, this);
5836         }
5837     }
5838 };/*
5839  * - LGPL
5840  *
5841  * table
5842  * 
5843  */
5844
5845 /**
5846  * @class Roo.bootstrap.Table
5847  * @extends Roo.bootstrap.Component
5848  * Bootstrap Table class
5849  * @cfg {String} cls table class
5850  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5851  * @cfg {String} bgcolor Specifies the background color for a table
5852  * @cfg {Number} border Specifies whether the table cells should have borders or not
5853  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5854  * @cfg {Number} cellspacing Specifies the space between cells
5855  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5856  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5857  * @cfg {String} sortable Specifies that the table should be sortable
5858  * @cfg {String} summary Specifies a summary of the content of a table
5859  * @cfg {Number} width Specifies the width of a table
5860  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5861  * 
5862  * @cfg {boolean} striped Should the rows be alternative striped
5863  * @cfg {boolean} bordered Add borders to the table
5864  * @cfg {boolean} hover Add hover highlighting
5865  * @cfg {boolean} condensed Format condensed
5866  * @cfg {boolean} responsive Format condensed
5867  * @cfg {Boolean} loadMask (true|false) default false
5868  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5869  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5870  * @cfg {Boolean} rowSelection (true|false) default false
5871  * @cfg {Boolean} cellSelection (true|false) default false
5872  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5873  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5874  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5875  
5876  * 
5877  * @constructor
5878  * Create a new Table
5879  * @param {Object} config The config object
5880  */
5881
5882 Roo.bootstrap.Table = function(config){
5883     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5884     
5885   
5886     
5887     // BC...
5888     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5889     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5890     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5891     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5892     
5893     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5894     if (this.sm) {
5895         this.sm.grid = this;
5896         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5897         this.sm = this.selModel;
5898         this.sm.xmodule = this.xmodule || false;
5899     }
5900     
5901     if (this.cm && typeof(this.cm.config) == 'undefined') {
5902         this.colModel = new Roo.grid.ColumnModel(this.cm);
5903         this.cm = this.colModel;
5904         this.cm.xmodule = this.xmodule || false;
5905     }
5906     if (this.store) {
5907         this.store= Roo.factory(this.store, Roo.data);
5908         this.ds = this.store;
5909         this.ds.xmodule = this.xmodule || false;
5910          
5911     }
5912     if (this.footer && this.store) {
5913         this.footer.dataSource = this.ds;
5914         this.footer = Roo.factory(this.footer);
5915     }
5916     
5917     /** @private */
5918     this.addEvents({
5919         /**
5920          * @event cellclick
5921          * Fires when a cell is clicked
5922          * @param {Roo.bootstrap.Table} this
5923          * @param {Roo.Element} el
5924          * @param {Number} rowIndex
5925          * @param {Number} columnIndex
5926          * @param {Roo.EventObject} e
5927          */
5928         "cellclick" : true,
5929         /**
5930          * @event celldblclick
5931          * Fires when a cell is double clicked
5932          * @param {Roo.bootstrap.Table} this
5933          * @param {Roo.Element} el
5934          * @param {Number} rowIndex
5935          * @param {Number} columnIndex
5936          * @param {Roo.EventObject} e
5937          */
5938         "celldblclick" : true,
5939         /**
5940          * @event rowclick
5941          * Fires when a row is clicked
5942          * @param {Roo.bootstrap.Table} this
5943          * @param {Roo.Element} el
5944          * @param {Number} rowIndex
5945          * @param {Roo.EventObject} e
5946          */
5947         "rowclick" : true,
5948         /**
5949          * @event rowdblclick
5950          * Fires when a row is double clicked
5951          * @param {Roo.bootstrap.Table} this
5952          * @param {Roo.Element} el
5953          * @param {Number} rowIndex
5954          * @param {Roo.EventObject} e
5955          */
5956         "rowdblclick" : true,
5957         /**
5958          * @event mouseover
5959          * Fires when a mouseover occur
5960          * @param {Roo.bootstrap.Table} this
5961          * @param {Roo.Element} el
5962          * @param {Number} rowIndex
5963          * @param {Number} columnIndex
5964          * @param {Roo.EventObject} e
5965          */
5966         "mouseover" : true,
5967         /**
5968          * @event mouseout
5969          * Fires when a mouseout occur
5970          * @param {Roo.bootstrap.Table} this
5971          * @param {Roo.Element} el
5972          * @param {Number} rowIndex
5973          * @param {Number} columnIndex
5974          * @param {Roo.EventObject} e
5975          */
5976         "mouseout" : true,
5977         /**
5978          * @event rowclass
5979          * Fires when a row is rendered, so you can change add a style to it.
5980          * @param {Roo.bootstrap.Table} this
5981          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5982          */
5983         'rowclass' : true,
5984           /**
5985          * @event rowsrendered
5986          * Fires when all the  rows have been rendered
5987          * @param {Roo.bootstrap.Table} this
5988          */
5989         'rowsrendered' : true,
5990         /**
5991          * @event contextmenu
5992          * The raw contextmenu event for the entire grid.
5993          * @param {Roo.EventObject} e
5994          */
5995         "contextmenu" : true,
5996         /**
5997          * @event rowcontextmenu
5998          * Fires when a row is right clicked
5999          * @param {Roo.bootstrap.Table} this
6000          * @param {Number} rowIndex
6001          * @param {Roo.EventObject} e
6002          */
6003         "rowcontextmenu" : true,
6004         /**
6005          * @event cellcontextmenu
6006          * Fires when a cell is right clicked
6007          * @param {Roo.bootstrap.Table} this
6008          * @param {Number} rowIndex
6009          * @param {Number} cellIndex
6010          * @param {Roo.EventObject} e
6011          */
6012          "cellcontextmenu" : true,
6013          /**
6014          * @event headercontextmenu
6015          * Fires when a header is right clicked
6016          * @param {Roo.bootstrap.Table} this
6017          * @param {Number} columnIndex
6018          * @param {Roo.EventObject} e
6019          */
6020         "headercontextmenu" : true
6021     });
6022 };
6023
6024 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6025     
6026     cls: false,
6027     align: false,
6028     bgcolor: false,
6029     border: false,
6030     cellpadding: false,
6031     cellspacing: false,
6032     frame: false,
6033     rules: false,
6034     sortable: false,
6035     summary: false,
6036     width: false,
6037     striped : false,
6038     scrollBody : false,
6039     bordered: false,
6040     hover:  false,
6041     condensed : false,
6042     responsive : false,
6043     sm : false,
6044     cm : false,
6045     store : false,
6046     loadMask : false,
6047     footerShow : true,
6048     headerShow : true,
6049   
6050     rowSelection : false,
6051     cellSelection : false,
6052     layout : false,
6053     
6054     // Roo.Element - the tbody
6055     mainBody: false,
6056     // Roo.Element - thead element
6057     mainHead: false,
6058     
6059     container: false, // used by gridpanel...
6060     
6061     lazyLoad : false,
6062     
6063     CSS : Roo.util.CSS,
6064     
6065     getAutoCreate : function()
6066     {
6067         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6068         
6069         cfg = {
6070             tag: 'table',
6071             cls : 'table',
6072             cn : []
6073         };
6074         if (this.scrollBody) {
6075             cfg.cls += ' table-body-fixed';
6076         }    
6077         if (this.striped) {
6078             cfg.cls += ' table-striped';
6079         }
6080         
6081         if (this.hover) {
6082             cfg.cls += ' table-hover';
6083         }
6084         if (this.bordered) {
6085             cfg.cls += ' table-bordered';
6086         }
6087         if (this.condensed) {
6088             cfg.cls += ' table-condensed';
6089         }
6090         if (this.responsive) {
6091             cfg.cls += ' table-responsive';
6092         }
6093         
6094         if (this.cls) {
6095             cfg.cls+=  ' ' +this.cls;
6096         }
6097         
6098         // this lot should be simplifed...
6099         
6100         if (this.align) {
6101             cfg.align=this.align;
6102         }
6103         if (this.bgcolor) {
6104             cfg.bgcolor=this.bgcolor;
6105         }
6106         if (this.border) {
6107             cfg.border=this.border;
6108         }
6109         if (this.cellpadding) {
6110             cfg.cellpadding=this.cellpadding;
6111         }
6112         if (this.cellspacing) {
6113             cfg.cellspacing=this.cellspacing;
6114         }
6115         if (this.frame) {
6116             cfg.frame=this.frame;
6117         }
6118         if (this.rules) {
6119             cfg.rules=this.rules;
6120         }
6121         if (this.sortable) {
6122             cfg.sortable=this.sortable;
6123         }
6124         if (this.summary) {
6125             cfg.summary=this.summary;
6126         }
6127         if (this.width) {
6128             cfg.width=this.width;
6129         }
6130         if (this.layout) {
6131             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6132         }
6133         
6134         if(this.store || this.cm){
6135             if(this.headerShow){
6136                 cfg.cn.push(this.renderHeader());
6137             }
6138             
6139             cfg.cn.push(this.renderBody());
6140             
6141             if(this.footerShow){
6142                 cfg.cn.push(this.renderFooter());
6143             }
6144             // where does this come from?
6145             //cfg.cls+=  ' TableGrid';
6146         }
6147         
6148         return { cn : [ cfg ] };
6149     },
6150     
6151     initEvents : function()
6152     {   
6153         if(!this.store || !this.cm){
6154             return;
6155         }
6156         if (this.selModel) {
6157             this.selModel.initEvents();
6158         }
6159         
6160         
6161         //Roo.log('initEvents with ds!!!!');
6162         
6163         this.mainBody = this.el.select('tbody', true).first();
6164         this.mainHead = this.el.select('thead', true).first();
6165         
6166         
6167         
6168         
6169         var _this = this;
6170         
6171         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6172             e.on('click', _this.sort, _this);
6173         });
6174         
6175         this.mainBody.on("click", this.onClick, this);
6176         this.mainBody.on("dblclick", this.onDblClick, this);
6177         
6178         // why is this done????? = it breaks dialogs??
6179         //this.parent().el.setStyle('position', 'relative');
6180         
6181         
6182         if (this.footer) {
6183             this.footer.parentId = this.id;
6184             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6185             
6186             if(this.lazyLoad){
6187                 this.el.select('tfoot tr td').first().addClass('hide');
6188             }
6189         } 
6190         
6191         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6192         
6193         this.store.on('load', this.onLoad, this);
6194         this.store.on('beforeload', this.onBeforeLoad, this);
6195         this.store.on('update', this.onUpdate, this);
6196         this.store.on('add', this.onAdd, this);
6197         this.store.on("clear", this.clear, this);
6198         
6199         this.el.on("contextmenu", this.onContextMenu, this);
6200         
6201         this.mainBody.on('scroll', this.onBodyScroll, this);
6202         
6203         this.cm.on("headerchange", this.onHeaderChange, this);
6204         
6205         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6206         
6207     },
6208     
6209     onContextMenu : function(e, t)
6210     {
6211         this.processEvent("contextmenu", e);
6212     },
6213     
6214     processEvent : function(name, e)
6215     {
6216         if (name != 'touchstart' ) {
6217             this.fireEvent(name, e);    
6218         }
6219         
6220         var t = e.getTarget();
6221         
6222         var cell = Roo.get(t);
6223         
6224         if(!cell){
6225             return;
6226         }
6227         
6228         if(cell.findParent('tfoot', false, true)){
6229             return;
6230         }
6231         
6232         if(cell.findParent('thead', false, true)){
6233             
6234             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6235                 cell = Roo.get(t).findParent('th', false, true);
6236                 if (!cell) {
6237                     Roo.log("failed to find th in thead?");
6238                     Roo.log(e.getTarget());
6239                     return;
6240                 }
6241             }
6242             
6243             var cellIndex = cell.dom.cellIndex;
6244             
6245             var ename = name == 'touchstart' ? 'click' : name;
6246             this.fireEvent("header" + ename, this, cellIndex, e);
6247             
6248             return;
6249         }
6250         
6251         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6252             cell = Roo.get(t).findParent('td', false, true);
6253             if (!cell) {
6254                 Roo.log("failed to find th in tbody?");
6255                 Roo.log(e.getTarget());
6256                 return;
6257             }
6258         }
6259         
6260         var row = cell.findParent('tr', false, true);
6261         var cellIndex = cell.dom.cellIndex;
6262         var rowIndex = row.dom.rowIndex - 1;
6263         
6264         if(row !== false){
6265             
6266             this.fireEvent("row" + name, this, rowIndex, e);
6267             
6268             if(cell !== false){
6269             
6270                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6271             }
6272         }
6273         
6274     },
6275     
6276     onMouseover : function(e, el)
6277     {
6278         var cell = Roo.get(el);
6279         
6280         if(!cell){
6281             return;
6282         }
6283         
6284         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6285             cell = cell.findParent('td', false, true);
6286         }
6287         
6288         var row = cell.findParent('tr', false, true);
6289         var cellIndex = cell.dom.cellIndex;
6290         var rowIndex = row.dom.rowIndex - 1; // start from 0
6291         
6292         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6293         
6294     },
6295     
6296     onMouseout : function(e, el)
6297     {
6298         var cell = Roo.get(el);
6299         
6300         if(!cell){
6301             return;
6302         }
6303         
6304         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6305             cell = cell.findParent('td', false, true);
6306         }
6307         
6308         var row = cell.findParent('tr', false, true);
6309         var cellIndex = cell.dom.cellIndex;
6310         var rowIndex = row.dom.rowIndex - 1; // start from 0
6311         
6312         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6313         
6314     },
6315     
6316     onClick : function(e, el)
6317     {
6318         var cell = Roo.get(el);
6319         
6320         if(!cell || (!this.cellSelection && !this.rowSelection)){
6321             return;
6322         }
6323         
6324         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6325             cell = cell.findParent('td', false, true);
6326         }
6327         
6328         if(!cell || typeof(cell) == 'undefined'){
6329             return;
6330         }
6331         
6332         var row = cell.findParent('tr', false, true);
6333         
6334         if(!row || typeof(row) == 'undefined'){
6335             return;
6336         }
6337         
6338         var cellIndex = cell.dom.cellIndex;
6339         var rowIndex = this.getRowIndex(row);
6340         
6341         // why??? - should these not be based on SelectionModel?
6342         if(this.cellSelection){
6343             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6344         }
6345         
6346         if(this.rowSelection){
6347             this.fireEvent('rowclick', this, row, rowIndex, e);
6348         }
6349         
6350         
6351     },
6352         
6353     onDblClick : function(e,el)
6354     {
6355         var cell = Roo.get(el);
6356         
6357         if(!cell || (!this.cellSelection && !this.rowSelection)){
6358             return;
6359         }
6360         
6361         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6362             cell = cell.findParent('td', false, true);
6363         }
6364         
6365         if(!cell || typeof(cell) == 'undefined'){
6366             return;
6367         }
6368         
6369         var row = cell.findParent('tr', false, true);
6370         
6371         if(!row || typeof(row) == 'undefined'){
6372             return;
6373         }
6374         
6375         var cellIndex = cell.dom.cellIndex;
6376         var rowIndex = this.getRowIndex(row);
6377         
6378         if(this.cellSelection){
6379             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6380         }
6381         
6382         if(this.rowSelection){
6383             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6384         }
6385     },
6386     
6387     sort : function(e,el)
6388     {
6389         var col = Roo.get(el);
6390         
6391         if(!col.hasClass('sortable')){
6392             return;
6393         }
6394         
6395         var sort = col.attr('sort');
6396         var dir = 'ASC';
6397         
6398         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6399             dir = 'DESC';
6400         }
6401         
6402         this.store.sortInfo = {field : sort, direction : dir};
6403         
6404         if (this.footer) {
6405             Roo.log("calling footer first");
6406             this.footer.onClick('first');
6407         } else {
6408         
6409             this.store.load({ params : { start : 0 } });
6410         }
6411     },
6412     
6413     renderHeader : function()
6414     {
6415         var header = {
6416             tag: 'thead',
6417             cn : []
6418         };
6419         
6420         var cm = this.cm;
6421         this.totalWidth = 0;
6422         
6423         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6424             
6425             var config = cm.config[i];
6426             
6427             var c = {
6428                 tag: 'th',
6429                 cls : 'x-hcol-' + i,
6430                 style : '',
6431                 html: cm.getColumnHeader(i)
6432             };
6433             
6434             var hh = '';
6435             
6436             if(typeof(config.sortable) != 'undefined' && config.sortable){
6437                 c.cls = 'sortable';
6438                 c.html = '<i class="glyphicon"></i>' + c.html;
6439             }
6440             
6441             if(typeof(config.lgHeader) != 'undefined'){
6442                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6443             }
6444             
6445             if(typeof(config.mdHeader) != 'undefined'){
6446                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6447             }
6448             
6449             if(typeof(config.smHeader) != 'undefined'){
6450                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6451             }
6452             
6453             if(typeof(config.xsHeader) != 'undefined'){
6454                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6455             }
6456             
6457             if(hh.length){
6458                 c.html = hh;
6459             }
6460             
6461             if(typeof(config.tooltip) != 'undefined'){
6462                 c.tooltip = config.tooltip;
6463             }
6464             
6465             if(typeof(config.colspan) != 'undefined'){
6466                 c.colspan = config.colspan;
6467             }
6468             
6469             if(typeof(config.hidden) != 'undefined' && config.hidden){
6470                 c.style += ' display:none;';
6471             }
6472             
6473             if(typeof(config.dataIndex) != 'undefined'){
6474                 c.sort = config.dataIndex;
6475             }
6476             
6477            
6478             
6479             if(typeof(config.align) != 'undefined' && config.align.length){
6480                 c.style += ' text-align:' + config.align + ';';
6481             }
6482             
6483             if(typeof(config.width) != 'undefined'){
6484                 c.style += ' width:' + config.width + 'px;';
6485                 this.totalWidth += config.width;
6486             } else {
6487                 this.totalWidth += 100; // assume minimum of 100 per column?
6488             }
6489             
6490             if(typeof(config.cls) != 'undefined'){
6491                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6492             }
6493             
6494             ['xs','sm','md','lg'].map(function(size){
6495                 
6496                 if(typeof(config[size]) == 'undefined'){
6497                     return;
6498                 }
6499                 
6500                 if (!config[size]) { // 0 = hidden
6501                     c.cls += ' hidden-' + size;
6502                     return;
6503                 }
6504                 
6505                 c.cls += ' col-' + size + '-' + config[size];
6506
6507             });
6508             
6509             header.cn.push(c)
6510         }
6511         
6512         return header;
6513     },
6514     
6515     renderBody : function()
6516     {
6517         var body = {
6518             tag: 'tbody',
6519             cn : [
6520                 {
6521                     tag: 'tr',
6522                     cn : [
6523                         {
6524                             tag : 'td',
6525                             colspan :  this.cm.getColumnCount()
6526                         }
6527                     ]
6528                 }
6529             ]
6530         };
6531         
6532         return body;
6533     },
6534     
6535     renderFooter : function()
6536     {
6537         var footer = {
6538             tag: 'tfoot',
6539             cn : [
6540                 {
6541                     tag: 'tr',
6542                     cn : [
6543                         {
6544                             tag : 'td',
6545                             colspan :  this.cm.getColumnCount()
6546                         }
6547                     ]
6548                 }
6549             ]
6550         };
6551         
6552         return footer;
6553     },
6554     
6555     
6556     
6557     onLoad : function()
6558     {
6559 //        Roo.log('ds onload');
6560         this.clear();
6561         
6562         var _this = this;
6563         var cm = this.cm;
6564         var ds = this.store;
6565         
6566         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6567             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6568             if (_this.store.sortInfo) {
6569                     
6570                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6571                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6572                 }
6573                 
6574                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6575                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6576                 }
6577             }
6578         });
6579         
6580         var tbody =  this.mainBody;
6581               
6582         if(ds.getCount() > 0){
6583             ds.data.each(function(d,rowIndex){
6584                 var row =  this.renderRow(cm, ds, rowIndex);
6585                 
6586                 tbody.createChild(row);
6587                 
6588                 var _this = this;
6589                 
6590                 if(row.cellObjects.length){
6591                     Roo.each(row.cellObjects, function(r){
6592                         _this.renderCellObject(r);
6593                     })
6594                 }
6595                 
6596             }, this);
6597         }
6598         
6599         Roo.each(this.el.select('tbody td', true).elements, function(e){
6600             e.on('mouseover', _this.onMouseover, _this);
6601         });
6602         
6603         Roo.each(this.el.select('tbody td', true).elements, function(e){
6604             e.on('mouseout', _this.onMouseout, _this);
6605         });
6606         this.fireEvent('rowsrendered', this);
6607         //if(this.loadMask){
6608         //    this.maskEl.hide();
6609         //}
6610         
6611         this.autoSize();
6612     },
6613     
6614     
6615     onUpdate : function(ds,record)
6616     {
6617         this.refreshRow(record);
6618         this.autoSize();
6619     },
6620     
6621     onRemove : function(ds, record, index, isUpdate){
6622         if(isUpdate !== true){
6623             this.fireEvent("beforerowremoved", this, index, record);
6624         }
6625         var bt = this.mainBody.dom;
6626         
6627         var rows = this.el.select('tbody > tr', true).elements;
6628         
6629         if(typeof(rows[index]) != 'undefined'){
6630             bt.removeChild(rows[index].dom);
6631         }
6632         
6633 //        if(bt.rows[index]){
6634 //            bt.removeChild(bt.rows[index]);
6635 //        }
6636         
6637         if(isUpdate !== true){
6638             //this.stripeRows(index);
6639             //this.syncRowHeights(index, index);
6640             //this.layout();
6641             this.fireEvent("rowremoved", this, index, record);
6642         }
6643     },
6644     
6645     onAdd : function(ds, records, rowIndex)
6646     {
6647         //Roo.log('on Add called');
6648         // - note this does not handle multiple adding very well..
6649         var bt = this.mainBody.dom;
6650         for (var i =0 ; i < records.length;i++) {
6651             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6652             //Roo.log(records[i]);
6653             //Roo.log(this.store.getAt(rowIndex+i));
6654             this.insertRow(this.store, rowIndex + i, false);
6655             return;
6656         }
6657         
6658     },
6659     
6660     
6661     refreshRow : function(record){
6662         var ds = this.store, index;
6663         if(typeof record == 'number'){
6664             index = record;
6665             record = ds.getAt(index);
6666         }else{
6667             index = ds.indexOf(record);
6668         }
6669         this.insertRow(ds, index, true);
6670         this.autoSize();
6671         this.onRemove(ds, record, index+1, true);
6672         this.autoSize();
6673         //this.syncRowHeights(index, index);
6674         //this.layout();
6675         this.fireEvent("rowupdated", this, index, record);
6676     },
6677     
6678     insertRow : function(dm, rowIndex, isUpdate){
6679         
6680         if(!isUpdate){
6681             this.fireEvent("beforerowsinserted", this, rowIndex);
6682         }
6683             //var s = this.getScrollState();
6684         var row = this.renderRow(this.cm, this.store, rowIndex);
6685         // insert before rowIndex..
6686         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6687         
6688         var _this = this;
6689                 
6690         if(row.cellObjects.length){
6691             Roo.each(row.cellObjects, function(r){
6692                 _this.renderCellObject(r);
6693             })
6694         }
6695             
6696         if(!isUpdate){
6697             this.fireEvent("rowsinserted", this, rowIndex);
6698             //this.syncRowHeights(firstRow, lastRow);
6699             //this.stripeRows(firstRow);
6700             //this.layout();
6701         }
6702         
6703     },
6704     
6705     
6706     getRowDom : function(rowIndex)
6707     {
6708         var rows = this.el.select('tbody > tr', true).elements;
6709         
6710         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6711         
6712     },
6713     // returns the object tree for a tr..
6714   
6715     
6716     renderRow : function(cm, ds, rowIndex) 
6717     {
6718         var d = ds.getAt(rowIndex);
6719         
6720         var row = {
6721             tag : 'tr',
6722             cls : 'x-row-' + rowIndex,
6723             cn : []
6724         };
6725             
6726         var cellObjects = [];
6727         
6728         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6729             var config = cm.config[i];
6730             
6731             var renderer = cm.getRenderer(i);
6732             var value = '';
6733             var id = false;
6734             
6735             if(typeof(renderer) !== 'undefined'){
6736                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6737             }
6738             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6739             // and are rendered into the cells after the row is rendered - using the id for the element.
6740             
6741             if(typeof(value) === 'object'){
6742                 id = Roo.id();
6743                 cellObjects.push({
6744                     container : id,
6745                     cfg : value 
6746                 })
6747             }
6748             
6749             var rowcfg = {
6750                 record: d,
6751                 rowIndex : rowIndex,
6752                 colIndex : i,
6753                 rowClass : ''
6754             };
6755
6756             this.fireEvent('rowclass', this, rowcfg);
6757             
6758             var td = {
6759                 tag: 'td',
6760                 cls : rowcfg.rowClass + ' x-col-' + i,
6761                 style: '',
6762                 html: (typeof(value) === 'object') ? '' : value
6763             };
6764             
6765             if (id) {
6766                 td.id = id;
6767             }
6768             
6769             if(typeof(config.colspan) != 'undefined'){
6770                 td.colspan = config.colspan;
6771             }
6772             
6773             if(typeof(config.hidden) != 'undefined' && config.hidden){
6774                 td.style += ' display:none;';
6775             }
6776             
6777             if(typeof(config.align) != 'undefined' && config.align.length){
6778                 td.style += ' text-align:' + config.align + ';';
6779             }
6780             
6781             if(typeof(config.width) != 'undefined'){
6782                 td.style += ' width:' +  config.width + 'px;';
6783             }
6784             
6785             if(typeof(config.cursor) != 'undefined'){
6786                 td.style += ' cursor:' +  config.cursor + ';';
6787             }
6788             
6789             if(typeof(config.cls) != 'undefined'){
6790                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6791             }
6792             
6793             ['xs','sm','md','lg'].map(function(size){
6794                 
6795                 if(typeof(config[size]) == 'undefined'){
6796                     return;
6797                 }
6798                 
6799                 if (!config[size]) { // 0 = hidden
6800                     td.cls += ' hidden-' + size;
6801                     return;
6802                 }
6803                 
6804                 td.cls += ' col-' + size + '-' + config[size];
6805
6806             });
6807             
6808             row.cn.push(td);
6809            
6810         }
6811         
6812         row.cellObjects = cellObjects;
6813         
6814         return row;
6815           
6816     },
6817     
6818     
6819     
6820     onBeforeLoad : function()
6821     {
6822         //Roo.log('ds onBeforeLoad');
6823         
6824         //this.clear();
6825         
6826         //if(this.loadMask){
6827         //    this.maskEl.show();
6828         //}
6829     },
6830      /**
6831      * Remove all rows
6832      */
6833     clear : function()
6834     {
6835         this.el.select('tbody', true).first().dom.innerHTML = '';
6836     },
6837     /**
6838      * Show or hide a row.
6839      * @param {Number} rowIndex to show or hide
6840      * @param {Boolean} state hide
6841      */
6842     setRowVisibility : function(rowIndex, state)
6843     {
6844         var bt = this.mainBody.dom;
6845         
6846         var rows = this.el.select('tbody > tr', true).elements;
6847         
6848         if(typeof(rows[rowIndex]) == 'undefined'){
6849             return;
6850         }
6851         rows[rowIndex].dom.style.display = state ? '' : 'none';
6852     },
6853     
6854     
6855     getSelectionModel : function(){
6856         if(!this.selModel){
6857             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6858         }
6859         return this.selModel;
6860     },
6861     /*
6862      * Render the Roo.bootstrap object from renderder
6863      */
6864     renderCellObject : function(r)
6865     {
6866         var _this = this;
6867         
6868         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6869         
6870         var t = r.cfg.render(r.container);
6871         
6872         if(r.cfg.cn){
6873             Roo.each(r.cfg.cn, function(c){
6874                 var child = {
6875                     container: t.getChildContainer(),
6876                     cfg: c
6877                 };
6878                 _this.renderCellObject(child);
6879             })
6880         }
6881     },
6882     
6883     getRowIndex : function(row)
6884     {
6885         var rowIndex = -1;
6886         
6887         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6888             if(el != row){
6889                 return;
6890             }
6891             
6892             rowIndex = index;
6893         });
6894         
6895         return rowIndex;
6896     },
6897      /**
6898      * Returns the grid's underlying element = used by panel.Grid
6899      * @return {Element} The element
6900      */
6901     getGridEl : function(){
6902         return this.el;
6903     },
6904      /**
6905      * Forces a resize - used by panel.Grid
6906      * @return {Element} The element
6907      */
6908     autoSize : function()
6909     {
6910         //var ctr = Roo.get(this.container.dom.parentElement);
6911         var ctr = Roo.get(this.el.dom);
6912         
6913         var thd = this.getGridEl().select('thead',true).first();
6914         var tbd = this.getGridEl().select('tbody', true).first();
6915         var tfd = this.getGridEl().select('tfoot', true).first();
6916         
6917         var cw = ctr.getWidth();
6918         
6919         if (tbd) {
6920             
6921             tbd.setSize(ctr.getWidth(),
6922                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6923             );
6924             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6925             cw -= barsize;
6926         }
6927         cw = Math.max(cw, this.totalWidth);
6928         this.getGridEl().select('tr',true).setWidth(cw);
6929         // resize 'expandable coloumn?
6930         
6931         return; // we doe not have a view in this design..
6932         
6933     },
6934     onBodyScroll: function()
6935     {
6936         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6937         if(this.mainHead){
6938             this.mainHead.setStyle({
6939                 'position' : 'relative',
6940                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6941             });
6942         }
6943         
6944         if(this.lazyLoad){
6945             
6946             var scrollHeight = this.mainBody.dom.scrollHeight;
6947             
6948             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6949             
6950             var height = this.mainBody.getHeight();
6951             
6952             if(scrollHeight - height == scrollTop) {
6953                 
6954                 var total = this.ds.getTotalCount();
6955                 
6956                 if(this.footer.cursor + this.footer.pageSize < total){
6957                     
6958                     this.footer.ds.load({
6959                         params : {
6960                             start : this.footer.cursor + this.footer.pageSize,
6961                             limit : this.footer.pageSize
6962                         },
6963                         add : true
6964                     });
6965                 }
6966             }
6967             
6968         }
6969     },
6970     
6971     onHeaderChange : function()
6972     {
6973         var header = this.renderHeader();
6974         var table = this.el.select('table', true).first();
6975         
6976         this.mainHead.remove();
6977         this.mainHead = table.createChild(header, this.mainBody, false);
6978     },
6979     
6980     onHiddenChange : function(colModel, colIndex, hidden)
6981     {
6982         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6983         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6984         
6985         this.CSS.updateRule(thSelector, "display", "");
6986         this.CSS.updateRule(tdSelector, "display", "");
6987         
6988         if(hidden){
6989             this.CSS.updateRule(thSelector, "display", "none");
6990             this.CSS.updateRule(tdSelector, "display", "none");
6991         }
6992         
6993         this.onHeaderChange();
6994         this.onLoad();
6995         
6996     }
6997     
6998 });
6999
7000  
7001
7002  /*
7003  * - LGPL
7004  *
7005  * table cell
7006  * 
7007  */
7008
7009 /**
7010  * @class Roo.bootstrap.TableCell
7011  * @extends Roo.bootstrap.Component
7012  * Bootstrap TableCell class
7013  * @cfg {String} html cell contain text
7014  * @cfg {String} cls cell class
7015  * @cfg {String} tag cell tag (td|th) default td
7016  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7017  * @cfg {String} align Aligns the content in a cell
7018  * @cfg {String} axis Categorizes cells
7019  * @cfg {String} bgcolor Specifies the background color of a cell
7020  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7021  * @cfg {Number} colspan Specifies the number of columns a cell should span
7022  * @cfg {String} headers Specifies one or more header cells a cell is related to
7023  * @cfg {Number} height Sets the height of a cell
7024  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7025  * @cfg {Number} rowspan Sets the number of rows a cell should span
7026  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7027  * @cfg {String} valign Vertical aligns the content in a cell
7028  * @cfg {Number} width Specifies the width of a cell
7029  * 
7030  * @constructor
7031  * Create a new TableCell
7032  * @param {Object} config The config object
7033  */
7034
7035 Roo.bootstrap.TableCell = function(config){
7036     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7037 };
7038
7039 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7040     
7041     html: false,
7042     cls: false,
7043     tag: false,
7044     abbr: false,
7045     align: false,
7046     axis: false,
7047     bgcolor: false,
7048     charoff: false,
7049     colspan: false,
7050     headers: false,
7051     height: false,
7052     nowrap: false,
7053     rowspan: false,
7054     scope: false,
7055     valign: false,
7056     width: false,
7057     
7058     
7059     getAutoCreate : function(){
7060         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7061         
7062         cfg = {
7063             tag: 'td'
7064         };
7065         
7066         if(this.tag){
7067             cfg.tag = this.tag;
7068         }
7069         
7070         if (this.html) {
7071             cfg.html=this.html
7072         }
7073         if (this.cls) {
7074             cfg.cls=this.cls
7075         }
7076         if (this.abbr) {
7077             cfg.abbr=this.abbr
7078         }
7079         if (this.align) {
7080             cfg.align=this.align
7081         }
7082         if (this.axis) {
7083             cfg.axis=this.axis
7084         }
7085         if (this.bgcolor) {
7086             cfg.bgcolor=this.bgcolor
7087         }
7088         if (this.charoff) {
7089             cfg.charoff=this.charoff
7090         }
7091         if (this.colspan) {
7092             cfg.colspan=this.colspan
7093         }
7094         if (this.headers) {
7095             cfg.headers=this.headers
7096         }
7097         if (this.height) {
7098             cfg.height=this.height
7099         }
7100         if (this.nowrap) {
7101             cfg.nowrap=this.nowrap
7102         }
7103         if (this.rowspan) {
7104             cfg.rowspan=this.rowspan
7105         }
7106         if (this.scope) {
7107             cfg.scope=this.scope
7108         }
7109         if (this.valign) {
7110             cfg.valign=this.valign
7111         }
7112         if (this.width) {
7113             cfg.width=this.width
7114         }
7115         
7116         
7117         return cfg;
7118     }
7119    
7120 });
7121
7122  
7123
7124  /*
7125  * - LGPL
7126  *
7127  * table row
7128  * 
7129  */
7130
7131 /**
7132  * @class Roo.bootstrap.TableRow
7133  * @extends Roo.bootstrap.Component
7134  * Bootstrap TableRow class
7135  * @cfg {String} cls row class
7136  * @cfg {String} align Aligns the content in a table row
7137  * @cfg {String} bgcolor Specifies a background color for a table row
7138  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7139  * @cfg {String} valign Vertical aligns the content in a table row
7140  * 
7141  * @constructor
7142  * Create a new TableRow
7143  * @param {Object} config The config object
7144  */
7145
7146 Roo.bootstrap.TableRow = function(config){
7147     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7148 };
7149
7150 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7151     
7152     cls: false,
7153     align: false,
7154     bgcolor: false,
7155     charoff: false,
7156     valign: false,
7157     
7158     getAutoCreate : function(){
7159         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7160         
7161         cfg = {
7162             tag: 'tr'
7163         };
7164             
7165         if(this.cls){
7166             cfg.cls = this.cls;
7167         }
7168         if(this.align){
7169             cfg.align = this.align;
7170         }
7171         if(this.bgcolor){
7172             cfg.bgcolor = this.bgcolor;
7173         }
7174         if(this.charoff){
7175             cfg.charoff = this.charoff;
7176         }
7177         if(this.valign){
7178             cfg.valign = this.valign;
7179         }
7180         
7181         return cfg;
7182     }
7183    
7184 });
7185
7186  
7187
7188  /*
7189  * - LGPL
7190  *
7191  * table body
7192  * 
7193  */
7194
7195 /**
7196  * @class Roo.bootstrap.TableBody
7197  * @extends Roo.bootstrap.Component
7198  * Bootstrap TableBody class
7199  * @cfg {String} cls element class
7200  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7201  * @cfg {String} align Aligns the content inside the element
7202  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7203  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7204  * 
7205  * @constructor
7206  * Create a new TableBody
7207  * @param {Object} config The config object
7208  */
7209
7210 Roo.bootstrap.TableBody = function(config){
7211     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7212 };
7213
7214 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7215     
7216     cls: false,
7217     tag: false,
7218     align: false,
7219     charoff: false,
7220     valign: false,
7221     
7222     getAutoCreate : function(){
7223         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7224         
7225         cfg = {
7226             tag: 'tbody'
7227         };
7228             
7229         if (this.cls) {
7230             cfg.cls=this.cls
7231         }
7232         if(this.tag){
7233             cfg.tag = this.tag;
7234         }
7235         
7236         if(this.align){
7237             cfg.align = this.align;
7238         }
7239         if(this.charoff){
7240             cfg.charoff = this.charoff;
7241         }
7242         if(this.valign){
7243             cfg.valign = this.valign;
7244         }
7245         
7246         return cfg;
7247     }
7248     
7249     
7250 //    initEvents : function()
7251 //    {
7252 //        
7253 //        if(!this.store){
7254 //            return;
7255 //        }
7256 //        
7257 //        this.store = Roo.factory(this.store, Roo.data);
7258 //        this.store.on('load', this.onLoad, this);
7259 //        
7260 //        this.store.load();
7261 //        
7262 //    },
7263 //    
7264 //    onLoad: function () 
7265 //    {   
7266 //        this.fireEvent('load', this);
7267 //    }
7268 //    
7269 //   
7270 });
7271
7272  
7273
7274  /*
7275  * Based on:
7276  * Ext JS Library 1.1.1
7277  * Copyright(c) 2006-2007, Ext JS, LLC.
7278  *
7279  * Originally Released Under LGPL - original licence link has changed is not relivant.
7280  *
7281  * Fork - LGPL
7282  * <script type="text/javascript">
7283  */
7284
7285 // as we use this in bootstrap.
7286 Roo.namespace('Roo.form');
7287  /**
7288  * @class Roo.form.Action
7289  * Internal Class used to handle form actions
7290  * @constructor
7291  * @param {Roo.form.BasicForm} el The form element or its id
7292  * @param {Object} config Configuration options
7293  */
7294
7295  
7296  
7297 // define the action interface
7298 Roo.form.Action = function(form, options){
7299     this.form = form;
7300     this.options = options || {};
7301 };
7302 /**
7303  * Client Validation Failed
7304  * @const 
7305  */
7306 Roo.form.Action.CLIENT_INVALID = 'client';
7307 /**
7308  * Server Validation Failed
7309  * @const 
7310  */
7311 Roo.form.Action.SERVER_INVALID = 'server';
7312  /**
7313  * Connect to Server Failed
7314  * @const 
7315  */
7316 Roo.form.Action.CONNECT_FAILURE = 'connect';
7317 /**
7318  * Reading Data from Server Failed
7319  * @const 
7320  */
7321 Roo.form.Action.LOAD_FAILURE = 'load';
7322
7323 Roo.form.Action.prototype = {
7324     type : 'default',
7325     failureType : undefined,
7326     response : undefined,
7327     result : undefined,
7328
7329     // interface method
7330     run : function(options){
7331
7332     },
7333
7334     // interface method
7335     success : function(response){
7336
7337     },
7338
7339     // interface method
7340     handleResponse : function(response){
7341
7342     },
7343
7344     // default connection failure
7345     failure : function(response){
7346         
7347         this.response = response;
7348         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7349         this.form.afterAction(this, false);
7350     },
7351
7352     processResponse : function(response){
7353         this.response = response;
7354         if(!response.responseText){
7355             return true;
7356         }
7357         this.result = this.handleResponse(response);
7358         return this.result;
7359     },
7360
7361     // utility functions used internally
7362     getUrl : function(appendParams){
7363         var url = this.options.url || this.form.url || this.form.el.dom.action;
7364         if(appendParams){
7365             var p = this.getParams();
7366             if(p){
7367                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7368             }
7369         }
7370         return url;
7371     },
7372
7373     getMethod : function(){
7374         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7375     },
7376
7377     getParams : function(){
7378         var bp = this.form.baseParams;
7379         var p = this.options.params;
7380         if(p){
7381             if(typeof p == "object"){
7382                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7383             }else if(typeof p == 'string' && bp){
7384                 p += '&' + Roo.urlEncode(bp);
7385             }
7386         }else if(bp){
7387             p = Roo.urlEncode(bp);
7388         }
7389         return p;
7390     },
7391
7392     createCallback : function(){
7393         return {
7394             success: this.success,
7395             failure: this.failure,
7396             scope: this,
7397             timeout: (this.form.timeout*1000),
7398             upload: this.form.fileUpload ? this.success : undefined
7399         };
7400     }
7401 };
7402
7403 Roo.form.Action.Submit = function(form, options){
7404     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7405 };
7406
7407 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7408     type : 'submit',
7409
7410     haveProgress : false,
7411     uploadComplete : false,
7412     
7413     // uploadProgress indicator.
7414     uploadProgress : function()
7415     {
7416         if (!this.form.progressUrl) {
7417             return;
7418         }
7419         
7420         if (!this.haveProgress) {
7421             Roo.MessageBox.progress("Uploading", "Uploading");
7422         }
7423         if (this.uploadComplete) {
7424            Roo.MessageBox.hide();
7425            return;
7426         }
7427         
7428         this.haveProgress = true;
7429    
7430         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7431         
7432         var c = new Roo.data.Connection();
7433         c.request({
7434             url : this.form.progressUrl,
7435             params: {
7436                 id : uid
7437             },
7438             method: 'GET',
7439             success : function(req){
7440                //console.log(data);
7441                 var rdata = false;
7442                 var edata;
7443                 try  {
7444                    rdata = Roo.decode(req.responseText)
7445                 } catch (e) {
7446                     Roo.log("Invalid data from server..");
7447                     Roo.log(edata);
7448                     return;
7449                 }
7450                 if (!rdata || !rdata.success) {
7451                     Roo.log(rdata);
7452                     Roo.MessageBox.alert(Roo.encode(rdata));
7453                     return;
7454                 }
7455                 var data = rdata.data;
7456                 
7457                 if (this.uploadComplete) {
7458                    Roo.MessageBox.hide();
7459                    return;
7460                 }
7461                    
7462                 if (data){
7463                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7464                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7465                     );
7466                 }
7467                 this.uploadProgress.defer(2000,this);
7468             },
7469        
7470             failure: function(data) {
7471                 Roo.log('progress url failed ');
7472                 Roo.log(data);
7473             },
7474             scope : this
7475         });
7476            
7477     },
7478     
7479     
7480     run : function()
7481     {
7482         // run get Values on the form, so it syncs any secondary forms.
7483         this.form.getValues();
7484         
7485         var o = this.options;
7486         var method = this.getMethod();
7487         var isPost = method == 'POST';
7488         if(o.clientValidation === false || this.form.isValid()){
7489             
7490             if (this.form.progressUrl) {
7491                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7492                     (new Date() * 1) + '' + Math.random());
7493                     
7494             } 
7495             
7496             
7497             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7498                 form:this.form.el.dom,
7499                 url:this.getUrl(!isPost),
7500                 method: method,
7501                 params:isPost ? this.getParams() : null,
7502                 isUpload: this.form.fileUpload
7503             }));
7504             
7505             this.uploadProgress();
7506
7507         }else if (o.clientValidation !== false){ // client validation failed
7508             this.failureType = Roo.form.Action.CLIENT_INVALID;
7509             this.form.afterAction(this, false);
7510         }
7511     },
7512
7513     success : function(response)
7514     {
7515         this.uploadComplete= true;
7516         if (this.haveProgress) {
7517             Roo.MessageBox.hide();
7518         }
7519         
7520         
7521         var result = this.processResponse(response);
7522         if(result === true || result.success){
7523             this.form.afterAction(this, true);
7524             return;
7525         }
7526         if(result.errors){
7527             this.form.markInvalid(result.errors);
7528             this.failureType = Roo.form.Action.SERVER_INVALID;
7529         }
7530         this.form.afterAction(this, false);
7531     },
7532     failure : function(response)
7533     {
7534         this.uploadComplete= true;
7535         if (this.haveProgress) {
7536             Roo.MessageBox.hide();
7537         }
7538         
7539         this.response = response;
7540         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7541         this.form.afterAction(this, false);
7542     },
7543     
7544     handleResponse : function(response){
7545         if(this.form.errorReader){
7546             var rs = this.form.errorReader.read(response);
7547             var errors = [];
7548             if(rs.records){
7549                 for(var i = 0, len = rs.records.length; i < len; i++) {
7550                     var r = rs.records[i];
7551                     errors[i] = r.data;
7552                 }
7553             }
7554             if(errors.length < 1){
7555                 errors = null;
7556             }
7557             return {
7558                 success : rs.success,
7559                 errors : errors
7560             };
7561         }
7562         var ret = false;
7563         try {
7564             ret = Roo.decode(response.responseText);
7565         } catch (e) {
7566             ret = {
7567                 success: false,
7568                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7569                 errors : []
7570             };
7571         }
7572         return ret;
7573         
7574     }
7575 });
7576
7577
7578 Roo.form.Action.Load = function(form, options){
7579     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7580     this.reader = this.form.reader;
7581 };
7582
7583 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7584     type : 'load',
7585
7586     run : function(){
7587         
7588         Roo.Ajax.request(Roo.apply(
7589                 this.createCallback(), {
7590                     method:this.getMethod(),
7591                     url:this.getUrl(false),
7592                     params:this.getParams()
7593         }));
7594     },
7595
7596     success : function(response){
7597         
7598         var result = this.processResponse(response);
7599         if(result === true || !result.success || !result.data){
7600             this.failureType = Roo.form.Action.LOAD_FAILURE;
7601             this.form.afterAction(this, false);
7602             return;
7603         }
7604         this.form.clearInvalid();
7605         this.form.setValues(result.data);
7606         this.form.afterAction(this, true);
7607     },
7608
7609     handleResponse : function(response){
7610         if(this.form.reader){
7611             var rs = this.form.reader.read(response);
7612             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7613             return {
7614                 success : rs.success,
7615                 data : data
7616             };
7617         }
7618         return Roo.decode(response.responseText);
7619     }
7620 });
7621
7622 Roo.form.Action.ACTION_TYPES = {
7623     'load' : Roo.form.Action.Load,
7624     'submit' : Roo.form.Action.Submit
7625 };/*
7626  * - LGPL
7627  *
7628  * form
7629  *
7630  */
7631
7632 /**
7633  * @class Roo.bootstrap.Form
7634  * @extends Roo.bootstrap.Component
7635  * Bootstrap Form class
7636  * @cfg {String} method  GET | POST (default POST)
7637  * @cfg {String} labelAlign top | left (default top)
7638  * @cfg {String} align left  | right - for navbars
7639  * @cfg {Boolean} loadMask load mask when submit (default true)
7640
7641  *
7642  * @constructor
7643  * Create a new Form
7644  * @param {Object} config The config object
7645  */
7646
7647
7648 Roo.bootstrap.Form = function(config){
7649     
7650     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7651     
7652     Roo.bootstrap.Form.popover.apply();
7653     
7654     this.addEvents({
7655         /**
7656          * @event clientvalidation
7657          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7658          * @param {Form} this
7659          * @param {Boolean} valid true if the form has passed client-side validation
7660          */
7661         clientvalidation: true,
7662         /**
7663          * @event beforeaction
7664          * Fires before any action is performed. Return false to cancel the action.
7665          * @param {Form} this
7666          * @param {Action} action The action to be performed
7667          */
7668         beforeaction: true,
7669         /**
7670          * @event actionfailed
7671          * Fires when an action fails.
7672          * @param {Form} this
7673          * @param {Action} action The action that failed
7674          */
7675         actionfailed : true,
7676         /**
7677          * @event actioncomplete
7678          * Fires when an action is completed.
7679          * @param {Form} this
7680          * @param {Action} action The action that completed
7681          */
7682         actioncomplete : true
7683     });
7684 };
7685
7686 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7687
7688      /**
7689      * @cfg {String} method
7690      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7691      */
7692     method : 'POST',
7693     /**
7694      * @cfg {String} url
7695      * The URL to use for form actions if one isn't supplied in the action options.
7696      */
7697     /**
7698      * @cfg {Boolean} fileUpload
7699      * Set to true if this form is a file upload.
7700      */
7701
7702     /**
7703      * @cfg {Object} baseParams
7704      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7705      */
7706
7707     /**
7708      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7709      */
7710     timeout: 30,
7711     /**
7712      * @cfg {Sting} align (left|right) for navbar forms
7713      */
7714     align : 'left',
7715
7716     // private
7717     activeAction : null,
7718
7719     /**
7720      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7721      * element by passing it or its id or mask the form itself by passing in true.
7722      * @type Mixed
7723      */
7724     waitMsgTarget : false,
7725
7726     loadMask : true,
7727     
7728     /**
7729      * @cfg {Boolean} errorMask (true|false) default false
7730      */
7731     errorMask : false,
7732     
7733     /**
7734      * @cfg {Number} maskOffset Default 100
7735      */
7736     maskOffset : 100,
7737     
7738     /**
7739      * @cfg {Boolean} maskBody
7740      */
7741     maskBody : false,
7742
7743     getAutoCreate : function(){
7744
7745         var cfg = {
7746             tag: 'form',
7747             method : this.method || 'POST',
7748             id : this.id || Roo.id(),
7749             cls : ''
7750         };
7751         if (this.parent().xtype.match(/^Nav/)) {
7752             cfg.cls = 'navbar-form navbar-' + this.align;
7753
7754         }
7755
7756         if (this.labelAlign == 'left' ) {
7757             cfg.cls += ' form-horizontal';
7758         }
7759
7760
7761         return cfg;
7762     },
7763     initEvents : function()
7764     {
7765         this.el.on('submit', this.onSubmit, this);
7766         // this was added as random key presses on the form where triggering form submit.
7767         this.el.on('keypress', function(e) {
7768             if (e.getCharCode() != 13) {
7769                 return true;
7770             }
7771             // we might need to allow it for textareas.. and some other items.
7772             // check e.getTarget().
7773
7774             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7775                 return true;
7776             }
7777
7778             Roo.log("keypress blocked");
7779
7780             e.preventDefault();
7781             return false;
7782         });
7783         
7784     },
7785     // private
7786     onSubmit : function(e){
7787         e.stopEvent();
7788     },
7789
7790      /**
7791      * Returns true if client-side validation on the form is successful.
7792      * @return Boolean
7793      */
7794     isValid : function(){
7795         var items = this.getItems();
7796         var valid = true;
7797         var target = false;
7798         
7799         items.each(function(f){
7800             
7801             if(f.validate()){
7802                 return;
7803             }
7804             valid = false;
7805
7806             if(!target && f.el.isVisible(true)){
7807                 target = f;
7808             }
7809            
7810         });
7811         
7812         if(this.errorMask && !valid){
7813             Roo.bootstrap.Form.popover.mask(this, target);
7814         }
7815         
7816         return valid;
7817     },
7818     
7819     /**
7820      * Returns true if any fields in this form have changed since their original load.
7821      * @return Boolean
7822      */
7823     isDirty : function(){
7824         var dirty = false;
7825         var items = this.getItems();
7826         items.each(function(f){
7827            if(f.isDirty()){
7828                dirty = true;
7829                return false;
7830            }
7831            return true;
7832         });
7833         return dirty;
7834     },
7835      /**
7836      * Performs a predefined action (submit or load) or custom actions you define on this form.
7837      * @param {String} actionName The name of the action type
7838      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7839      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7840      * accept other config options):
7841      * <pre>
7842 Property          Type             Description
7843 ----------------  ---------------  ----------------------------------------------------------------------------------
7844 url               String           The url for the action (defaults to the form's url)
7845 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7846 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7847 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7848                                    validate the form on the client (defaults to false)
7849      * </pre>
7850      * @return {BasicForm} this
7851      */
7852     doAction : function(action, options){
7853         if(typeof action == 'string'){
7854             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7855         }
7856         if(this.fireEvent('beforeaction', this, action) !== false){
7857             this.beforeAction(action);
7858             action.run.defer(100, action);
7859         }
7860         return this;
7861     },
7862
7863     // private
7864     beforeAction : function(action){
7865         var o = action.options;
7866         
7867         if(this.loadMask){
7868             
7869             if(this.maskBody){
7870                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7871             } else {
7872                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7873             }
7874         }
7875         // not really supported yet.. ??
7876
7877         //if(this.waitMsgTarget === true){
7878         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7879         //}else if(this.waitMsgTarget){
7880         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7881         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7882         //}else {
7883         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7884        // }
7885
7886     },
7887
7888     // private
7889     afterAction : function(action, success){
7890         this.activeAction = null;
7891         var o = action.options;
7892
7893         if(this.loadMask){
7894             
7895             if(this.maskBody){
7896                 Roo.get(document.body).unmask();
7897             } else {
7898                 this.el.unmask();
7899             }
7900         }
7901         
7902         //if(this.waitMsgTarget === true){
7903 //            this.el.unmask();
7904         //}else if(this.waitMsgTarget){
7905         //    this.waitMsgTarget.unmask();
7906         //}else{
7907         //    Roo.MessageBox.updateProgress(1);
7908         //    Roo.MessageBox.hide();
7909        // }
7910         //
7911         if(success){
7912             if(o.reset){
7913                 this.reset();
7914             }
7915             Roo.callback(o.success, o.scope, [this, action]);
7916             this.fireEvent('actioncomplete', this, action);
7917
7918         }else{
7919
7920             // failure condition..
7921             // we have a scenario where updates need confirming.
7922             // eg. if a locking scenario exists..
7923             // we look for { errors : { needs_confirm : true }} in the response.
7924             if (
7925                 (typeof(action.result) != 'undefined')  &&
7926                 (typeof(action.result.errors) != 'undefined')  &&
7927                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7928            ){
7929                 var _t = this;
7930                 Roo.log("not supported yet");
7931                  /*
7932
7933                 Roo.MessageBox.confirm(
7934                     "Change requires confirmation",
7935                     action.result.errorMsg,
7936                     function(r) {
7937                         if (r != 'yes') {
7938                             return;
7939                         }
7940                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7941                     }
7942
7943                 );
7944                 */
7945
7946
7947                 return;
7948             }
7949
7950             Roo.callback(o.failure, o.scope, [this, action]);
7951             // show an error message if no failed handler is set..
7952             if (!this.hasListener('actionfailed')) {
7953                 Roo.log("need to add dialog support");
7954                 /*
7955                 Roo.MessageBox.alert("Error",
7956                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7957                         action.result.errorMsg :
7958                         "Saving Failed, please check your entries or try again"
7959                 );
7960                 */
7961             }
7962
7963             this.fireEvent('actionfailed', this, action);
7964         }
7965
7966     },
7967     /**
7968      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7969      * @param {String} id The value to search for
7970      * @return Field
7971      */
7972     findField : function(id){
7973         var items = this.getItems();
7974         var field = items.get(id);
7975         if(!field){
7976              items.each(function(f){
7977                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7978                     field = f;
7979                     return false;
7980                 }
7981                 return true;
7982             });
7983         }
7984         return field || null;
7985     },
7986      /**
7987      * Mark fields in this form invalid in bulk.
7988      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7989      * @return {BasicForm} this
7990      */
7991     markInvalid : function(errors){
7992         if(errors instanceof Array){
7993             for(var i = 0, len = errors.length; i < len; i++){
7994                 var fieldError = errors[i];
7995                 var f = this.findField(fieldError.id);
7996                 if(f){
7997                     f.markInvalid(fieldError.msg);
7998                 }
7999             }
8000         }else{
8001             var field, id;
8002             for(id in errors){
8003                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8004                     field.markInvalid(errors[id]);
8005                 }
8006             }
8007         }
8008         //Roo.each(this.childForms || [], function (f) {
8009         //    f.markInvalid(errors);
8010         //});
8011
8012         return this;
8013     },
8014
8015     /**
8016      * Set values for fields in this form in bulk.
8017      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8018      * @return {BasicForm} this
8019      */
8020     setValues : function(values){
8021         if(values instanceof Array){ // array of objects
8022             for(var i = 0, len = values.length; i < len; i++){
8023                 var v = values[i];
8024                 var f = this.findField(v.id);
8025                 if(f){
8026                     f.setValue(v.value);
8027                     if(this.trackResetOnLoad){
8028                         f.originalValue = f.getValue();
8029                     }
8030                 }
8031             }
8032         }else{ // object hash
8033             var field, id;
8034             for(id in values){
8035                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8036
8037                     if (field.setFromData &&
8038                         field.valueField &&
8039                         field.displayField &&
8040                         // combos' with local stores can
8041                         // be queried via setValue()
8042                         // to set their value..
8043                         (field.store && !field.store.isLocal)
8044                         ) {
8045                         // it's a combo
8046                         var sd = { };
8047                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8048                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8049                         field.setFromData(sd);
8050
8051                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8052                         
8053                         field.setFromData(values);
8054                         
8055                     } else {
8056                         field.setValue(values[id]);
8057                     }
8058
8059
8060                     if(this.trackResetOnLoad){
8061                         field.originalValue = field.getValue();
8062                     }
8063                 }
8064             }
8065         }
8066
8067         //Roo.each(this.childForms || [], function (f) {
8068         //    f.setValues(values);
8069         //});
8070
8071         return this;
8072     },
8073
8074     /**
8075      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8076      * they are returned as an array.
8077      * @param {Boolean} asString
8078      * @return {Object}
8079      */
8080     getValues : function(asString){
8081         //if (this.childForms) {
8082             // copy values from the child forms
8083         //    Roo.each(this.childForms, function (f) {
8084         //        this.setValues(f.getValues());
8085         //    }, this);
8086         //}
8087
8088
8089
8090         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8091         if(asString === true){
8092             return fs;
8093         }
8094         return Roo.urlDecode(fs);
8095     },
8096
8097     /**
8098      * Returns the fields in this form as an object with key/value pairs.
8099      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8100      * @return {Object}
8101      */
8102     getFieldValues : function(with_hidden)
8103     {
8104         var items = this.getItems();
8105         var ret = {};
8106         items.each(function(f){
8107             
8108             if (!f.getName()) {
8109                 return;
8110             }
8111             
8112             var v = f.getValue();
8113             
8114             if (f.inputType =='radio') {
8115                 if (typeof(ret[f.getName()]) == 'undefined') {
8116                     ret[f.getName()] = ''; // empty..
8117                 }
8118
8119                 if (!f.el.dom.checked) {
8120                     return;
8121
8122                 }
8123                 v = f.el.dom.value;
8124
8125             }
8126             
8127             if(f.xtype == 'MoneyField'){
8128                 ret[f.currencyName] = f.getCurrency();
8129             }
8130
8131             // not sure if this supported any more..
8132             if ((typeof(v) == 'object') && f.getRawValue) {
8133                 v = f.getRawValue() ; // dates..
8134             }
8135             // combo boxes where name != hiddenName...
8136             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8137                 ret[f.name] = f.getRawValue();
8138             }
8139             ret[f.getName()] = v;
8140         });
8141
8142         return ret;
8143     },
8144
8145     /**
8146      * Clears all invalid messages in this form.
8147      * @return {BasicForm} this
8148      */
8149     clearInvalid : function(){
8150         var items = this.getItems();
8151
8152         items.each(function(f){
8153            f.clearInvalid();
8154         });
8155
8156         return this;
8157     },
8158
8159     /**
8160      * Resets this form.
8161      * @return {BasicForm} this
8162      */
8163     reset : function(){
8164         var items = this.getItems();
8165         items.each(function(f){
8166             f.reset();
8167         });
8168
8169         Roo.each(this.childForms || [], function (f) {
8170             f.reset();
8171         });
8172
8173
8174         return this;
8175     },
8176     
8177     getItems : function()
8178     {
8179         var r=new Roo.util.MixedCollection(false, function(o){
8180             return o.id || (o.id = Roo.id());
8181         });
8182         var iter = function(el) {
8183             if (el.inputEl) {
8184                 r.add(el);
8185             }
8186             if (!el.items) {
8187                 return;
8188             }
8189             Roo.each(el.items,function(e) {
8190                 iter(e);
8191             });
8192         };
8193
8194         iter(this);
8195         return r;
8196     },
8197     
8198     hideFields : function(items)
8199     {
8200         Roo.each(items, function(i){
8201             
8202             var f = this.findField(i);
8203             
8204             if(!f){
8205                 return;
8206             }
8207             
8208             if(f.xtype == 'DateField'){
8209                 f.setVisible(false);
8210                 return;
8211             }
8212             
8213             f.hide();
8214             
8215         }, this);
8216     },
8217     
8218     showFields : function(items)
8219     {
8220         Roo.each(items, function(i){
8221             
8222             var f = this.findField(i);
8223             
8224             if(!f){
8225                 return;
8226             }
8227             
8228             if(f.xtype == 'DateField'){
8229                 f.setVisible(true);
8230                 return;
8231             }
8232             
8233             f.show();
8234             
8235         }, this);
8236     }
8237
8238 });
8239
8240 Roo.apply(Roo.bootstrap.Form, {
8241     
8242     popover : {
8243         
8244         padding : 5,
8245         
8246         isApplied : false,
8247         
8248         isMasked : false,
8249         
8250         form : false,
8251         
8252         target : false,
8253         
8254         toolTip : false,
8255         
8256         intervalID : false,
8257         
8258         maskEl : false,
8259         
8260         apply : function()
8261         {
8262             if(this.isApplied){
8263                 return;
8264             }
8265             
8266             this.maskEl = {
8267                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8268                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8269                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8270                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8271             };
8272             
8273             this.maskEl.top.enableDisplayMode("block");
8274             this.maskEl.left.enableDisplayMode("block");
8275             this.maskEl.bottom.enableDisplayMode("block");
8276             this.maskEl.right.enableDisplayMode("block");
8277             
8278             this.toolTip = new Roo.bootstrap.Tooltip({
8279                 cls : 'roo-form-error-popover',
8280                 alignment : {
8281                     'left' : ['r-l', [-2,0], 'right'],
8282                     'right' : ['l-r', [2,0], 'left'],
8283                     'bottom' : ['tl-bl', [0,2], 'top'],
8284                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8285                 }
8286             });
8287             
8288             this.toolTip.render(Roo.get(document.body));
8289
8290             this.toolTip.el.enableDisplayMode("block");
8291             
8292             Roo.get(document.body).on('click', function(){
8293                 this.unmask();
8294             }, this);
8295             
8296             Roo.get(document.body).on('touchstart', function(){
8297                 this.unmask();
8298             }, this);
8299             
8300             this.isApplied = true
8301         },
8302         
8303         mask : function(form, target)
8304         {
8305             this.form = form;
8306             
8307             this.target = target;
8308             
8309             if(!this.form.errorMask || !target.el){
8310                 return;
8311             }
8312             
8313             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8314             
8315             Roo.log(scrollable);
8316             
8317             var ot = this.target.el.calcOffsetsTo(scrollable);
8318             
8319             var scrollTo = ot[1] - this.form.maskOffset;
8320             
8321             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8322             
8323             scrollable.scrollTo('top', scrollTo);
8324             
8325             var box = this.target.el.getBox();
8326             Roo.log(box);
8327             var zIndex = Roo.bootstrap.Modal.zIndex++;
8328
8329             
8330             this.maskEl.top.setStyle('position', 'absolute');
8331             this.maskEl.top.setStyle('z-index', zIndex);
8332             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8333             this.maskEl.top.setLeft(0);
8334             this.maskEl.top.setTop(0);
8335             this.maskEl.top.show();
8336             
8337             this.maskEl.left.setStyle('position', 'absolute');
8338             this.maskEl.left.setStyle('z-index', zIndex);
8339             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8340             this.maskEl.left.setLeft(0);
8341             this.maskEl.left.setTop(box.y - this.padding);
8342             this.maskEl.left.show();
8343
8344             this.maskEl.bottom.setStyle('position', 'absolute');
8345             this.maskEl.bottom.setStyle('z-index', zIndex);
8346             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8347             this.maskEl.bottom.setLeft(0);
8348             this.maskEl.bottom.setTop(box.bottom + this.padding);
8349             this.maskEl.bottom.show();
8350
8351             this.maskEl.right.setStyle('position', 'absolute');
8352             this.maskEl.right.setStyle('z-index', zIndex);
8353             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8354             this.maskEl.right.setLeft(box.right + this.padding);
8355             this.maskEl.right.setTop(box.y - this.padding);
8356             this.maskEl.right.show();
8357
8358             this.toolTip.bindEl = this.target.el;
8359
8360             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8361
8362             var tip = this.target.blankText;
8363
8364             if(this.target.getValue() !== '' ) {
8365                 
8366                 if (this.target.invalidText.length) {
8367                     tip = this.target.invalidText;
8368                 } else if (this.target.regexText.length){
8369                     tip = this.target.regexText;
8370                 }
8371             }
8372
8373             this.toolTip.show(tip);
8374
8375             this.intervalID = window.setInterval(function() {
8376                 Roo.bootstrap.Form.popover.unmask();
8377             }, 10000);
8378
8379             window.onwheel = function(){ return false;};
8380             
8381             (function(){ this.isMasked = true; }).defer(500, this);
8382             
8383         },
8384         
8385         unmask : function()
8386         {
8387             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8388                 return;
8389             }
8390             
8391             this.maskEl.top.setStyle('position', 'absolute');
8392             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8393             this.maskEl.top.hide();
8394
8395             this.maskEl.left.setStyle('position', 'absolute');
8396             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8397             this.maskEl.left.hide();
8398
8399             this.maskEl.bottom.setStyle('position', 'absolute');
8400             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8401             this.maskEl.bottom.hide();
8402
8403             this.maskEl.right.setStyle('position', 'absolute');
8404             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8405             this.maskEl.right.hide();
8406             
8407             this.toolTip.hide();
8408             
8409             this.toolTip.el.hide();
8410             
8411             window.onwheel = function(){ return true;};
8412             
8413             if(this.intervalID){
8414                 window.clearInterval(this.intervalID);
8415                 this.intervalID = false;
8416             }
8417             
8418             this.isMasked = false;
8419             
8420         }
8421         
8422     }
8423     
8424 });
8425
8426 /*
8427  * Based on:
8428  * Ext JS Library 1.1.1
8429  * Copyright(c) 2006-2007, Ext JS, LLC.
8430  *
8431  * Originally Released Under LGPL - original licence link has changed is not relivant.
8432  *
8433  * Fork - LGPL
8434  * <script type="text/javascript">
8435  */
8436 /**
8437  * @class Roo.form.VTypes
8438  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8439  * @singleton
8440  */
8441 Roo.form.VTypes = function(){
8442     // closure these in so they are only created once.
8443     var alpha = /^[a-zA-Z_]+$/;
8444     var alphanum = /^[a-zA-Z0-9_]+$/;
8445     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8446     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8447
8448     // All these messages and functions are configurable
8449     return {
8450         /**
8451          * The function used to validate email addresses
8452          * @param {String} value The email address
8453          */
8454         'email' : function(v){
8455             return email.test(v);
8456         },
8457         /**
8458          * The error text to display when the email validation function returns false
8459          * @type String
8460          */
8461         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8462         /**
8463          * The keystroke filter mask to be applied on email input
8464          * @type RegExp
8465          */
8466         'emailMask' : /[a-z0-9_\.\-@]/i,
8467
8468         /**
8469          * The function used to validate URLs
8470          * @param {String} value The URL
8471          */
8472         'url' : function(v){
8473             return url.test(v);
8474         },
8475         /**
8476          * The error text to display when the url validation function returns false
8477          * @type String
8478          */
8479         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8480         
8481         /**
8482          * The function used to validate alpha values
8483          * @param {String} value The value
8484          */
8485         'alpha' : function(v){
8486             return alpha.test(v);
8487         },
8488         /**
8489          * The error text to display when the alpha validation function returns false
8490          * @type String
8491          */
8492         'alphaText' : 'This field should only contain letters and _',
8493         /**
8494          * The keystroke filter mask to be applied on alpha input
8495          * @type RegExp
8496          */
8497         'alphaMask' : /[a-z_]/i,
8498
8499         /**
8500          * The function used to validate alphanumeric values
8501          * @param {String} value The value
8502          */
8503         'alphanum' : function(v){
8504             return alphanum.test(v);
8505         },
8506         /**
8507          * The error text to display when the alphanumeric validation function returns false
8508          * @type String
8509          */
8510         'alphanumText' : 'This field should only contain letters, numbers and _',
8511         /**
8512          * The keystroke filter mask to be applied on alphanumeric input
8513          * @type RegExp
8514          */
8515         'alphanumMask' : /[a-z0-9_]/i
8516     };
8517 }();/*
8518  * - LGPL
8519  *
8520  * Input
8521  * 
8522  */
8523
8524 /**
8525  * @class Roo.bootstrap.Input
8526  * @extends Roo.bootstrap.Component
8527  * Bootstrap Input class
8528  * @cfg {Boolean} disabled is it disabled
8529  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8530  * @cfg {String} name name of the input
8531  * @cfg {string} fieldLabel - the label associated
8532  * @cfg {string} placeholder - placeholder to put in text.
8533  * @cfg {string}  before - input group add on before
8534  * @cfg {string} after - input group add on after
8535  * @cfg {string} size - (lg|sm) or leave empty..
8536  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8537  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8538  * @cfg {Number} md colspan out of 12 for computer-sized screens
8539  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8540  * @cfg {string} value default value of the input
8541  * @cfg {Number} labelWidth set the width of label 
8542  * @cfg {Number} labellg set the width of label (1-12)
8543  * @cfg {Number} labelmd set the width of label (1-12)
8544  * @cfg {Number} labelsm set the width of label (1-12)
8545  * @cfg {Number} labelxs set the width of label (1-12)
8546  * @cfg {String} labelAlign (top|left)
8547  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8548  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8549  * @cfg {String} indicatorpos (left|right) default left
8550  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8551  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8552
8553  * @cfg {String} align (left|center|right) Default left
8554  * @cfg {Boolean} forceFeedback (true|false) Default false
8555  * 
8556  * @constructor
8557  * Create a new Input
8558  * @param {Object} config The config object
8559  */
8560
8561 Roo.bootstrap.Input = function(config){
8562     
8563     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8564     
8565     this.addEvents({
8566         /**
8567          * @event focus
8568          * Fires when this field receives input focus.
8569          * @param {Roo.form.Field} this
8570          */
8571         focus : true,
8572         /**
8573          * @event blur
8574          * Fires when this field loses input focus.
8575          * @param {Roo.form.Field} this
8576          */
8577         blur : true,
8578         /**
8579          * @event specialkey
8580          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8581          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8582          * @param {Roo.form.Field} this
8583          * @param {Roo.EventObject} e The event object
8584          */
8585         specialkey : true,
8586         /**
8587          * @event change
8588          * Fires just before the field blurs if the field value has changed.
8589          * @param {Roo.form.Field} this
8590          * @param {Mixed} newValue The new value
8591          * @param {Mixed} oldValue The original value
8592          */
8593         change : true,
8594         /**
8595          * @event invalid
8596          * Fires after the field has been marked as invalid.
8597          * @param {Roo.form.Field} this
8598          * @param {String} msg The validation message
8599          */
8600         invalid : true,
8601         /**
8602          * @event valid
8603          * Fires after the field has been validated with no errors.
8604          * @param {Roo.form.Field} this
8605          */
8606         valid : true,
8607          /**
8608          * @event keyup
8609          * Fires after the key up
8610          * @param {Roo.form.Field} this
8611          * @param {Roo.EventObject}  e The event Object
8612          */
8613         keyup : true
8614     });
8615 };
8616
8617 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8618      /**
8619      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8620       automatic validation (defaults to "keyup").
8621      */
8622     validationEvent : "keyup",
8623      /**
8624      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8625      */
8626     validateOnBlur : true,
8627     /**
8628      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8629      */
8630     validationDelay : 250,
8631      /**
8632      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8633      */
8634     focusClass : "x-form-focus",  // not needed???
8635     
8636        
8637     /**
8638      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8639      */
8640     invalidClass : "has-warning",
8641     
8642     /**
8643      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8644      */
8645     validClass : "has-success",
8646     
8647     /**
8648      * @cfg {Boolean} hasFeedback (true|false) default true
8649      */
8650     hasFeedback : true,
8651     
8652     /**
8653      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8654      */
8655     invalidFeedbackClass : "glyphicon-warning-sign",
8656     
8657     /**
8658      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8659      */
8660     validFeedbackClass : "glyphicon-ok",
8661     
8662     /**
8663      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8664      */
8665     selectOnFocus : false,
8666     
8667      /**
8668      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8669      */
8670     maskRe : null,
8671        /**
8672      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8673      */
8674     vtype : null,
8675     
8676       /**
8677      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8678      */
8679     disableKeyFilter : false,
8680     
8681        /**
8682      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8683      */
8684     disabled : false,
8685      /**
8686      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8687      */
8688     allowBlank : true,
8689     /**
8690      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8691      */
8692     blankText : "Please complete this mandatory field",
8693     
8694      /**
8695      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8696      */
8697     minLength : 0,
8698     /**
8699      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8700      */
8701     maxLength : Number.MAX_VALUE,
8702     /**
8703      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8704      */
8705     minLengthText : "The minimum length for this field is {0}",
8706     /**
8707      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8708      */
8709     maxLengthText : "The maximum length for this field is {0}",
8710   
8711     
8712     /**
8713      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8714      * If available, this function will be called only after the basic validators all return true, and will be passed the
8715      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8716      */
8717     validator : null,
8718     /**
8719      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8720      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8721      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8722      */
8723     regex : null,
8724     /**
8725      * @cfg {String} regexText -- Depricated - use Invalid Text
8726      */
8727     regexText : "",
8728     
8729     /**
8730      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8731      */
8732     invalidText : "",
8733     
8734     
8735     
8736     autocomplete: false,
8737     
8738     
8739     fieldLabel : '',
8740     inputType : 'text',
8741     
8742     name : false,
8743     placeholder: false,
8744     before : false,
8745     after : false,
8746     size : false,
8747     hasFocus : false,
8748     preventMark: false,
8749     isFormField : true,
8750     value : '',
8751     labelWidth : 2,
8752     labelAlign : false,
8753     readOnly : false,
8754     align : false,
8755     formatedValue : false,
8756     forceFeedback : false,
8757     
8758     indicatorpos : 'left',
8759     
8760     labellg : 0,
8761     labelmd : 0,
8762     labelsm : 0,
8763     labelxs : 0,
8764     
8765     capture : '',
8766     accept : '',
8767     
8768     parentLabelAlign : function()
8769     {
8770         var parent = this;
8771         while (parent.parent()) {
8772             parent = parent.parent();
8773             if (typeof(parent.labelAlign) !='undefined') {
8774                 return parent.labelAlign;
8775             }
8776         }
8777         return 'left';
8778         
8779     },
8780     
8781     getAutoCreate : function()
8782     {
8783         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8784         
8785         var id = Roo.id();
8786         
8787         var cfg = {};
8788         
8789         if(this.inputType != 'hidden'){
8790             cfg.cls = 'form-group' //input-group
8791         }
8792         
8793         var input =  {
8794             tag: 'input',
8795             id : id,
8796             type : this.inputType,
8797             value : this.value,
8798             cls : 'form-control',
8799             placeholder : this.placeholder || '',
8800             autocomplete : this.autocomplete || 'new-password'
8801         };
8802         
8803         if(this.capture.length){
8804             input.capture = this.capture;
8805         }
8806         
8807         if(this.accept.length){
8808             input.accept = this.accept + "/*";
8809         }
8810         
8811         if(this.align){
8812             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8813         }
8814         
8815         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8816             input.maxLength = this.maxLength;
8817         }
8818         
8819         if (this.disabled) {
8820             input.disabled=true;
8821         }
8822         
8823         if (this.readOnly) {
8824             input.readonly=true;
8825         }
8826         
8827         if (this.name) {
8828             input.name = this.name;
8829         }
8830         
8831         if (this.size) {
8832             input.cls += ' input-' + this.size;
8833         }
8834         
8835         var settings=this;
8836         ['xs','sm','md','lg'].map(function(size){
8837             if (settings[size]) {
8838                 cfg.cls += ' col-' + size + '-' + settings[size];
8839             }
8840         });
8841         
8842         var inputblock = input;
8843         
8844         var feedback = {
8845             tag: 'span',
8846             cls: 'glyphicon form-control-feedback'
8847         };
8848             
8849         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8850             
8851             inputblock = {
8852                 cls : 'has-feedback',
8853                 cn :  [
8854                     input,
8855                     feedback
8856                 ] 
8857             };  
8858         }
8859         
8860         if (this.before || this.after) {
8861             
8862             inputblock = {
8863                 cls : 'input-group',
8864                 cn :  [] 
8865             };
8866             
8867             if (this.before && typeof(this.before) == 'string') {
8868                 
8869                 inputblock.cn.push({
8870                     tag :'span',
8871                     cls : 'roo-input-before input-group-addon',
8872                     html : this.before
8873                 });
8874             }
8875             if (this.before && typeof(this.before) == 'object') {
8876                 this.before = Roo.factory(this.before);
8877                 
8878                 inputblock.cn.push({
8879                     tag :'span',
8880                     cls : 'roo-input-before input-group-' +
8881                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8882                 });
8883             }
8884             
8885             inputblock.cn.push(input);
8886             
8887             if (this.after && typeof(this.after) == 'string') {
8888                 inputblock.cn.push({
8889                     tag :'span',
8890                     cls : 'roo-input-after input-group-addon',
8891                     html : this.after
8892                 });
8893             }
8894             if (this.after && typeof(this.after) == 'object') {
8895                 this.after = Roo.factory(this.after);
8896                 
8897                 inputblock.cn.push({
8898                     tag :'span',
8899                     cls : 'roo-input-after input-group-' +
8900                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8901                 });
8902             }
8903             
8904             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8905                 inputblock.cls += ' has-feedback';
8906                 inputblock.cn.push(feedback);
8907             }
8908         };
8909         
8910         if (align ==='left' && this.fieldLabel.length) {
8911             
8912             cfg.cls += ' roo-form-group-label-left';
8913             
8914             cfg.cn = [
8915                 {
8916                     tag : 'i',
8917                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8918                     tooltip : 'This field is required'
8919                 },
8920                 {
8921                     tag: 'label',
8922                     'for' :  id,
8923                     cls : 'control-label',
8924                     html : this.fieldLabel
8925
8926                 },
8927                 {
8928                     cls : "", 
8929                     cn: [
8930                         inputblock
8931                     ]
8932                 }
8933             ];
8934             
8935             var labelCfg = cfg.cn[1];
8936             var contentCfg = cfg.cn[2];
8937             
8938             if(this.indicatorpos == 'right'){
8939                 cfg.cn = [
8940                     {
8941                         tag: 'label',
8942                         'for' :  id,
8943                         cls : 'control-label',
8944                         cn : [
8945                             {
8946                                 tag : 'span',
8947                                 html : this.fieldLabel
8948                             },
8949                             {
8950                                 tag : 'i',
8951                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8952                                 tooltip : 'This field is required'
8953                             }
8954                         ]
8955                     },
8956                     {
8957                         cls : "",
8958                         cn: [
8959                             inputblock
8960                         ]
8961                     }
8962
8963                 ];
8964                 
8965                 labelCfg = cfg.cn[0];
8966                 contentCfg = cfg.cn[1];
8967             
8968             }
8969             
8970             if(this.labelWidth > 12){
8971                 labelCfg.style = "width: " + this.labelWidth + 'px';
8972             }
8973             
8974             if(this.labelWidth < 13 && this.labelmd == 0){
8975                 this.labelmd = this.labelWidth;
8976             }
8977             
8978             if(this.labellg > 0){
8979                 labelCfg.cls += ' col-lg-' + this.labellg;
8980                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8981             }
8982             
8983             if(this.labelmd > 0){
8984                 labelCfg.cls += ' col-md-' + this.labelmd;
8985                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8986             }
8987             
8988             if(this.labelsm > 0){
8989                 labelCfg.cls += ' col-sm-' + this.labelsm;
8990                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8991             }
8992             
8993             if(this.labelxs > 0){
8994                 labelCfg.cls += ' col-xs-' + this.labelxs;
8995                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8996             }
8997             
8998             
8999         } else if ( this.fieldLabel.length) {
9000                 
9001             cfg.cn = [
9002                 {
9003                     tag : 'i',
9004                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9005                     tooltip : 'This field is required'
9006                 },
9007                 {
9008                     tag: 'label',
9009                    //cls : 'input-group-addon',
9010                     html : this.fieldLabel
9011
9012                 },
9013
9014                inputblock
9015
9016            ];
9017            
9018            if(this.indicatorpos == 'right'){
9019                 
9020                 cfg.cn = [
9021                     {
9022                         tag: 'label',
9023                        //cls : 'input-group-addon',
9024                         html : this.fieldLabel
9025
9026                     },
9027                     {
9028                         tag : 'i',
9029                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9030                         tooltip : 'This field is required'
9031                     },
9032
9033                    inputblock
9034
9035                ];
9036
9037             }
9038
9039         } else {
9040             
9041             cfg.cn = [
9042
9043                     inputblock
9044
9045             ];
9046                 
9047                 
9048         };
9049         
9050         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9051            cfg.cls += ' navbar-form';
9052         }
9053         
9054         if (this.parentType === 'NavGroup') {
9055            cfg.cls += ' navbar-form';
9056            cfg.tag = 'li';
9057         }
9058         
9059         return cfg;
9060         
9061     },
9062     /**
9063      * return the real input element.
9064      */
9065     inputEl: function ()
9066     {
9067         return this.el.select('input.form-control',true).first();
9068     },
9069     
9070     tooltipEl : function()
9071     {
9072         return this.inputEl();
9073     },
9074     
9075     indicatorEl : function()
9076     {
9077         var indicator = this.el.select('i.roo-required-indicator',true).first();
9078         
9079         if(!indicator){
9080             return false;
9081         }
9082         
9083         return indicator;
9084         
9085     },
9086     
9087     setDisabled : function(v)
9088     {
9089         var i  = this.inputEl().dom;
9090         if (!v) {
9091             i.removeAttribute('disabled');
9092             return;
9093             
9094         }
9095         i.setAttribute('disabled','true');
9096     },
9097     initEvents : function()
9098     {
9099           
9100         this.inputEl().on("keydown" , this.fireKey,  this);
9101         this.inputEl().on("focus", this.onFocus,  this);
9102         this.inputEl().on("blur", this.onBlur,  this);
9103         
9104         this.inputEl().relayEvent('keyup', this);
9105         
9106         this.indicator = this.indicatorEl();
9107         
9108         if(this.indicator){
9109             this.indicator.addClass('invisible');
9110         }
9111  
9112         // reference to original value for reset
9113         this.originalValue = this.getValue();
9114         //Roo.form.TextField.superclass.initEvents.call(this);
9115         if(this.validationEvent == 'keyup'){
9116             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9117             this.inputEl().on('keyup', this.filterValidation, this);
9118         }
9119         else if(this.validationEvent !== false){
9120             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9121         }
9122         
9123         if(this.selectOnFocus){
9124             this.on("focus", this.preFocus, this);
9125             
9126         }
9127         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9128             this.inputEl().on("keypress", this.filterKeys, this);
9129         } else {
9130             this.inputEl().relayEvent('keypress', this);
9131         }
9132        /* if(this.grow){
9133             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9134             this.el.on("click", this.autoSize,  this);
9135         }
9136         */
9137         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9138             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9139         }
9140         
9141         if (typeof(this.before) == 'object') {
9142             this.before.render(this.el.select('.roo-input-before',true).first());
9143         }
9144         if (typeof(this.after) == 'object') {
9145             this.after.render(this.el.select('.roo-input-after',true).first());
9146         }
9147         
9148         this.inputEl().on('change', this.onChange, this);
9149         
9150     },
9151     filterValidation : function(e){
9152         if(!e.isNavKeyPress()){
9153             this.validationTask.delay(this.validationDelay);
9154         }
9155     },
9156      /**
9157      * Validates the field value
9158      * @return {Boolean} True if the value is valid, else false
9159      */
9160     validate : function(){
9161         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9162         if(this.disabled || this.validateValue(this.getRawValue())){
9163             this.markValid();
9164             return true;
9165         }
9166         
9167         this.markInvalid();
9168         return false;
9169     },
9170     
9171     
9172     /**
9173      * Validates a value according to the field's validation rules and marks the field as invalid
9174      * if the validation fails
9175      * @param {Mixed} value The value to validate
9176      * @return {Boolean} True if the value is valid, else false
9177      */
9178     validateValue : function(value)
9179     {
9180         if(this.getVisibilityEl().hasClass('hidden')){
9181             return true;
9182         }
9183         
9184         if(value.length < 1)  { // if it's blank
9185             if(this.allowBlank){
9186                 return true;
9187             }
9188             return false;
9189         }
9190         
9191         if(value.length < this.minLength){
9192             return false;
9193         }
9194         if(value.length > this.maxLength){
9195             return false;
9196         }
9197         if(this.vtype){
9198             var vt = Roo.form.VTypes;
9199             if(!vt[this.vtype](value, this)){
9200                 return false;
9201             }
9202         }
9203         if(typeof this.validator == "function"){
9204             var msg = this.validator(value);
9205             if(msg !== true){
9206                 return false;
9207             }
9208             if (typeof(msg) == 'string') {
9209                 this.invalidText = msg;
9210             }
9211         }
9212         
9213         if(this.regex && !this.regex.test(value)){
9214             return false;
9215         }
9216         
9217         return true;
9218     },
9219     
9220      // private
9221     fireKey : function(e){
9222         //Roo.log('field ' + e.getKey());
9223         if(e.isNavKeyPress()){
9224             this.fireEvent("specialkey", this, e);
9225         }
9226     },
9227     focus : function (selectText){
9228         if(this.rendered){
9229             this.inputEl().focus();
9230             if(selectText === true){
9231                 this.inputEl().dom.select();
9232             }
9233         }
9234         return this;
9235     } ,
9236     
9237     onFocus : function(){
9238         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9239            // this.el.addClass(this.focusClass);
9240         }
9241         if(!this.hasFocus){
9242             this.hasFocus = true;
9243             this.startValue = this.getValue();
9244             this.fireEvent("focus", this);
9245         }
9246     },
9247     
9248     beforeBlur : Roo.emptyFn,
9249
9250     
9251     // private
9252     onBlur : function(){
9253         this.beforeBlur();
9254         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9255             //this.el.removeClass(this.focusClass);
9256         }
9257         this.hasFocus = false;
9258         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9259             this.validate();
9260         }
9261         var v = this.getValue();
9262         if(String(v) !== String(this.startValue)){
9263             this.fireEvent('change', this, v, this.startValue);
9264         }
9265         this.fireEvent("blur", this);
9266     },
9267     
9268     onChange : function(e)
9269     {
9270         var v = this.getValue();
9271         if(String(v) !== String(this.startValue)){
9272             this.fireEvent('change', this, v, this.startValue);
9273         }
9274         
9275     },
9276     
9277     /**
9278      * Resets the current field value to the originally loaded value and clears any validation messages
9279      */
9280     reset : function(){
9281         this.setValue(this.originalValue);
9282         this.validate();
9283     },
9284      /**
9285      * Returns the name of the field
9286      * @return {Mixed} name The name field
9287      */
9288     getName: function(){
9289         return this.name;
9290     },
9291      /**
9292      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9293      * @return {Mixed} value The field value
9294      */
9295     getValue : function(){
9296         
9297         var v = this.inputEl().getValue();
9298         
9299         return v;
9300     },
9301     /**
9302      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9303      * @return {Mixed} value The field value
9304      */
9305     getRawValue : function(){
9306         var v = this.inputEl().getValue();
9307         
9308         return v;
9309     },
9310     
9311     /**
9312      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9313      * @param {Mixed} value The value to set
9314      */
9315     setRawValue : function(v){
9316         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9317     },
9318     
9319     selectText : function(start, end){
9320         var v = this.getRawValue();
9321         if(v.length > 0){
9322             start = start === undefined ? 0 : start;
9323             end = end === undefined ? v.length : end;
9324             var d = this.inputEl().dom;
9325             if(d.setSelectionRange){
9326                 d.setSelectionRange(start, end);
9327             }else if(d.createTextRange){
9328                 var range = d.createTextRange();
9329                 range.moveStart("character", start);
9330                 range.moveEnd("character", v.length-end);
9331                 range.select();
9332             }
9333         }
9334     },
9335     
9336     /**
9337      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9338      * @param {Mixed} value The value to set
9339      */
9340     setValue : function(v){
9341         this.value = v;
9342         if(this.rendered){
9343             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9344             this.validate();
9345         }
9346     },
9347     
9348     /*
9349     processValue : function(value){
9350         if(this.stripCharsRe){
9351             var newValue = value.replace(this.stripCharsRe, '');
9352             if(newValue !== value){
9353                 this.setRawValue(newValue);
9354                 return newValue;
9355             }
9356         }
9357         return value;
9358     },
9359   */
9360     preFocus : function(){
9361         
9362         if(this.selectOnFocus){
9363             this.inputEl().dom.select();
9364         }
9365     },
9366     filterKeys : function(e){
9367         var k = e.getKey();
9368         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9369             return;
9370         }
9371         var c = e.getCharCode(), cc = String.fromCharCode(c);
9372         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9373             return;
9374         }
9375         if(!this.maskRe.test(cc)){
9376             e.stopEvent();
9377         }
9378     },
9379      /**
9380      * Clear any invalid styles/messages for this field
9381      */
9382     clearInvalid : function(){
9383         
9384         if(!this.el || this.preventMark){ // not rendered
9385             return;
9386         }
9387         
9388      
9389         this.el.removeClass(this.invalidClass);
9390         
9391         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9392             
9393             var feedback = this.el.select('.form-control-feedback', true).first();
9394             
9395             if(feedback){
9396                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9397             }
9398             
9399         }
9400         
9401         this.fireEvent('valid', this);
9402     },
9403     
9404      /**
9405      * Mark this field as valid
9406      */
9407     markValid : function()
9408     {
9409         if(!this.el  || this.preventMark){ // not rendered...
9410             return;
9411         }
9412         
9413         this.el.removeClass([this.invalidClass, this.validClass]);
9414         
9415         var feedback = this.el.select('.form-control-feedback', true).first();
9416             
9417         if(feedback){
9418             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9419         }
9420         
9421         if(this.indicator){
9422             this.indicator.removeClass('visible');
9423             this.indicator.addClass('invisible');
9424         }
9425         
9426         if(this.disabled){
9427             return;
9428         }
9429         
9430         if(this.allowBlank && !this.getRawValue().length){
9431             return;
9432         }
9433         
9434         this.el.addClass(this.validClass);
9435         
9436         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9437             
9438             var feedback = this.el.select('.form-control-feedback', true).first();
9439             
9440             if(feedback){
9441                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9442                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9443             }
9444             
9445         }
9446         
9447         this.fireEvent('valid', this);
9448     },
9449     
9450      /**
9451      * Mark this field as invalid
9452      * @param {String} msg The validation message
9453      */
9454     markInvalid : function(msg)
9455     {
9456         if(!this.el  || this.preventMark){ // not rendered
9457             return;
9458         }
9459         
9460         this.el.removeClass([this.invalidClass, this.validClass]);
9461         
9462         var feedback = this.el.select('.form-control-feedback', true).first();
9463             
9464         if(feedback){
9465             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9466         }
9467
9468         if(this.disabled){
9469             return;
9470         }
9471         
9472         if(this.allowBlank && !this.getRawValue().length){
9473             return;
9474         }
9475         
9476         if(this.indicator){
9477             this.indicator.removeClass('invisible');
9478             this.indicator.addClass('visible');
9479         }
9480         
9481         this.el.addClass(this.invalidClass);
9482         
9483         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9484             
9485             var feedback = this.el.select('.form-control-feedback', true).first();
9486             
9487             if(feedback){
9488                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9489                 
9490                 if(this.getValue().length || this.forceFeedback){
9491                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9492                 }
9493                 
9494             }
9495             
9496         }
9497         
9498         this.fireEvent('invalid', this, msg);
9499     },
9500     // private
9501     SafariOnKeyDown : function(event)
9502     {
9503         // this is a workaround for a password hang bug on chrome/ webkit.
9504         if (this.inputEl().dom.type != 'password') {
9505             return;
9506         }
9507         
9508         var isSelectAll = false;
9509         
9510         if(this.inputEl().dom.selectionEnd > 0){
9511             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9512         }
9513         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9514             event.preventDefault();
9515             this.setValue('');
9516             return;
9517         }
9518         
9519         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9520             
9521             event.preventDefault();
9522             // this is very hacky as keydown always get's upper case.
9523             //
9524             var cc = String.fromCharCode(event.getCharCode());
9525             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9526             
9527         }
9528     },
9529     adjustWidth : function(tag, w){
9530         tag = tag.toLowerCase();
9531         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9532             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9533                 if(tag == 'input'){
9534                     return w + 2;
9535                 }
9536                 if(tag == 'textarea'){
9537                     return w-2;
9538                 }
9539             }else if(Roo.isOpera){
9540                 if(tag == 'input'){
9541                     return w + 2;
9542                 }
9543                 if(tag == 'textarea'){
9544                     return w-2;
9545                 }
9546             }
9547         }
9548         return w;
9549     },
9550     
9551     setFieldLabel : function(v)
9552     {
9553         if(!this.rendered){
9554             return;
9555         }
9556         
9557         if(this.indicator){
9558             var ar = this.el.select('label > span',true);
9559             
9560             if (ar.elements.length) {
9561                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9562                 this.fieldLabel = v;
9563                 return;
9564             }
9565             
9566             var br = this.el.select('label',true);
9567             
9568             if(br.elements.length) {
9569                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9570                 this.fieldLabel = v;
9571                 return;
9572             }
9573             
9574             Roo.log('Cannot Found any of label > span || label in input');
9575             return;
9576         }
9577         
9578         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9579         this.fieldLabel = v;
9580         
9581         
9582     }
9583 });
9584
9585  
9586 /*
9587  * - LGPL
9588  *
9589  * Input
9590  * 
9591  */
9592
9593 /**
9594  * @class Roo.bootstrap.TextArea
9595  * @extends Roo.bootstrap.Input
9596  * Bootstrap TextArea class
9597  * @cfg {Number} cols Specifies the visible width of a text area
9598  * @cfg {Number} rows Specifies the visible number of lines in a text area
9599  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9600  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9601  * @cfg {string} html text
9602  * 
9603  * @constructor
9604  * Create a new TextArea
9605  * @param {Object} config The config object
9606  */
9607
9608 Roo.bootstrap.TextArea = function(config){
9609     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9610    
9611 };
9612
9613 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9614      
9615     cols : false,
9616     rows : 5,
9617     readOnly : false,
9618     warp : 'soft',
9619     resize : false,
9620     value: false,
9621     html: false,
9622     
9623     getAutoCreate : function(){
9624         
9625         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9626         
9627         var id = Roo.id();
9628         
9629         var cfg = {};
9630         
9631         if(this.inputType != 'hidden'){
9632             cfg.cls = 'form-group' //input-group
9633         }
9634         
9635         var input =  {
9636             tag: 'textarea',
9637             id : id,
9638             warp : this.warp,
9639             rows : this.rows,
9640             value : this.value || '',
9641             html: this.html || '',
9642             cls : 'form-control',
9643             placeholder : this.placeholder || '' 
9644             
9645         };
9646         
9647         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9648             input.maxLength = this.maxLength;
9649         }
9650         
9651         if(this.resize){
9652             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9653         }
9654         
9655         if(this.cols){
9656             input.cols = this.cols;
9657         }
9658         
9659         if (this.readOnly) {
9660             input.readonly = true;
9661         }
9662         
9663         if (this.name) {
9664             input.name = this.name;
9665         }
9666         
9667         if (this.size) {
9668             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9669         }
9670         
9671         var settings=this;
9672         ['xs','sm','md','lg'].map(function(size){
9673             if (settings[size]) {
9674                 cfg.cls += ' col-' + size + '-' + settings[size];
9675             }
9676         });
9677         
9678         var inputblock = input;
9679         
9680         if(this.hasFeedback && !this.allowBlank){
9681             
9682             var feedback = {
9683                 tag: 'span',
9684                 cls: 'glyphicon form-control-feedback'
9685             };
9686
9687             inputblock = {
9688                 cls : 'has-feedback',
9689                 cn :  [
9690                     input,
9691                     feedback
9692                 ] 
9693             };  
9694         }
9695         
9696         
9697         if (this.before || this.after) {
9698             
9699             inputblock = {
9700                 cls : 'input-group',
9701                 cn :  [] 
9702             };
9703             if (this.before) {
9704                 inputblock.cn.push({
9705                     tag :'span',
9706                     cls : 'input-group-addon',
9707                     html : this.before
9708                 });
9709             }
9710             
9711             inputblock.cn.push(input);
9712             
9713             if(this.hasFeedback && !this.allowBlank){
9714                 inputblock.cls += ' has-feedback';
9715                 inputblock.cn.push(feedback);
9716             }
9717             
9718             if (this.after) {
9719                 inputblock.cn.push({
9720                     tag :'span',
9721                     cls : 'input-group-addon',
9722                     html : this.after
9723                 });
9724             }
9725             
9726         }
9727         
9728         if (align ==='left' && this.fieldLabel.length) {
9729             cfg.cn = [
9730                 {
9731                     tag: 'label',
9732                     'for' :  id,
9733                     cls : 'control-label',
9734                     html : this.fieldLabel
9735                 },
9736                 {
9737                     cls : "",
9738                     cn: [
9739                         inputblock
9740                     ]
9741                 }
9742
9743             ];
9744             
9745             if(this.labelWidth > 12){
9746                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9747             }
9748
9749             if(this.labelWidth < 13 && this.labelmd == 0){
9750                 this.labelmd = this.labelWidth;
9751             }
9752
9753             if(this.labellg > 0){
9754                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9755                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9756             }
9757
9758             if(this.labelmd > 0){
9759                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9760                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9761             }
9762
9763             if(this.labelsm > 0){
9764                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9765                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9766             }
9767
9768             if(this.labelxs > 0){
9769                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9770                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9771             }
9772             
9773         } else if ( this.fieldLabel.length) {
9774             cfg.cn = [
9775
9776                {
9777                    tag: 'label',
9778                    //cls : 'input-group-addon',
9779                    html : this.fieldLabel
9780
9781                },
9782
9783                inputblock
9784
9785            ];
9786
9787         } else {
9788
9789             cfg.cn = [
9790
9791                 inputblock
9792
9793             ];
9794                 
9795         }
9796         
9797         if (this.disabled) {
9798             input.disabled=true;
9799         }
9800         
9801         return cfg;
9802         
9803     },
9804     /**
9805      * return the real textarea element.
9806      */
9807     inputEl: function ()
9808     {
9809         return this.el.select('textarea.form-control',true).first();
9810     },
9811     
9812     /**
9813      * Clear any invalid styles/messages for this field
9814      */
9815     clearInvalid : function()
9816     {
9817         
9818         if(!this.el || this.preventMark){ // not rendered
9819             return;
9820         }
9821         
9822         var label = this.el.select('label', true).first();
9823         var icon = this.el.select('i.fa-star', true).first();
9824         
9825         if(label && icon){
9826             icon.remove();
9827         }
9828         
9829         this.el.removeClass(this.invalidClass);
9830         
9831         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9832             
9833             var feedback = this.el.select('.form-control-feedback', true).first();
9834             
9835             if(feedback){
9836                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9837             }
9838             
9839         }
9840         
9841         this.fireEvent('valid', this);
9842     },
9843     
9844      /**
9845      * Mark this field as valid
9846      */
9847     markValid : function()
9848     {
9849         if(!this.el  || this.preventMark){ // not rendered
9850             return;
9851         }
9852         
9853         this.el.removeClass([this.invalidClass, this.validClass]);
9854         
9855         var feedback = this.el.select('.form-control-feedback', true).first();
9856             
9857         if(feedback){
9858             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9859         }
9860
9861         if(this.disabled || this.allowBlank){
9862             return;
9863         }
9864         
9865         var label = this.el.select('label', true).first();
9866         var icon = this.el.select('i.fa-star', true).first();
9867         
9868         if(label && icon){
9869             icon.remove();
9870         }
9871         
9872         this.el.addClass(this.validClass);
9873         
9874         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9875             
9876             var feedback = this.el.select('.form-control-feedback', true).first();
9877             
9878             if(feedback){
9879                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9880                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9881             }
9882             
9883         }
9884         
9885         this.fireEvent('valid', this);
9886     },
9887     
9888      /**
9889      * Mark this field as invalid
9890      * @param {String} msg The validation message
9891      */
9892     markInvalid : function(msg)
9893     {
9894         if(!this.el  || this.preventMark){ // not rendered
9895             return;
9896         }
9897         
9898         this.el.removeClass([this.invalidClass, this.validClass]);
9899         
9900         var feedback = this.el.select('.form-control-feedback', true).first();
9901             
9902         if(feedback){
9903             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9904         }
9905
9906         if(this.disabled || this.allowBlank){
9907             return;
9908         }
9909         
9910         var label = this.el.select('label', true).first();
9911         var icon = this.el.select('i.fa-star', true).first();
9912         
9913         if(!this.getValue().length && label && !icon){
9914             this.el.createChild({
9915                 tag : 'i',
9916                 cls : 'text-danger fa fa-lg fa-star',
9917                 tooltip : 'This field is required',
9918                 style : 'margin-right:5px;'
9919             }, label, true);
9920         }
9921
9922         this.el.addClass(this.invalidClass);
9923         
9924         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9925             
9926             var feedback = this.el.select('.form-control-feedback', true).first();
9927             
9928             if(feedback){
9929                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9930                 
9931                 if(this.getValue().length || this.forceFeedback){
9932                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9933                 }
9934                 
9935             }
9936             
9937         }
9938         
9939         this.fireEvent('invalid', this, msg);
9940     }
9941 });
9942
9943  
9944 /*
9945  * - LGPL
9946  *
9947  * trigger field - base class for combo..
9948  * 
9949  */
9950  
9951 /**
9952  * @class Roo.bootstrap.TriggerField
9953  * @extends Roo.bootstrap.Input
9954  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9955  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9956  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9957  * for which you can provide a custom implementation.  For example:
9958  * <pre><code>
9959 var trigger = new Roo.bootstrap.TriggerField();
9960 trigger.onTriggerClick = myTriggerFn;
9961 trigger.applyTo('my-field');
9962 </code></pre>
9963  *
9964  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9965  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9966  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9967  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9968  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9969
9970  * @constructor
9971  * Create a new TriggerField.
9972  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9973  * to the base TextField)
9974  */
9975 Roo.bootstrap.TriggerField = function(config){
9976     this.mimicing = false;
9977     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9978 };
9979
9980 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9981     /**
9982      * @cfg {String} triggerClass A CSS class to apply to the trigger
9983      */
9984      /**
9985      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9986      */
9987     hideTrigger:false,
9988
9989     /**
9990      * @cfg {Boolean} removable (true|false) special filter default false
9991      */
9992     removable : false,
9993     
9994     /** @cfg {Boolean} grow @hide */
9995     /** @cfg {Number} growMin @hide */
9996     /** @cfg {Number} growMax @hide */
9997
9998     /**
9999      * @hide 
10000      * @method
10001      */
10002     autoSize: Roo.emptyFn,
10003     // private
10004     monitorTab : true,
10005     // private
10006     deferHeight : true,
10007
10008     
10009     actionMode : 'wrap',
10010     
10011     caret : false,
10012     
10013     
10014     getAutoCreate : function(){
10015        
10016         var align = this.labelAlign || this.parentLabelAlign();
10017         
10018         var id = Roo.id();
10019         
10020         var cfg = {
10021             cls: 'form-group' //input-group
10022         };
10023         
10024         
10025         var input =  {
10026             tag: 'input',
10027             id : id,
10028             type : this.inputType,
10029             cls : 'form-control',
10030             autocomplete: 'new-password',
10031             placeholder : this.placeholder || '' 
10032             
10033         };
10034         if (this.name) {
10035             input.name = this.name;
10036         }
10037         if (this.size) {
10038             input.cls += ' input-' + this.size;
10039         }
10040         
10041         if (this.disabled) {
10042             input.disabled=true;
10043         }
10044         
10045         var inputblock = input;
10046         
10047         if(this.hasFeedback && !this.allowBlank){
10048             
10049             var feedback = {
10050                 tag: 'span',
10051                 cls: 'glyphicon form-control-feedback'
10052             };
10053             
10054             if(this.removable && !this.editable && !this.tickable){
10055                 inputblock = {
10056                     cls : 'has-feedback',
10057                     cn :  [
10058                         inputblock,
10059                         {
10060                             tag: 'button',
10061                             html : 'x',
10062                             cls : 'roo-combo-removable-btn close'
10063                         },
10064                         feedback
10065                     ] 
10066                 };
10067             } else {
10068                 inputblock = {
10069                     cls : 'has-feedback',
10070                     cn :  [
10071                         inputblock,
10072                         feedback
10073                     ] 
10074                 };
10075             }
10076
10077         } else {
10078             if(this.removable && !this.editable && !this.tickable){
10079                 inputblock = {
10080                     cls : 'roo-removable',
10081                     cn :  [
10082                         inputblock,
10083                         {
10084                             tag: 'button',
10085                             html : 'x',
10086                             cls : 'roo-combo-removable-btn close'
10087                         }
10088                     ] 
10089                 };
10090             }
10091         }
10092         
10093         if (this.before || this.after) {
10094             
10095             inputblock = {
10096                 cls : 'input-group',
10097                 cn :  [] 
10098             };
10099             if (this.before) {
10100                 inputblock.cn.push({
10101                     tag :'span',
10102                     cls : 'input-group-addon',
10103                     html : this.before
10104                 });
10105             }
10106             
10107             inputblock.cn.push(input);
10108             
10109             if(this.hasFeedback && !this.allowBlank){
10110                 inputblock.cls += ' has-feedback';
10111                 inputblock.cn.push(feedback);
10112             }
10113             
10114             if (this.after) {
10115                 inputblock.cn.push({
10116                     tag :'span',
10117                     cls : 'input-group-addon',
10118                     html : this.after
10119                 });
10120             }
10121             
10122         };
10123         
10124         var box = {
10125             tag: 'div',
10126             cn: [
10127                 {
10128                     tag: 'input',
10129                     type : 'hidden',
10130                     cls: 'form-hidden-field'
10131                 },
10132                 inputblock
10133             ]
10134             
10135         };
10136         
10137         if(this.multiple){
10138             box = {
10139                 tag: 'div',
10140                 cn: [
10141                     {
10142                         tag: 'input',
10143                         type : 'hidden',
10144                         cls: 'form-hidden-field'
10145                     },
10146                     {
10147                         tag: 'ul',
10148                         cls: 'roo-select2-choices',
10149                         cn:[
10150                             {
10151                                 tag: 'li',
10152                                 cls: 'roo-select2-search-field',
10153                                 cn: [
10154
10155                                     inputblock
10156                                 ]
10157                             }
10158                         ]
10159                     }
10160                 ]
10161             }
10162         };
10163         
10164         var combobox = {
10165             cls: 'roo-select2-container input-group',
10166             cn: [
10167                 box
10168 //                {
10169 //                    tag: 'ul',
10170 //                    cls: 'typeahead typeahead-long dropdown-menu',
10171 //                    style: 'display:none'
10172 //                }
10173             ]
10174         };
10175         
10176         if(!this.multiple && this.showToggleBtn){
10177             
10178             var caret = {
10179                         tag: 'span',
10180                         cls: 'caret'
10181              };
10182             if (this.caret != false) {
10183                 caret = {
10184                      tag: 'i',
10185                      cls: 'fa fa-' + this.caret
10186                 };
10187                 
10188             }
10189             
10190             combobox.cn.push({
10191                 tag :'span',
10192                 cls : 'input-group-addon btn dropdown-toggle',
10193                 cn : [
10194                     caret,
10195                     {
10196                         tag: 'span',
10197                         cls: 'combobox-clear',
10198                         cn  : [
10199                             {
10200                                 tag : 'i',
10201                                 cls: 'icon-remove'
10202                             }
10203                         ]
10204                     }
10205                 ]
10206
10207             })
10208         }
10209         
10210         if(this.multiple){
10211             combobox.cls += ' roo-select2-container-multi';
10212         }
10213         
10214         if (align ==='left' && this.fieldLabel.length) {
10215             
10216             cfg.cls += ' roo-form-group-label-left';
10217
10218             cfg.cn = [
10219                 {
10220                     tag : 'i',
10221                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10222                     tooltip : 'This field is required'
10223                 },
10224                 {
10225                     tag: 'label',
10226                     'for' :  id,
10227                     cls : 'control-label',
10228                     html : this.fieldLabel
10229
10230                 },
10231                 {
10232                     cls : "", 
10233                     cn: [
10234                         combobox
10235                     ]
10236                 }
10237
10238             ];
10239             
10240             var labelCfg = cfg.cn[1];
10241             var contentCfg = cfg.cn[2];
10242             
10243             if(this.indicatorpos == 'right'){
10244                 cfg.cn = [
10245                     {
10246                         tag: 'label',
10247                         'for' :  id,
10248                         cls : 'control-label',
10249                         cn : [
10250                             {
10251                                 tag : 'span',
10252                                 html : this.fieldLabel
10253                             },
10254                             {
10255                                 tag : 'i',
10256                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10257                                 tooltip : 'This field is required'
10258                             }
10259                         ]
10260                     },
10261                     {
10262                         cls : "", 
10263                         cn: [
10264                             combobox
10265                         ]
10266                     }
10267
10268                 ];
10269                 
10270                 labelCfg = cfg.cn[0];
10271                 contentCfg = cfg.cn[1];
10272             }
10273             
10274             if(this.labelWidth > 12){
10275                 labelCfg.style = "width: " + this.labelWidth + 'px';
10276             }
10277             
10278             if(this.labelWidth < 13 && this.labelmd == 0){
10279                 this.labelmd = this.labelWidth;
10280             }
10281             
10282             if(this.labellg > 0){
10283                 labelCfg.cls += ' col-lg-' + this.labellg;
10284                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10285             }
10286             
10287             if(this.labelmd > 0){
10288                 labelCfg.cls += ' col-md-' + this.labelmd;
10289                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10290             }
10291             
10292             if(this.labelsm > 0){
10293                 labelCfg.cls += ' col-sm-' + this.labelsm;
10294                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10295             }
10296             
10297             if(this.labelxs > 0){
10298                 labelCfg.cls += ' col-xs-' + this.labelxs;
10299                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10300             }
10301             
10302         } else if ( this.fieldLabel.length) {
10303 //                Roo.log(" label");
10304             cfg.cn = [
10305                 {
10306                    tag : 'i',
10307                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10308                    tooltip : 'This field is required'
10309                },
10310                {
10311                    tag: 'label',
10312                    //cls : 'input-group-addon',
10313                    html : this.fieldLabel
10314
10315                },
10316
10317                combobox
10318
10319             ];
10320             
10321             if(this.indicatorpos == 'right'){
10322                 
10323                 cfg.cn = [
10324                     {
10325                        tag: 'label',
10326                        cn : [
10327                            {
10328                                tag : 'span',
10329                                html : this.fieldLabel
10330                            },
10331                            {
10332                               tag : 'i',
10333                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10334                               tooltip : 'This field is required'
10335                            }
10336                        ]
10337
10338                     },
10339                     combobox
10340
10341                 ];
10342
10343             }
10344
10345         } else {
10346             
10347 //                Roo.log(" no label && no align");
10348                 cfg = combobox
10349                      
10350                 
10351         }
10352         
10353         var settings=this;
10354         ['xs','sm','md','lg'].map(function(size){
10355             if (settings[size]) {
10356                 cfg.cls += ' col-' + size + '-' + settings[size];
10357             }
10358         });
10359         
10360         return cfg;
10361         
10362     },
10363     
10364     
10365     
10366     // private
10367     onResize : function(w, h){
10368 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10369 //        if(typeof w == 'number'){
10370 //            var x = w - this.trigger.getWidth();
10371 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10372 //            this.trigger.setStyle('left', x+'px');
10373 //        }
10374     },
10375
10376     // private
10377     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10378
10379     // private
10380     getResizeEl : function(){
10381         return this.inputEl();
10382     },
10383
10384     // private
10385     getPositionEl : function(){
10386         return this.inputEl();
10387     },
10388
10389     // private
10390     alignErrorIcon : function(){
10391         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10392     },
10393
10394     // private
10395     initEvents : function(){
10396         
10397         this.createList();
10398         
10399         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10400         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10401         if(!this.multiple && this.showToggleBtn){
10402             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10403             if(this.hideTrigger){
10404                 this.trigger.setDisplayed(false);
10405             }
10406             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10407         }
10408         
10409         if(this.multiple){
10410             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10411         }
10412         
10413         if(this.removable && !this.editable && !this.tickable){
10414             var close = this.closeTriggerEl();
10415             
10416             if(close){
10417                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10418                 close.on('click', this.removeBtnClick, this, close);
10419             }
10420         }
10421         
10422         //this.trigger.addClassOnOver('x-form-trigger-over');
10423         //this.trigger.addClassOnClick('x-form-trigger-click');
10424         
10425         //if(!this.width){
10426         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10427         //}
10428     },
10429     
10430     closeTriggerEl : function()
10431     {
10432         var close = this.el.select('.roo-combo-removable-btn', true).first();
10433         return close ? close : false;
10434     },
10435     
10436     removeBtnClick : function(e, h, el)
10437     {
10438         e.preventDefault();
10439         
10440         if(this.fireEvent("remove", this) !== false){
10441             this.reset();
10442             this.fireEvent("afterremove", this)
10443         }
10444     },
10445     
10446     createList : function()
10447     {
10448         this.list = Roo.get(document.body).createChild({
10449             tag: 'ul',
10450             cls: 'typeahead typeahead-long dropdown-menu',
10451             style: 'display:none'
10452         });
10453         
10454         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10455         
10456     },
10457
10458     // private
10459     initTrigger : function(){
10460        
10461     },
10462
10463     // private
10464     onDestroy : function(){
10465         if(this.trigger){
10466             this.trigger.removeAllListeners();
10467           //  this.trigger.remove();
10468         }
10469         //if(this.wrap){
10470         //    this.wrap.remove();
10471         //}
10472         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10473     },
10474
10475     // private
10476     onFocus : function(){
10477         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10478         /*
10479         if(!this.mimicing){
10480             this.wrap.addClass('x-trigger-wrap-focus');
10481             this.mimicing = true;
10482             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10483             if(this.monitorTab){
10484                 this.el.on("keydown", this.checkTab, this);
10485             }
10486         }
10487         */
10488     },
10489
10490     // private
10491     checkTab : function(e){
10492         if(e.getKey() == e.TAB){
10493             this.triggerBlur();
10494         }
10495     },
10496
10497     // private
10498     onBlur : function(){
10499         // do nothing
10500     },
10501
10502     // private
10503     mimicBlur : function(e, t){
10504         /*
10505         if(!this.wrap.contains(t) && this.validateBlur()){
10506             this.triggerBlur();
10507         }
10508         */
10509     },
10510
10511     // private
10512     triggerBlur : function(){
10513         this.mimicing = false;
10514         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10515         if(this.monitorTab){
10516             this.el.un("keydown", this.checkTab, this);
10517         }
10518         //this.wrap.removeClass('x-trigger-wrap-focus');
10519         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10520     },
10521
10522     // private
10523     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10524     validateBlur : function(e, t){
10525         return true;
10526     },
10527
10528     // private
10529     onDisable : function(){
10530         this.inputEl().dom.disabled = true;
10531         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10532         //if(this.wrap){
10533         //    this.wrap.addClass('x-item-disabled');
10534         //}
10535     },
10536
10537     // private
10538     onEnable : function(){
10539         this.inputEl().dom.disabled = false;
10540         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10541         //if(this.wrap){
10542         //    this.el.removeClass('x-item-disabled');
10543         //}
10544     },
10545
10546     // private
10547     onShow : function(){
10548         var ae = this.getActionEl();
10549         
10550         if(ae){
10551             ae.dom.style.display = '';
10552             ae.dom.style.visibility = 'visible';
10553         }
10554     },
10555
10556     // private
10557     
10558     onHide : function(){
10559         var ae = this.getActionEl();
10560         ae.dom.style.display = 'none';
10561     },
10562
10563     /**
10564      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10565      * by an implementing function.
10566      * @method
10567      * @param {EventObject} e
10568      */
10569     onTriggerClick : Roo.emptyFn
10570 });
10571  /*
10572  * Based on:
10573  * Ext JS Library 1.1.1
10574  * Copyright(c) 2006-2007, Ext JS, LLC.
10575  *
10576  * Originally Released Under LGPL - original licence link has changed is not relivant.
10577  *
10578  * Fork - LGPL
10579  * <script type="text/javascript">
10580  */
10581
10582
10583 /**
10584  * @class Roo.data.SortTypes
10585  * @singleton
10586  * Defines the default sorting (casting?) comparison functions used when sorting data.
10587  */
10588 Roo.data.SortTypes = {
10589     /**
10590      * Default sort that does nothing
10591      * @param {Mixed} s The value being converted
10592      * @return {Mixed} The comparison value
10593      */
10594     none : function(s){
10595         return s;
10596     },
10597     
10598     /**
10599      * The regular expression used to strip tags
10600      * @type {RegExp}
10601      * @property
10602      */
10603     stripTagsRE : /<\/?[^>]+>/gi,
10604     
10605     /**
10606      * Strips all HTML tags to sort on text only
10607      * @param {Mixed} s The value being converted
10608      * @return {String} The comparison value
10609      */
10610     asText : function(s){
10611         return String(s).replace(this.stripTagsRE, "");
10612     },
10613     
10614     /**
10615      * Strips all HTML tags to sort on text only - Case insensitive
10616      * @param {Mixed} s The value being converted
10617      * @return {String} The comparison value
10618      */
10619     asUCText : function(s){
10620         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10621     },
10622     
10623     /**
10624      * Case insensitive string
10625      * @param {Mixed} s The value being converted
10626      * @return {String} The comparison value
10627      */
10628     asUCString : function(s) {
10629         return String(s).toUpperCase();
10630     },
10631     
10632     /**
10633      * Date sorting
10634      * @param {Mixed} s The value being converted
10635      * @return {Number} The comparison value
10636      */
10637     asDate : function(s) {
10638         if(!s){
10639             return 0;
10640         }
10641         if(s instanceof Date){
10642             return s.getTime();
10643         }
10644         return Date.parse(String(s));
10645     },
10646     
10647     /**
10648      * Float sorting
10649      * @param {Mixed} s The value being converted
10650      * @return {Float} The comparison value
10651      */
10652     asFloat : function(s) {
10653         var val = parseFloat(String(s).replace(/,/g, ""));
10654         if(isNaN(val)) {
10655             val = 0;
10656         }
10657         return val;
10658     },
10659     
10660     /**
10661      * Integer sorting
10662      * @param {Mixed} s The value being converted
10663      * @return {Number} The comparison value
10664      */
10665     asInt : function(s) {
10666         var val = parseInt(String(s).replace(/,/g, ""));
10667         if(isNaN(val)) {
10668             val = 0;
10669         }
10670         return val;
10671     }
10672 };/*
10673  * Based on:
10674  * Ext JS Library 1.1.1
10675  * Copyright(c) 2006-2007, Ext JS, LLC.
10676  *
10677  * Originally Released Under LGPL - original licence link has changed is not relivant.
10678  *
10679  * Fork - LGPL
10680  * <script type="text/javascript">
10681  */
10682
10683 /**
10684 * @class Roo.data.Record
10685  * Instances of this class encapsulate both record <em>definition</em> information, and record
10686  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10687  * to access Records cached in an {@link Roo.data.Store} object.<br>
10688  * <p>
10689  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10690  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10691  * objects.<br>
10692  * <p>
10693  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10694  * @constructor
10695  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10696  * {@link #create}. The parameters are the same.
10697  * @param {Array} data An associative Array of data values keyed by the field name.
10698  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10699  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10700  * not specified an integer id is generated.
10701  */
10702 Roo.data.Record = function(data, id){
10703     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10704     this.data = data;
10705 };
10706
10707 /**
10708  * Generate a constructor for a specific record layout.
10709  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10710  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10711  * Each field definition object may contain the following properties: <ul>
10712  * <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,
10713  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10714  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10715  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10716  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10717  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10718  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10719  * this may be omitted.</p></li>
10720  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10721  * <ul><li>auto (Default, implies no conversion)</li>
10722  * <li>string</li>
10723  * <li>int</li>
10724  * <li>float</li>
10725  * <li>boolean</li>
10726  * <li>date</li></ul></p></li>
10727  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10728  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10729  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10730  * by the Reader into an object that will be stored in the Record. It is passed the
10731  * following parameters:<ul>
10732  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10733  * </ul></p></li>
10734  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10735  * </ul>
10736  * <br>usage:<br><pre><code>
10737 var TopicRecord = Roo.data.Record.create(
10738     {name: 'title', mapping: 'topic_title'},
10739     {name: 'author', mapping: 'username'},
10740     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10741     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10742     {name: 'lastPoster', mapping: 'user2'},
10743     {name: 'excerpt', mapping: 'post_text'}
10744 );
10745
10746 var myNewRecord = new TopicRecord({
10747     title: 'Do my job please',
10748     author: 'noobie',
10749     totalPosts: 1,
10750     lastPost: new Date(),
10751     lastPoster: 'Animal',
10752     excerpt: 'No way dude!'
10753 });
10754 myStore.add(myNewRecord);
10755 </code></pre>
10756  * @method create
10757  * @static
10758  */
10759 Roo.data.Record.create = function(o){
10760     var f = function(){
10761         f.superclass.constructor.apply(this, arguments);
10762     };
10763     Roo.extend(f, Roo.data.Record);
10764     var p = f.prototype;
10765     p.fields = new Roo.util.MixedCollection(false, function(field){
10766         return field.name;
10767     });
10768     for(var i = 0, len = o.length; i < len; i++){
10769         p.fields.add(new Roo.data.Field(o[i]));
10770     }
10771     f.getField = function(name){
10772         return p.fields.get(name);  
10773     };
10774     return f;
10775 };
10776
10777 Roo.data.Record.AUTO_ID = 1000;
10778 Roo.data.Record.EDIT = 'edit';
10779 Roo.data.Record.REJECT = 'reject';
10780 Roo.data.Record.COMMIT = 'commit';
10781
10782 Roo.data.Record.prototype = {
10783     /**
10784      * Readonly flag - true if this record has been modified.
10785      * @type Boolean
10786      */
10787     dirty : false,
10788     editing : false,
10789     error: null,
10790     modified: null,
10791
10792     // private
10793     join : function(store){
10794         this.store = store;
10795     },
10796
10797     /**
10798      * Set the named field to the specified value.
10799      * @param {String} name The name of the field to set.
10800      * @param {Object} value The value to set the field to.
10801      */
10802     set : function(name, value){
10803         if(this.data[name] == value){
10804             return;
10805         }
10806         this.dirty = true;
10807         if(!this.modified){
10808             this.modified = {};
10809         }
10810         if(typeof this.modified[name] == 'undefined'){
10811             this.modified[name] = this.data[name];
10812         }
10813         this.data[name] = value;
10814         if(!this.editing && this.store){
10815             this.store.afterEdit(this);
10816         }       
10817     },
10818
10819     /**
10820      * Get the value of the named field.
10821      * @param {String} name The name of the field to get the value of.
10822      * @return {Object} The value of the field.
10823      */
10824     get : function(name){
10825         return this.data[name]; 
10826     },
10827
10828     // private
10829     beginEdit : function(){
10830         this.editing = true;
10831         this.modified = {}; 
10832     },
10833
10834     // private
10835     cancelEdit : function(){
10836         this.editing = false;
10837         delete this.modified;
10838     },
10839
10840     // private
10841     endEdit : function(){
10842         this.editing = false;
10843         if(this.dirty && this.store){
10844             this.store.afterEdit(this);
10845         }
10846     },
10847
10848     /**
10849      * Usually called by the {@link Roo.data.Store} which owns the Record.
10850      * Rejects all changes made to the Record since either creation, or the last commit operation.
10851      * Modified fields are reverted to their original values.
10852      * <p>
10853      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10854      * of reject operations.
10855      */
10856     reject : function(){
10857         var m = this.modified;
10858         for(var n in m){
10859             if(typeof m[n] != "function"){
10860                 this.data[n] = m[n];
10861             }
10862         }
10863         this.dirty = false;
10864         delete this.modified;
10865         this.editing = false;
10866         if(this.store){
10867             this.store.afterReject(this);
10868         }
10869     },
10870
10871     /**
10872      * Usually called by the {@link Roo.data.Store} which owns the Record.
10873      * Commits all changes made to the Record since either creation, or the last commit operation.
10874      * <p>
10875      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10876      * of commit operations.
10877      */
10878     commit : function(){
10879         this.dirty = false;
10880         delete this.modified;
10881         this.editing = false;
10882         if(this.store){
10883             this.store.afterCommit(this);
10884         }
10885     },
10886
10887     // private
10888     hasError : function(){
10889         return this.error != null;
10890     },
10891
10892     // private
10893     clearError : function(){
10894         this.error = null;
10895     },
10896
10897     /**
10898      * Creates a copy of this record.
10899      * @param {String} id (optional) A new record id if you don't want to use this record's id
10900      * @return {Record}
10901      */
10902     copy : function(newId) {
10903         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10904     }
10905 };/*
10906  * Based on:
10907  * Ext JS Library 1.1.1
10908  * Copyright(c) 2006-2007, Ext JS, LLC.
10909  *
10910  * Originally Released Under LGPL - original licence link has changed is not relivant.
10911  *
10912  * Fork - LGPL
10913  * <script type="text/javascript">
10914  */
10915
10916
10917
10918 /**
10919  * @class Roo.data.Store
10920  * @extends Roo.util.Observable
10921  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10922  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10923  * <p>
10924  * 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
10925  * has no knowledge of the format of the data returned by the Proxy.<br>
10926  * <p>
10927  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10928  * instances from the data object. These records are cached and made available through accessor functions.
10929  * @constructor
10930  * Creates a new Store.
10931  * @param {Object} config A config object containing the objects needed for the Store to access data,
10932  * and read the data into Records.
10933  */
10934 Roo.data.Store = function(config){
10935     this.data = new Roo.util.MixedCollection(false);
10936     this.data.getKey = function(o){
10937         return o.id;
10938     };
10939     this.baseParams = {};
10940     // private
10941     this.paramNames = {
10942         "start" : "start",
10943         "limit" : "limit",
10944         "sort" : "sort",
10945         "dir" : "dir",
10946         "multisort" : "_multisort"
10947     };
10948
10949     if(config && config.data){
10950         this.inlineData = config.data;
10951         delete config.data;
10952     }
10953
10954     Roo.apply(this, config);
10955     
10956     if(this.reader){ // reader passed
10957         this.reader = Roo.factory(this.reader, Roo.data);
10958         this.reader.xmodule = this.xmodule || false;
10959         if(!this.recordType){
10960             this.recordType = this.reader.recordType;
10961         }
10962         if(this.reader.onMetaChange){
10963             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10964         }
10965     }
10966
10967     if(this.recordType){
10968         this.fields = this.recordType.prototype.fields;
10969     }
10970     this.modified = [];
10971
10972     this.addEvents({
10973         /**
10974          * @event datachanged
10975          * Fires when the data cache has changed, and a widget which is using this Store
10976          * as a Record cache should refresh its view.
10977          * @param {Store} this
10978          */
10979         datachanged : true,
10980         /**
10981          * @event metachange
10982          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10983          * @param {Store} this
10984          * @param {Object} meta The JSON metadata
10985          */
10986         metachange : true,
10987         /**
10988          * @event add
10989          * Fires when Records have been added to the Store
10990          * @param {Store} this
10991          * @param {Roo.data.Record[]} records The array of Records added
10992          * @param {Number} index The index at which the record(s) were added
10993          */
10994         add : true,
10995         /**
10996          * @event remove
10997          * Fires when a Record has been removed from the Store
10998          * @param {Store} this
10999          * @param {Roo.data.Record} record The Record that was removed
11000          * @param {Number} index The index at which the record was removed
11001          */
11002         remove : true,
11003         /**
11004          * @event update
11005          * Fires when a Record has been updated
11006          * @param {Store} this
11007          * @param {Roo.data.Record} record The Record that was updated
11008          * @param {String} operation The update operation being performed.  Value may be one of:
11009          * <pre><code>
11010  Roo.data.Record.EDIT
11011  Roo.data.Record.REJECT
11012  Roo.data.Record.COMMIT
11013          * </code></pre>
11014          */
11015         update : true,
11016         /**
11017          * @event clear
11018          * Fires when the data cache has been cleared.
11019          * @param {Store} this
11020          */
11021         clear : true,
11022         /**
11023          * @event beforeload
11024          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11025          * the load action will be canceled.
11026          * @param {Store} this
11027          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11028          */
11029         beforeload : true,
11030         /**
11031          * @event beforeloadadd
11032          * Fires after a new set of Records has been loaded.
11033          * @param {Store} this
11034          * @param {Roo.data.Record[]} records The Records that were loaded
11035          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11036          */
11037         beforeloadadd : true,
11038         /**
11039          * @event load
11040          * Fires after a new set of Records has been loaded, before they are added to the store.
11041          * @param {Store} this
11042          * @param {Roo.data.Record[]} records The Records that were loaded
11043          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11044          * @params {Object} return from reader
11045          */
11046         load : true,
11047         /**
11048          * @event loadexception
11049          * Fires if an exception occurs in the Proxy during loading.
11050          * Called with the signature of the Proxy's "loadexception" event.
11051          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11052          * 
11053          * @param {Proxy} 
11054          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11055          * @param {Object} load options 
11056          * @param {Object} jsonData from your request (normally this contains the Exception)
11057          */
11058         loadexception : true
11059     });
11060     
11061     if(this.proxy){
11062         this.proxy = Roo.factory(this.proxy, Roo.data);
11063         this.proxy.xmodule = this.xmodule || false;
11064         this.relayEvents(this.proxy,  ["loadexception"]);
11065     }
11066     this.sortToggle = {};
11067     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11068
11069     Roo.data.Store.superclass.constructor.call(this);
11070
11071     if(this.inlineData){
11072         this.loadData(this.inlineData);
11073         delete this.inlineData;
11074     }
11075 };
11076
11077 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11078      /**
11079     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11080     * without a remote query - used by combo/forms at present.
11081     */
11082     
11083     /**
11084     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11085     */
11086     /**
11087     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11088     */
11089     /**
11090     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11091     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11092     */
11093     /**
11094     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11095     * on any HTTP request
11096     */
11097     /**
11098     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11099     */
11100     /**
11101     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11102     */
11103     multiSort: false,
11104     /**
11105     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11106     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11107     */
11108     remoteSort : false,
11109
11110     /**
11111     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11112      * loaded or when a record is removed. (defaults to false).
11113     */
11114     pruneModifiedRecords : false,
11115
11116     // private
11117     lastOptions : null,
11118
11119     /**
11120      * Add Records to the Store and fires the add event.
11121      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11122      */
11123     add : function(records){
11124         records = [].concat(records);
11125         for(var i = 0, len = records.length; i < len; i++){
11126             records[i].join(this);
11127         }
11128         var index = this.data.length;
11129         this.data.addAll(records);
11130         this.fireEvent("add", this, records, index);
11131     },
11132
11133     /**
11134      * Remove a Record from the Store and fires the remove event.
11135      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11136      */
11137     remove : function(record){
11138         var index = this.data.indexOf(record);
11139         this.data.removeAt(index);
11140  
11141         if(this.pruneModifiedRecords){
11142             this.modified.remove(record);
11143         }
11144         this.fireEvent("remove", this, record, index);
11145     },
11146
11147     /**
11148      * Remove all Records from the Store and fires the clear event.
11149      */
11150     removeAll : function(){
11151         this.data.clear();
11152         if(this.pruneModifiedRecords){
11153             this.modified = [];
11154         }
11155         this.fireEvent("clear", this);
11156     },
11157
11158     /**
11159      * Inserts Records to the Store at the given index and fires the add event.
11160      * @param {Number} index The start index at which to insert the passed Records.
11161      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11162      */
11163     insert : function(index, records){
11164         records = [].concat(records);
11165         for(var i = 0, len = records.length; i < len; i++){
11166             this.data.insert(index, records[i]);
11167             records[i].join(this);
11168         }
11169         this.fireEvent("add", this, records, index);
11170     },
11171
11172     /**
11173      * Get the index within the cache of the passed Record.
11174      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11175      * @return {Number} The index of the passed Record. Returns -1 if not found.
11176      */
11177     indexOf : function(record){
11178         return this.data.indexOf(record);
11179     },
11180
11181     /**
11182      * Get the index within the cache of the Record with the passed id.
11183      * @param {String} id The id of the Record to find.
11184      * @return {Number} The index of the Record. Returns -1 if not found.
11185      */
11186     indexOfId : function(id){
11187         return this.data.indexOfKey(id);
11188     },
11189
11190     /**
11191      * Get the Record with the specified id.
11192      * @param {String} id The id of the Record to find.
11193      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11194      */
11195     getById : function(id){
11196         return this.data.key(id);
11197     },
11198
11199     /**
11200      * Get the Record at the specified index.
11201      * @param {Number} index The index of the Record to find.
11202      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11203      */
11204     getAt : function(index){
11205         return this.data.itemAt(index);
11206     },
11207
11208     /**
11209      * Returns a range of Records between specified indices.
11210      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11211      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11212      * @return {Roo.data.Record[]} An array of Records
11213      */
11214     getRange : function(start, end){
11215         return this.data.getRange(start, end);
11216     },
11217
11218     // private
11219     storeOptions : function(o){
11220         o = Roo.apply({}, o);
11221         delete o.callback;
11222         delete o.scope;
11223         this.lastOptions = o;
11224     },
11225
11226     /**
11227      * Loads the Record cache from the configured Proxy using the configured Reader.
11228      * <p>
11229      * If using remote paging, then the first load call must specify the <em>start</em>
11230      * and <em>limit</em> properties in the options.params property to establish the initial
11231      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11232      * <p>
11233      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11234      * and this call will return before the new data has been loaded. Perform any post-processing
11235      * in a callback function, or in a "load" event handler.</strong>
11236      * <p>
11237      * @param {Object} options An object containing properties which control loading options:<ul>
11238      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11239      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11240      * passed the following arguments:<ul>
11241      * <li>r : Roo.data.Record[]</li>
11242      * <li>options: Options object from the load call</li>
11243      * <li>success: Boolean success indicator</li></ul></li>
11244      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11245      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11246      * </ul>
11247      */
11248     load : function(options){
11249         options = options || {};
11250         if(this.fireEvent("beforeload", this, options) !== false){
11251             this.storeOptions(options);
11252             var p = Roo.apply(options.params || {}, this.baseParams);
11253             // if meta was not loaded from remote source.. try requesting it.
11254             if (!this.reader.metaFromRemote) {
11255                 p._requestMeta = 1;
11256             }
11257             if(this.sortInfo && this.remoteSort){
11258                 var pn = this.paramNames;
11259                 p[pn["sort"]] = this.sortInfo.field;
11260                 p[pn["dir"]] = this.sortInfo.direction;
11261             }
11262             if (this.multiSort) {
11263                 var pn = this.paramNames;
11264                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11265             }
11266             
11267             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11268         }
11269     },
11270
11271     /**
11272      * Reloads the Record cache from the configured Proxy using the configured Reader and
11273      * the options from the last load operation performed.
11274      * @param {Object} options (optional) An object containing properties which may override the options
11275      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11276      * the most recently used options are reused).
11277      */
11278     reload : function(options){
11279         this.load(Roo.applyIf(options||{}, this.lastOptions));
11280     },
11281
11282     // private
11283     // Called as a callback by the Reader during a load operation.
11284     loadRecords : function(o, options, success){
11285         if(!o || success === false){
11286             if(success !== false){
11287                 this.fireEvent("load", this, [], options, o);
11288             }
11289             if(options.callback){
11290                 options.callback.call(options.scope || this, [], options, false);
11291             }
11292             return;
11293         }
11294         // if data returned failure - throw an exception.
11295         if (o.success === false) {
11296             // show a message if no listener is registered.
11297             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11298                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11299             }
11300             // loadmask wil be hooked into this..
11301             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11302             return;
11303         }
11304         var r = o.records, t = o.totalRecords || r.length;
11305         
11306         this.fireEvent("beforeloadadd", this, r, options, o);
11307         
11308         if(!options || options.add !== true){
11309             if(this.pruneModifiedRecords){
11310                 this.modified = [];
11311             }
11312             for(var i = 0, len = r.length; i < len; i++){
11313                 r[i].join(this);
11314             }
11315             if(this.snapshot){
11316                 this.data = this.snapshot;
11317                 delete this.snapshot;
11318             }
11319             this.data.clear();
11320             this.data.addAll(r);
11321             this.totalLength = t;
11322             this.applySort();
11323             this.fireEvent("datachanged", this);
11324         }else{
11325             this.totalLength = Math.max(t, this.data.length+r.length);
11326             this.add(r);
11327         }
11328         
11329         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11330                 
11331             var e = new Roo.data.Record({});
11332
11333             e.set(this.parent.displayField, this.parent.emptyTitle);
11334             e.set(this.parent.valueField, '');
11335
11336             this.insert(0, e);
11337         }
11338             
11339         this.fireEvent("load", this, r, options, o);
11340         if(options.callback){
11341             options.callback.call(options.scope || this, r, options, true);
11342         }
11343     },
11344
11345
11346     /**
11347      * Loads data from a passed data block. A Reader which understands the format of the data
11348      * must have been configured in the constructor.
11349      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11350      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11351      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11352      */
11353     loadData : function(o, append){
11354         var r = this.reader.readRecords(o);
11355         this.loadRecords(r, {add: append}, true);
11356     },
11357
11358     /**
11359      * Gets the number of cached records.
11360      * <p>
11361      * <em>If using paging, this may not be the total size of the dataset. If the data object
11362      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11363      * the data set size</em>
11364      */
11365     getCount : function(){
11366         return this.data.length || 0;
11367     },
11368
11369     /**
11370      * Gets the total number of records in the dataset as returned by the server.
11371      * <p>
11372      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11373      * the dataset size</em>
11374      */
11375     getTotalCount : function(){
11376         return this.totalLength || 0;
11377     },
11378
11379     /**
11380      * Returns the sort state of the Store as an object with two properties:
11381      * <pre><code>
11382  field {String} The name of the field by which the Records are sorted
11383  direction {String} The sort order, "ASC" or "DESC"
11384      * </code></pre>
11385      */
11386     getSortState : function(){
11387         return this.sortInfo;
11388     },
11389
11390     // private
11391     applySort : function(){
11392         if(this.sortInfo && !this.remoteSort){
11393             var s = this.sortInfo, f = s.field;
11394             var st = this.fields.get(f).sortType;
11395             var fn = function(r1, r2){
11396                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11397                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11398             };
11399             this.data.sort(s.direction, fn);
11400             if(this.snapshot && this.snapshot != this.data){
11401                 this.snapshot.sort(s.direction, fn);
11402             }
11403         }
11404     },
11405
11406     /**
11407      * Sets the default sort column and order to be used by the next load operation.
11408      * @param {String} fieldName The name of the field to sort by.
11409      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11410      */
11411     setDefaultSort : function(field, dir){
11412         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11413     },
11414
11415     /**
11416      * Sort the Records.
11417      * If remote sorting is used, the sort is performed on the server, and the cache is
11418      * reloaded. If local sorting is used, the cache is sorted internally.
11419      * @param {String} fieldName The name of the field to sort by.
11420      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11421      */
11422     sort : function(fieldName, dir){
11423         var f = this.fields.get(fieldName);
11424         if(!dir){
11425             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11426             
11427             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11428                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11429             }else{
11430                 dir = f.sortDir;
11431             }
11432         }
11433         this.sortToggle[f.name] = dir;
11434         this.sortInfo = {field: f.name, direction: dir};
11435         if(!this.remoteSort){
11436             this.applySort();
11437             this.fireEvent("datachanged", this);
11438         }else{
11439             this.load(this.lastOptions);
11440         }
11441     },
11442
11443     /**
11444      * Calls the specified function for each of the Records in the cache.
11445      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11446      * Returning <em>false</em> aborts and exits the iteration.
11447      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11448      */
11449     each : function(fn, scope){
11450         this.data.each(fn, scope);
11451     },
11452
11453     /**
11454      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11455      * (e.g., during paging).
11456      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11457      */
11458     getModifiedRecords : function(){
11459         return this.modified;
11460     },
11461
11462     // private
11463     createFilterFn : function(property, value, anyMatch){
11464         if(!value.exec){ // not a regex
11465             value = String(value);
11466             if(value.length == 0){
11467                 return false;
11468             }
11469             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11470         }
11471         return function(r){
11472             return value.test(r.data[property]);
11473         };
11474     },
11475
11476     /**
11477      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11478      * @param {String} property A field on your records
11479      * @param {Number} start The record index to start at (defaults to 0)
11480      * @param {Number} end The last record index to include (defaults to length - 1)
11481      * @return {Number} The sum
11482      */
11483     sum : function(property, start, end){
11484         var rs = this.data.items, v = 0;
11485         start = start || 0;
11486         end = (end || end === 0) ? end : rs.length-1;
11487
11488         for(var i = start; i <= end; i++){
11489             v += (rs[i].data[property] || 0);
11490         }
11491         return v;
11492     },
11493
11494     /**
11495      * Filter the records by a specified property.
11496      * @param {String} field A field on your records
11497      * @param {String/RegExp} value Either a string that the field
11498      * should start with or a RegExp to test against the field
11499      * @param {Boolean} anyMatch True to match any part not just the beginning
11500      */
11501     filter : function(property, value, anyMatch){
11502         var fn = this.createFilterFn(property, value, anyMatch);
11503         return fn ? this.filterBy(fn) : this.clearFilter();
11504     },
11505
11506     /**
11507      * Filter by a function. The specified function will be called with each
11508      * record in this data source. If the function returns true the record is included,
11509      * otherwise it is filtered.
11510      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11511      * @param {Object} scope (optional) The scope of the function (defaults to this)
11512      */
11513     filterBy : function(fn, scope){
11514         this.snapshot = this.snapshot || this.data;
11515         this.data = this.queryBy(fn, scope||this);
11516         this.fireEvent("datachanged", this);
11517     },
11518
11519     /**
11520      * Query the records by a specified property.
11521      * @param {String} field A field on your records
11522      * @param {String/RegExp} value Either a string that the field
11523      * should start with or a RegExp to test against the field
11524      * @param {Boolean} anyMatch True to match any part not just the beginning
11525      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11526      */
11527     query : function(property, value, anyMatch){
11528         var fn = this.createFilterFn(property, value, anyMatch);
11529         return fn ? this.queryBy(fn) : this.data.clone();
11530     },
11531
11532     /**
11533      * Query by a function. The specified function will be called with each
11534      * record in this data source. If the function returns true the record is included
11535      * in the results.
11536      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11537      * @param {Object} scope (optional) The scope of the function (defaults to this)
11538       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11539      **/
11540     queryBy : function(fn, scope){
11541         var data = this.snapshot || this.data;
11542         return data.filterBy(fn, scope||this);
11543     },
11544
11545     /**
11546      * Collects unique values for a particular dataIndex from this store.
11547      * @param {String} dataIndex The property to collect
11548      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11549      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11550      * @return {Array} An array of the unique values
11551      **/
11552     collect : function(dataIndex, allowNull, bypassFilter){
11553         var d = (bypassFilter === true && this.snapshot) ?
11554                 this.snapshot.items : this.data.items;
11555         var v, sv, r = [], l = {};
11556         for(var i = 0, len = d.length; i < len; i++){
11557             v = d[i].data[dataIndex];
11558             sv = String(v);
11559             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11560                 l[sv] = true;
11561                 r[r.length] = v;
11562             }
11563         }
11564         return r;
11565     },
11566
11567     /**
11568      * Revert to a view of the Record cache with no filtering applied.
11569      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11570      */
11571     clearFilter : function(suppressEvent){
11572         if(this.snapshot && this.snapshot != this.data){
11573             this.data = this.snapshot;
11574             delete this.snapshot;
11575             if(suppressEvent !== true){
11576                 this.fireEvent("datachanged", this);
11577             }
11578         }
11579     },
11580
11581     // private
11582     afterEdit : function(record){
11583         if(this.modified.indexOf(record) == -1){
11584             this.modified.push(record);
11585         }
11586         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11587     },
11588     
11589     // private
11590     afterReject : function(record){
11591         this.modified.remove(record);
11592         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11593     },
11594
11595     // private
11596     afterCommit : function(record){
11597         this.modified.remove(record);
11598         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11599     },
11600
11601     /**
11602      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11603      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11604      */
11605     commitChanges : function(){
11606         var m = this.modified.slice(0);
11607         this.modified = [];
11608         for(var i = 0, len = m.length; i < len; i++){
11609             m[i].commit();
11610         }
11611     },
11612
11613     /**
11614      * Cancel outstanding changes on all changed records.
11615      */
11616     rejectChanges : function(){
11617         var m = this.modified.slice(0);
11618         this.modified = [];
11619         for(var i = 0, len = m.length; i < len; i++){
11620             m[i].reject();
11621         }
11622     },
11623
11624     onMetaChange : function(meta, rtype, o){
11625         this.recordType = rtype;
11626         this.fields = rtype.prototype.fields;
11627         delete this.snapshot;
11628         this.sortInfo = meta.sortInfo || this.sortInfo;
11629         this.modified = [];
11630         this.fireEvent('metachange', this, this.reader.meta);
11631     },
11632     
11633     moveIndex : function(data, type)
11634     {
11635         var index = this.indexOf(data);
11636         
11637         var newIndex = index + type;
11638         
11639         this.remove(data);
11640         
11641         this.insert(newIndex, data);
11642         
11643     }
11644 });/*
11645  * Based on:
11646  * Ext JS Library 1.1.1
11647  * Copyright(c) 2006-2007, Ext JS, LLC.
11648  *
11649  * Originally Released Under LGPL - original licence link has changed is not relivant.
11650  *
11651  * Fork - LGPL
11652  * <script type="text/javascript">
11653  */
11654
11655 /**
11656  * @class Roo.data.SimpleStore
11657  * @extends Roo.data.Store
11658  * Small helper class to make creating Stores from Array data easier.
11659  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11660  * @cfg {Array} fields An array of field definition objects, or field name strings.
11661  * @cfg {Array} data The multi-dimensional array of data
11662  * @constructor
11663  * @param {Object} config
11664  */
11665 Roo.data.SimpleStore = function(config){
11666     Roo.data.SimpleStore.superclass.constructor.call(this, {
11667         isLocal : true,
11668         reader: new Roo.data.ArrayReader({
11669                 id: config.id
11670             },
11671             Roo.data.Record.create(config.fields)
11672         ),
11673         proxy : new Roo.data.MemoryProxy(config.data)
11674     });
11675     this.load();
11676 };
11677 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11678  * Based on:
11679  * Ext JS Library 1.1.1
11680  * Copyright(c) 2006-2007, Ext JS, LLC.
11681  *
11682  * Originally Released Under LGPL - original licence link has changed is not relivant.
11683  *
11684  * Fork - LGPL
11685  * <script type="text/javascript">
11686  */
11687
11688 /**
11689 /**
11690  * @extends Roo.data.Store
11691  * @class Roo.data.JsonStore
11692  * Small helper class to make creating Stores for JSON data easier. <br/>
11693 <pre><code>
11694 var store = new Roo.data.JsonStore({
11695     url: 'get-images.php',
11696     root: 'images',
11697     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11698 });
11699 </code></pre>
11700  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11701  * JsonReader and HttpProxy (unless inline data is provided).</b>
11702  * @cfg {Array} fields An array of field definition objects, or field name strings.
11703  * @constructor
11704  * @param {Object} config
11705  */
11706 Roo.data.JsonStore = function(c){
11707     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11708         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11709         reader: new Roo.data.JsonReader(c, c.fields)
11710     }));
11711 };
11712 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11713  * Based on:
11714  * Ext JS Library 1.1.1
11715  * Copyright(c) 2006-2007, Ext JS, LLC.
11716  *
11717  * Originally Released Under LGPL - original licence link has changed is not relivant.
11718  *
11719  * Fork - LGPL
11720  * <script type="text/javascript">
11721  */
11722
11723  
11724 Roo.data.Field = function(config){
11725     if(typeof config == "string"){
11726         config = {name: config};
11727     }
11728     Roo.apply(this, config);
11729     
11730     if(!this.type){
11731         this.type = "auto";
11732     }
11733     
11734     var st = Roo.data.SortTypes;
11735     // named sortTypes are supported, here we look them up
11736     if(typeof this.sortType == "string"){
11737         this.sortType = st[this.sortType];
11738     }
11739     
11740     // set default sortType for strings and dates
11741     if(!this.sortType){
11742         switch(this.type){
11743             case "string":
11744                 this.sortType = st.asUCString;
11745                 break;
11746             case "date":
11747                 this.sortType = st.asDate;
11748                 break;
11749             default:
11750                 this.sortType = st.none;
11751         }
11752     }
11753
11754     // define once
11755     var stripRe = /[\$,%]/g;
11756
11757     // prebuilt conversion function for this field, instead of
11758     // switching every time we're reading a value
11759     if(!this.convert){
11760         var cv, dateFormat = this.dateFormat;
11761         switch(this.type){
11762             case "":
11763             case "auto":
11764             case undefined:
11765                 cv = function(v){ return v; };
11766                 break;
11767             case "string":
11768                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11769                 break;
11770             case "int":
11771                 cv = function(v){
11772                     return v !== undefined && v !== null && v !== '' ?
11773                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11774                     };
11775                 break;
11776             case "float":
11777                 cv = function(v){
11778                     return v !== undefined && v !== null && v !== '' ?
11779                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11780                     };
11781                 break;
11782             case "bool":
11783             case "boolean":
11784                 cv = function(v){ return v === true || v === "true" || v == 1; };
11785                 break;
11786             case "date":
11787                 cv = function(v){
11788                     if(!v){
11789                         return '';
11790                     }
11791                     if(v instanceof Date){
11792                         return v;
11793                     }
11794                     if(dateFormat){
11795                         if(dateFormat == "timestamp"){
11796                             return new Date(v*1000);
11797                         }
11798                         return Date.parseDate(v, dateFormat);
11799                     }
11800                     var parsed = Date.parse(v);
11801                     return parsed ? new Date(parsed) : null;
11802                 };
11803              break;
11804             
11805         }
11806         this.convert = cv;
11807     }
11808 };
11809
11810 Roo.data.Field.prototype = {
11811     dateFormat: null,
11812     defaultValue: "",
11813     mapping: null,
11814     sortType : null,
11815     sortDir : "ASC"
11816 };/*
11817  * Based on:
11818  * Ext JS Library 1.1.1
11819  * Copyright(c) 2006-2007, Ext JS, LLC.
11820  *
11821  * Originally Released Under LGPL - original licence link has changed is not relivant.
11822  *
11823  * Fork - LGPL
11824  * <script type="text/javascript">
11825  */
11826  
11827 // Base class for reading structured data from a data source.  This class is intended to be
11828 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11829
11830 /**
11831  * @class Roo.data.DataReader
11832  * Base class for reading structured data from a data source.  This class is intended to be
11833  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11834  */
11835
11836 Roo.data.DataReader = function(meta, recordType){
11837     
11838     this.meta = meta;
11839     
11840     this.recordType = recordType instanceof Array ? 
11841         Roo.data.Record.create(recordType) : recordType;
11842 };
11843
11844 Roo.data.DataReader.prototype = {
11845      /**
11846      * Create an empty record
11847      * @param {Object} data (optional) - overlay some values
11848      * @return {Roo.data.Record} record created.
11849      */
11850     newRow :  function(d) {
11851         var da =  {};
11852         this.recordType.prototype.fields.each(function(c) {
11853             switch( c.type) {
11854                 case 'int' : da[c.name] = 0; break;
11855                 case 'date' : da[c.name] = new Date(); break;
11856                 case 'float' : da[c.name] = 0.0; break;
11857                 case 'boolean' : da[c.name] = false; break;
11858                 default : da[c.name] = ""; break;
11859             }
11860             
11861         });
11862         return new this.recordType(Roo.apply(da, d));
11863     }
11864     
11865 };/*
11866  * Based on:
11867  * Ext JS Library 1.1.1
11868  * Copyright(c) 2006-2007, Ext JS, LLC.
11869  *
11870  * Originally Released Under LGPL - original licence link has changed is not relivant.
11871  *
11872  * Fork - LGPL
11873  * <script type="text/javascript">
11874  */
11875
11876 /**
11877  * @class Roo.data.DataProxy
11878  * @extends Roo.data.Observable
11879  * This class is an abstract base class for implementations which provide retrieval of
11880  * unformatted data objects.<br>
11881  * <p>
11882  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11883  * (of the appropriate type which knows how to parse the data object) to provide a block of
11884  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11885  * <p>
11886  * Custom implementations must implement the load method as described in
11887  * {@link Roo.data.HttpProxy#load}.
11888  */
11889 Roo.data.DataProxy = function(){
11890     this.addEvents({
11891         /**
11892          * @event beforeload
11893          * Fires before a network request is made to retrieve a data object.
11894          * @param {Object} This DataProxy object.
11895          * @param {Object} params The params parameter to the load function.
11896          */
11897         beforeload : true,
11898         /**
11899          * @event load
11900          * Fires before the load method's callback is called.
11901          * @param {Object} This DataProxy object.
11902          * @param {Object} o The data object.
11903          * @param {Object} arg The callback argument object passed to the load function.
11904          */
11905         load : true,
11906         /**
11907          * @event loadexception
11908          * Fires if an Exception occurs during data retrieval.
11909          * @param {Object} This DataProxy object.
11910          * @param {Object} o The data object.
11911          * @param {Object} arg The callback argument object passed to the load function.
11912          * @param {Object} e The Exception.
11913          */
11914         loadexception : true
11915     });
11916     Roo.data.DataProxy.superclass.constructor.call(this);
11917 };
11918
11919 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11920
11921     /**
11922      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11923      */
11924 /*
11925  * Based on:
11926  * Ext JS Library 1.1.1
11927  * Copyright(c) 2006-2007, Ext JS, LLC.
11928  *
11929  * Originally Released Under LGPL - original licence link has changed is not relivant.
11930  *
11931  * Fork - LGPL
11932  * <script type="text/javascript">
11933  */
11934 /**
11935  * @class Roo.data.MemoryProxy
11936  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11937  * to the Reader when its load method is called.
11938  * @constructor
11939  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11940  */
11941 Roo.data.MemoryProxy = function(data){
11942     if (data.data) {
11943         data = data.data;
11944     }
11945     Roo.data.MemoryProxy.superclass.constructor.call(this);
11946     this.data = data;
11947 };
11948
11949 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11950     
11951     /**
11952      * Load data from the requested source (in this case an in-memory
11953      * data object passed to the constructor), read the data object into
11954      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11955      * process that block using the passed callback.
11956      * @param {Object} params This parameter is not used by the MemoryProxy class.
11957      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11958      * object into a block of Roo.data.Records.
11959      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11960      * The function must be passed <ul>
11961      * <li>The Record block object</li>
11962      * <li>The "arg" argument from the load function</li>
11963      * <li>A boolean success indicator</li>
11964      * </ul>
11965      * @param {Object} scope The scope in which to call the callback
11966      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11967      */
11968     load : function(params, reader, callback, scope, arg){
11969         params = params || {};
11970         var result;
11971         try {
11972             result = reader.readRecords(this.data);
11973         }catch(e){
11974             this.fireEvent("loadexception", this, arg, null, e);
11975             callback.call(scope, null, arg, false);
11976             return;
11977         }
11978         callback.call(scope, result, arg, true);
11979     },
11980     
11981     // private
11982     update : function(params, records){
11983         
11984     }
11985 });/*
11986  * Based on:
11987  * Ext JS Library 1.1.1
11988  * Copyright(c) 2006-2007, Ext JS, LLC.
11989  *
11990  * Originally Released Under LGPL - original licence link has changed is not relivant.
11991  *
11992  * Fork - LGPL
11993  * <script type="text/javascript">
11994  */
11995 /**
11996  * @class Roo.data.HttpProxy
11997  * @extends Roo.data.DataProxy
11998  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11999  * configured to reference a certain URL.<br><br>
12000  * <p>
12001  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12002  * from which the running page was served.<br><br>
12003  * <p>
12004  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12005  * <p>
12006  * Be aware that to enable the browser to parse an XML document, the server must set
12007  * the Content-Type header in the HTTP response to "text/xml".
12008  * @constructor
12009  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12010  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12011  * will be used to make the request.
12012  */
12013 Roo.data.HttpProxy = function(conn){
12014     Roo.data.HttpProxy.superclass.constructor.call(this);
12015     // is conn a conn config or a real conn?
12016     this.conn = conn;
12017     this.useAjax = !conn || !conn.events;
12018   
12019 };
12020
12021 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12022     // thse are take from connection...
12023     
12024     /**
12025      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12026      */
12027     /**
12028      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12029      * extra parameters to each request made by this object. (defaults to undefined)
12030      */
12031     /**
12032      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12033      *  to each request made by this object. (defaults to undefined)
12034      */
12035     /**
12036      * @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)
12037      */
12038     /**
12039      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12040      */
12041      /**
12042      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12043      * @type Boolean
12044      */
12045   
12046
12047     /**
12048      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12049      * @type Boolean
12050      */
12051     /**
12052      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12053      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12054      * a finer-grained basis than the DataProxy events.
12055      */
12056     getConnection : function(){
12057         return this.useAjax ? Roo.Ajax : this.conn;
12058     },
12059
12060     /**
12061      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12062      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12063      * process that block using the passed callback.
12064      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12065      * for the request to the remote server.
12066      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12067      * object into a block of Roo.data.Records.
12068      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12069      * The function must be passed <ul>
12070      * <li>The Record block object</li>
12071      * <li>The "arg" argument from the load function</li>
12072      * <li>A boolean success indicator</li>
12073      * </ul>
12074      * @param {Object} scope The scope in which to call the callback
12075      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12076      */
12077     load : function(params, reader, callback, scope, arg){
12078         if(this.fireEvent("beforeload", this, params) !== false){
12079             var  o = {
12080                 params : params || {},
12081                 request: {
12082                     callback : callback,
12083                     scope : scope,
12084                     arg : arg
12085                 },
12086                 reader: reader,
12087                 callback : this.loadResponse,
12088                 scope: this
12089             };
12090             if(this.useAjax){
12091                 Roo.applyIf(o, this.conn);
12092                 if(this.activeRequest){
12093                     Roo.Ajax.abort(this.activeRequest);
12094                 }
12095                 this.activeRequest = Roo.Ajax.request(o);
12096             }else{
12097                 this.conn.request(o);
12098             }
12099         }else{
12100             callback.call(scope||this, null, arg, false);
12101         }
12102     },
12103
12104     // private
12105     loadResponse : function(o, success, response){
12106         delete this.activeRequest;
12107         if(!success){
12108             this.fireEvent("loadexception", this, o, response);
12109             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12110             return;
12111         }
12112         var result;
12113         try {
12114             result = o.reader.read(response);
12115         }catch(e){
12116             this.fireEvent("loadexception", this, o, response, e);
12117             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12118             return;
12119         }
12120         
12121         this.fireEvent("load", this, o, o.request.arg);
12122         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12123     },
12124
12125     // private
12126     update : function(dataSet){
12127
12128     },
12129
12130     // private
12131     updateResponse : function(dataSet){
12132
12133     }
12134 });/*
12135  * Based on:
12136  * Ext JS Library 1.1.1
12137  * Copyright(c) 2006-2007, Ext JS, LLC.
12138  *
12139  * Originally Released Under LGPL - original licence link has changed is not relivant.
12140  *
12141  * Fork - LGPL
12142  * <script type="text/javascript">
12143  */
12144
12145 /**
12146  * @class Roo.data.ScriptTagProxy
12147  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12148  * other than the originating domain of the running page.<br><br>
12149  * <p>
12150  * <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
12151  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12152  * <p>
12153  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12154  * source code that is used as the source inside a &lt;script> tag.<br><br>
12155  * <p>
12156  * In order for the browser to process the returned data, the server must wrap the data object
12157  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12158  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12159  * depending on whether the callback name was passed:
12160  * <p>
12161  * <pre><code>
12162 boolean scriptTag = false;
12163 String cb = request.getParameter("callback");
12164 if (cb != null) {
12165     scriptTag = true;
12166     response.setContentType("text/javascript");
12167 } else {
12168     response.setContentType("application/x-json");
12169 }
12170 Writer out = response.getWriter();
12171 if (scriptTag) {
12172     out.write(cb + "(");
12173 }
12174 out.print(dataBlock.toJsonString());
12175 if (scriptTag) {
12176     out.write(");");
12177 }
12178 </pre></code>
12179  *
12180  * @constructor
12181  * @param {Object} config A configuration object.
12182  */
12183 Roo.data.ScriptTagProxy = function(config){
12184     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12185     Roo.apply(this, config);
12186     this.head = document.getElementsByTagName("head")[0];
12187 };
12188
12189 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12190
12191 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12192     /**
12193      * @cfg {String} url The URL from which to request the data object.
12194      */
12195     /**
12196      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12197      */
12198     timeout : 30000,
12199     /**
12200      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12201      * the server the name of the callback function set up by the load call to process the returned data object.
12202      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12203      * javascript output which calls this named function passing the data object as its only parameter.
12204      */
12205     callbackParam : "callback",
12206     /**
12207      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12208      * name to the request.
12209      */
12210     nocache : true,
12211
12212     /**
12213      * Load data from the configured URL, read the data object into
12214      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12215      * process that block using the passed callback.
12216      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12217      * for the request to the remote server.
12218      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12219      * object into a block of Roo.data.Records.
12220      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12221      * The function must be passed <ul>
12222      * <li>The Record block object</li>
12223      * <li>The "arg" argument from the load function</li>
12224      * <li>A boolean success indicator</li>
12225      * </ul>
12226      * @param {Object} scope The scope in which to call the callback
12227      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12228      */
12229     load : function(params, reader, callback, scope, arg){
12230         if(this.fireEvent("beforeload", this, params) !== false){
12231
12232             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12233
12234             var url = this.url;
12235             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12236             if(this.nocache){
12237                 url += "&_dc=" + (new Date().getTime());
12238             }
12239             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12240             var trans = {
12241                 id : transId,
12242                 cb : "stcCallback"+transId,
12243                 scriptId : "stcScript"+transId,
12244                 params : params,
12245                 arg : arg,
12246                 url : url,
12247                 callback : callback,
12248                 scope : scope,
12249                 reader : reader
12250             };
12251             var conn = this;
12252
12253             window[trans.cb] = function(o){
12254                 conn.handleResponse(o, trans);
12255             };
12256
12257             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12258
12259             if(this.autoAbort !== false){
12260                 this.abort();
12261             }
12262
12263             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12264
12265             var script = document.createElement("script");
12266             script.setAttribute("src", url);
12267             script.setAttribute("type", "text/javascript");
12268             script.setAttribute("id", trans.scriptId);
12269             this.head.appendChild(script);
12270
12271             this.trans = trans;
12272         }else{
12273             callback.call(scope||this, null, arg, false);
12274         }
12275     },
12276
12277     // private
12278     isLoading : function(){
12279         return this.trans ? true : false;
12280     },
12281
12282     /**
12283      * Abort the current server request.
12284      */
12285     abort : function(){
12286         if(this.isLoading()){
12287             this.destroyTrans(this.trans);
12288         }
12289     },
12290
12291     // private
12292     destroyTrans : function(trans, isLoaded){
12293         this.head.removeChild(document.getElementById(trans.scriptId));
12294         clearTimeout(trans.timeoutId);
12295         if(isLoaded){
12296             window[trans.cb] = undefined;
12297             try{
12298                 delete window[trans.cb];
12299             }catch(e){}
12300         }else{
12301             // if hasn't been loaded, wait for load to remove it to prevent script error
12302             window[trans.cb] = function(){
12303                 window[trans.cb] = undefined;
12304                 try{
12305                     delete window[trans.cb];
12306                 }catch(e){}
12307             };
12308         }
12309     },
12310
12311     // private
12312     handleResponse : function(o, trans){
12313         this.trans = false;
12314         this.destroyTrans(trans, true);
12315         var result;
12316         try {
12317             result = trans.reader.readRecords(o);
12318         }catch(e){
12319             this.fireEvent("loadexception", this, o, trans.arg, e);
12320             trans.callback.call(trans.scope||window, null, trans.arg, false);
12321             return;
12322         }
12323         this.fireEvent("load", this, o, trans.arg);
12324         trans.callback.call(trans.scope||window, result, trans.arg, true);
12325     },
12326
12327     // private
12328     handleFailure : function(trans){
12329         this.trans = false;
12330         this.destroyTrans(trans, false);
12331         this.fireEvent("loadexception", this, null, trans.arg);
12332         trans.callback.call(trans.scope||window, null, trans.arg, false);
12333     }
12334 });/*
12335  * Based on:
12336  * Ext JS Library 1.1.1
12337  * Copyright(c) 2006-2007, Ext JS, LLC.
12338  *
12339  * Originally Released Under LGPL - original licence link has changed is not relivant.
12340  *
12341  * Fork - LGPL
12342  * <script type="text/javascript">
12343  */
12344
12345 /**
12346  * @class Roo.data.JsonReader
12347  * @extends Roo.data.DataReader
12348  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12349  * based on mappings in a provided Roo.data.Record constructor.
12350  * 
12351  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12352  * in the reply previously. 
12353  * 
12354  * <p>
12355  * Example code:
12356  * <pre><code>
12357 var RecordDef = Roo.data.Record.create([
12358     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12359     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12360 ]);
12361 var myReader = new Roo.data.JsonReader({
12362     totalProperty: "results",    // The property which contains the total dataset size (optional)
12363     root: "rows",                // The property which contains an Array of row objects
12364     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12365 }, RecordDef);
12366 </code></pre>
12367  * <p>
12368  * This would consume a JSON file like this:
12369  * <pre><code>
12370 { 'results': 2, 'rows': [
12371     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12372     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12373 }
12374 </code></pre>
12375  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12376  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12377  * paged from the remote server.
12378  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12379  * @cfg {String} root name of the property which contains the Array of row objects.
12380  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12381  * @cfg {Array} fields Array of field definition objects
12382  * @constructor
12383  * Create a new JsonReader
12384  * @param {Object} meta Metadata configuration options
12385  * @param {Object} recordType Either an Array of field definition objects,
12386  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12387  */
12388 Roo.data.JsonReader = function(meta, recordType){
12389     
12390     meta = meta || {};
12391     // set some defaults:
12392     Roo.applyIf(meta, {
12393         totalProperty: 'total',
12394         successProperty : 'success',
12395         root : 'data',
12396         id : 'id'
12397     });
12398     
12399     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12400 };
12401 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12402     
12403     /**
12404      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12405      * Used by Store query builder to append _requestMeta to params.
12406      * 
12407      */
12408     metaFromRemote : false,
12409     /**
12410      * This method is only used by a DataProxy which has retrieved data from a remote server.
12411      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12412      * @return {Object} data A data block which is used by an Roo.data.Store object as
12413      * a cache of Roo.data.Records.
12414      */
12415     read : function(response){
12416         var json = response.responseText;
12417        
12418         var o = /* eval:var:o */ eval("("+json+")");
12419         if(!o) {
12420             throw {message: "JsonReader.read: Json object not found"};
12421         }
12422         
12423         if(o.metaData){
12424             
12425             delete this.ef;
12426             this.metaFromRemote = true;
12427             this.meta = o.metaData;
12428             this.recordType = Roo.data.Record.create(o.metaData.fields);
12429             this.onMetaChange(this.meta, this.recordType, o);
12430         }
12431         return this.readRecords(o);
12432     },
12433
12434     // private function a store will implement
12435     onMetaChange : function(meta, recordType, o){
12436
12437     },
12438
12439     /**
12440          * @ignore
12441          */
12442     simpleAccess: function(obj, subsc) {
12443         return obj[subsc];
12444     },
12445
12446         /**
12447          * @ignore
12448          */
12449     getJsonAccessor: function(){
12450         var re = /[\[\.]/;
12451         return function(expr) {
12452             try {
12453                 return(re.test(expr))
12454                     ? new Function("obj", "return obj." + expr)
12455                     : function(obj){
12456                         return obj[expr];
12457                     };
12458             } catch(e){}
12459             return Roo.emptyFn;
12460         };
12461     }(),
12462
12463     /**
12464      * Create a data block containing Roo.data.Records from an XML document.
12465      * @param {Object} o An object which contains an Array of row objects in the property specified
12466      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12467      * which contains the total size of the dataset.
12468      * @return {Object} data A data block which is used by an Roo.data.Store object as
12469      * a cache of Roo.data.Records.
12470      */
12471     readRecords : function(o){
12472         /**
12473          * After any data loads, the raw JSON data is available for further custom processing.
12474          * @type Object
12475          */
12476         this.o = o;
12477         var s = this.meta, Record = this.recordType,
12478             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12479
12480 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12481         if (!this.ef) {
12482             if(s.totalProperty) {
12483                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12484                 }
12485                 if(s.successProperty) {
12486                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12487                 }
12488                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12489                 if (s.id) {
12490                         var g = this.getJsonAccessor(s.id);
12491                         this.getId = function(rec) {
12492                                 var r = g(rec);  
12493                                 return (r === undefined || r === "") ? null : r;
12494                         };
12495                 } else {
12496                         this.getId = function(){return null;};
12497                 }
12498             this.ef = [];
12499             for(var jj = 0; jj < fl; jj++){
12500                 f = fi[jj];
12501                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12502                 this.ef[jj] = this.getJsonAccessor(map);
12503             }
12504         }
12505
12506         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12507         if(s.totalProperty){
12508             var vt = parseInt(this.getTotal(o), 10);
12509             if(!isNaN(vt)){
12510                 totalRecords = vt;
12511             }
12512         }
12513         if(s.successProperty){
12514             var vs = this.getSuccess(o);
12515             if(vs === false || vs === 'false'){
12516                 success = false;
12517             }
12518         }
12519         var records = [];
12520         for(var i = 0; i < c; i++){
12521                 var n = root[i];
12522             var values = {};
12523             var id = this.getId(n);
12524             for(var j = 0; j < fl; j++){
12525                 f = fi[j];
12526             var v = this.ef[j](n);
12527             if (!f.convert) {
12528                 Roo.log('missing convert for ' + f.name);
12529                 Roo.log(f);
12530                 continue;
12531             }
12532             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12533             }
12534             var record = new Record(values, id);
12535             record.json = n;
12536             records[i] = record;
12537         }
12538         return {
12539             raw : o,
12540             success : success,
12541             records : records,
12542             totalRecords : totalRecords
12543         };
12544     }
12545 });/*
12546  * Based on:
12547  * Ext JS Library 1.1.1
12548  * Copyright(c) 2006-2007, Ext JS, LLC.
12549  *
12550  * Originally Released Under LGPL - original licence link has changed is not relivant.
12551  *
12552  * Fork - LGPL
12553  * <script type="text/javascript">
12554  */
12555
12556 /**
12557  * @class Roo.data.ArrayReader
12558  * @extends Roo.data.DataReader
12559  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12560  * Each element of that Array represents a row of data fields. The
12561  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12562  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12563  * <p>
12564  * Example code:.
12565  * <pre><code>
12566 var RecordDef = Roo.data.Record.create([
12567     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12568     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12569 ]);
12570 var myReader = new Roo.data.ArrayReader({
12571     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12572 }, RecordDef);
12573 </code></pre>
12574  * <p>
12575  * This would consume an Array like this:
12576  * <pre><code>
12577 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12578   </code></pre>
12579  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12580  * @constructor
12581  * Create a new JsonReader
12582  * @param {Object} meta Metadata configuration options.
12583  * @param {Object} recordType Either an Array of field definition objects
12584  * as specified to {@link Roo.data.Record#create},
12585  * or an {@link Roo.data.Record} object
12586  * created using {@link Roo.data.Record#create}.
12587  */
12588 Roo.data.ArrayReader = function(meta, recordType){
12589     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12590 };
12591
12592 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12593     /**
12594      * Create a data block containing Roo.data.Records from an XML document.
12595      * @param {Object} o An Array of row objects which represents the dataset.
12596      * @return {Object} data A data block which is used by an Roo.data.Store object as
12597      * a cache of Roo.data.Records.
12598      */
12599     readRecords : function(o){
12600         var sid = this.meta ? this.meta.id : null;
12601         var recordType = this.recordType, fields = recordType.prototype.fields;
12602         var records = [];
12603         var root = o;
12604             for(var i = 0; i < root.length; i++){
12605                     var n = root[i];
12606                 var values = {};
12607                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12608                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12609                 var f = fields.items[j];
12610                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12611                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12612                 v = f.convert(v);
12613                 values[f.name] = v;
12614             }
12615                 var record = new recordType(values, id);
12616                 record.json = n;
12617                 records[records.length] = record;
12618             }
12619             return {
12620                 records : records,
12621                 totalRecords : records.length
12622             };
12623     }
12624 });/*
12625  * - LGPL
12626  * * 
12627  */
12628
12629 /**
12630  * @class Roo.bootstrap.ComboBox
12631  * @extends Roo.bootstrap.TriggerField
12632  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12633  * @cfg {Boolean} append (true|false) default false
12634  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12635  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12636  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12637  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12638  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12639  * @cfg {Boolean} animate default true
12640  * @cfg {Boolean} emptyResultText only for touch device
12641  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12642  * @cfg {String} emptyTitle default ''
12643  * @constructor
12644  * Create a new ComboBox.
12645  * @param {Object} config Configuration options
12646  */
12647 Roo.bootstrap.ComboBox = function(config){
12648     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12649     this.addEvents({
12650         /**
12651          * @event expand
12652          * Fires when the dropdown list is expanded
12653         * @param {Roo.bootstrap.ComboBox} combo This combo box
12654         */
12655         'expand' : true,
12656         /**
12657          * @event collapse
12658          * Fires when the dropdown list is collapsed
12659         * @param {Roo.bootstrap.ComboBox} combo This combo box
12660         */
12661         'collapse' : true,
12662         /**
12663          * @event beforeselect
12664          * Fires before a list item is selected. Return false to cancel the selection.
12665         * @param {Roo.bootstrap.ComboBox} combo This combo box
12666         * @param {Roo.data.Record} record The data record returned from the underlying store
12667         * @param {Number} index The index of the selected item in the dropdown list
12668         */
12669         'beforeselect' : true,
12670         /**
12671          * @event select
12672          * Fires when a list item is selected
12673         * @param {Roo.bootstrap.ComboBox} combo This combo box
12674         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12675         * @param {Number} index The index of the selected item in the dropdown list
12676         */
12677         'select' : true,
12678         /**
12679          * @event beforequery
12680          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12681          * The event object passed has these properties:
12682         * @param {Roo.bootstrap.ComboBox} combo This combo box
12683         * @param {String} query The query
12684         * @param {Boolean} forceAll true to force "all" query
12685         * @param {Boolean} cancel true to cancel the query
12686         * @param {Object} e The query event object
12687         */
12688         'beforequery': true,
12689          /**
12690          * @event add
12691          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12692         * @param {Roo.bootstrap.ComboBox} combo This combo box
12693         */
12694         'add' : true,
12695         /**
12696          * @event edit
12697          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12698         * @param {Roo.bootstrap.ComboBox} combo This combo box
12699         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12700         */
12701         'edit' : true,
12702         /**
12703          * @event remove
12704          * Fires when the remove value from the combobox array
12705         * @param {Roo.bootstrap.ComboBox} combo This combo box
12706         */
12707         'remove' : true,
12708         /**
12709          * @event afterremove
12710          * Fires when the remove value from the combobox array
12711         * @param {Roo.bootstrap.ComboBox} combo This combo box
12712         */
12713         'afterremove' : true,
12714         /**
12715          * @event specialfilter
12716          * Fires when specialfilter
12717             * @param {Roo.bootstrap.ComboBox} combo This combo box
12718             */
12719         'specialfilter' : true,
12720         /**
12721          * @event tick
12722          * Fires when tick the element
12723             * @param {Roo.bootstrap.ComboBox} combo This combo box
12724             */
12725         'tick' : true,
12726         /**
12727          * @event touchviewdisplay
12728          * Fires when touch view require special display (default is using displayField)
12729             * @param {Roo.bootstrap.ComboBox} combo This combo box
12730             * @param {Object} cfg set html .
12731             */
12732         'touchviewdisplay' : true
12733         
12734     });
12735     
12736     this.item = [];
12737     this.tickItems = [];
12738     
12739     this.selectedIndex = -1;
12740     if(this.mode == 'local'){
12741         if(config.queryDelay === undefined){
12742             this.queryDelay = 10;
12743         }
12744         if(config.minChars === undefined){
12745             this.minChars = 0;
12746         }
12747     }
12748 };
12749
12750 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12751      
12752     /**
12753      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12754      * rendering into an Roo.Editor, defaults to false)
12755      */
12756     /**
12757      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12758      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12759      */
12760     /**
12761      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12762      */
12763     /**
12764      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12765      * the dropdown list (defaults to undefined, with no header element)
12766      */
12767
12768      /**
12769      * @cfg {String/Roo.Template} tpl The template to use to render the output
12770      */
12771      
12772      /**
12773      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12774      */
12775     listWidth: undefined,
12776     /**
12777      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12778      * mode = 'remote' or 'text' if mode = 'local')
12779      */
12780     displayField: undefined,
12781     
12782     /**
12783      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12784      * mode = 'remote' or 'value' if mode = 'local'). 
12785      * Note: use of a valueField requires the user make a selection
12786      * in order for a value to be mapped.
12787      */
12788     valueField: undefined,
12789     /**
12790      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12791      */
12792     modalTitle : '',
12793     
12794     /**
12795      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12796      * field's data value (defaults to the underlying DOM element's name)
12797      */
12798     hiddenName: undefined,
12799     /**
12800      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12801      */
12802     listClass: '',
12803     /**
12804      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12805      */
12806     selectedClass: 'active',
12807     
12808     /**
12809      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12810      */
12811     shadow:'sides',
12812     /**
12813      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12814      * anchor positions (defaults to 'tl-bl')
12815      */
12816     listAlign: 'tl-bl?',
12817     /**
12818      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12819      */
12820     maxHeight: 300,
12821     /**
12822      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12823      * query specified by the allQuery config option (defaults to 'query')
12824      */
12825     triggerAction: 'query',
12826     /**
12827      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12828      * (defaults to 4, does not apply if editable = false)
12829      */
12830     minChars : 4,
12831     /**
12832      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12833      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12834      */
12835     typeAhead: false,
12836     /**
12837      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12838      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12839      */
12840     queryDelay: 500,
12841     /**
12842      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12843      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12844      */
12845     pageSize: 0,
12846     /**
12847      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12848      * when editable = true (defaults to false)
12849      */
12850     selectOnFocus:false,
12851     /**
12852      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12853      */
12854     queryParam: 'query',
12855     /**
12856      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12857      * when mode = 'remote' (defaults to 'Loading...')
12858      */
12859     loadingText: 'Loading...',
12860     /**
12861      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12862      */
12863     resizable: false,
12864     /**
12865      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12866      */
12867     handleHeight : 8,
12868     /**
12869      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12870      * traditional select (defaults to true)
12871      */
12872     editable: true,
12873     /**
12874      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12875      */
12876     allQuery: '',
12877     /**
12878      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12879      */
12880     mode: 'remote',
12881     /**
12882      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12883      * listWidth has a higher value)
12884      */
12885     minListWidth : 70,
12886     /**
12887      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12888      * allow the user to set arbitrary text into the field (defaults to false)
12889      */
12890     forceSelection:false,
12891     /**
12892      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12893      * if typeAhead = true (defaults to 250)
12894      */
12895     typeAheadDelay : 250,
12896     /**
12897      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12898      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12899      */
12900     valueNotFoundText : undefined,
12901     /**
12902      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12903      */
12904     blockFocus : false,
12905     
12906     /**
12907      * @cfg {Boolean} disableClear Disable showing of clear button.
12908      */
12909     disableClear : false,
12910     /**
12911      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12912      */
12913     alwaysQuery : false,
12914     
12915     /**
12916      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12917      */
12918     multiple : false,
12919     
12920     /**
12921      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12922      */
12923     invalidClass : "has-warning",
12924     
12925     /**
12926      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12927      */
12928     validClass : "has-success",
12929     
12930     /**
12931      * @cfg {Boolean} specialFilter (true|false) special filter default false
12932      */
12933     specialFilter : false,
12934     
12935     /**
12936      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12937      */
12938     mobileTouchView : true,
12939     
12940     /**
12941      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12942      */
12943     useNativeIOS : false,
12944     
12945     ios_options : false,
12946     
12947     //private
12948     addicon : false,
12949     editicon: false,
12950     
12951     page: 0,
12952     hasQuery: false,
12953     append: false,
12954     loadNext: false,
12955     autoFocus : true,
12956     tickable : false,
12957     btnPosition : 'right',
12958     triggerList : true,
12959     showToggleBtn : true,
12960     animate : true,
12961     emptyResultText: 'Empty',
12962     triggerText : 'Select',
12963     emptyTitle : '',
12964     
12965     // element that contains real text value.. (when hidden is used..)
12966     
12967     getAutoCreate : function()
12968     {   
12969         var cfg = false;
12970         //render
12971         /*
12972          * Render classic select for iso
12973          */
12974         
12975         if(Roo.isIOS && this.useNativeIOS){
12976             cfg = this.getAutoCreateNativeIOS();
12977             return cfg;
12978         }
12979         
12980         /*
12981          * Touch Devices
12982          */
12983         
12984         if(Roo.isTouch && this.mobileTouchView){
12985             cfg = this.getAutoCreateTouchView();
12986             return cfg;;
12987         }
12988         
12989         /*
12990          *  Normal ComboBox
12991          */
12992         if(!this.tickable){
12993             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12994             return cfg;
12995         }
12996         
12997         /*
12998          *  ComboBox with tickable selections
12999          */
13000              
13001         var align = this.labelAlign || this.parentLabelAlign();
13002         
13003         cfg = {
13004             cls : 'form-group roo-combobox-tickable' //input-group
13005         };
13006         
13007         var btn_text_select = '';
13008         var btn_text_done = '';
13009         var btn_text_cancel = '';
13010         
13011         if (this.btn_text_show) {
13012             btn_text_select = 'Select';
13013             btn_text_done = 'Done';
13014             btn_text_cancel = 'Cancel'; 
13015         }
13016         
13017         var buttons = {
13018             tag : 'div',
13019             cls : 'tickable-buttons',
13020             cn : [
13021                 {
13022                     tag : 'button',
13023                     type : 'button',
13024                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13025                     //html : this.triggerText
13026                     html: btn_text_select
13027                 },
13028                 {
13029                     tag : 'button',
13030                     type : 'button',
13031                     name : 'ok',
13032                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13033                     //html : 'Done'
13034                     html: btn_text_done
13035                 },
13036                 {
13037                     tag : 'button',
13038                     type : 'button',
13039                     name : 'cancel',
13040                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13041                     //html : 'Cancel'
13042                     html: btn_text_cancel
13043                 }
13044             ]
13045         };
13046         
13047         if(this.editable){
13048             buttons.cn.unshift({
13049                 tag: 'input',
13050                 cls: 'roo-select2-search-field-input'
13051             });
13052         }
13053         
13054         var _this = this;
13055         
13056         Roo.each(buttons.cn, function(c){
13057             if (_this.size) {
13058                 c.cls += ' btn-' + _this.size;
13059             }
13060
13061             if (_this.disabled) {
13062                 c.disabled = true;
13063             }
13064         });
13065         
13066         var box = {
13067             tag: 'div',
13068             cn: [
13069                 {
13070                     tag: 'input',
13071                     type : 'hidden',
13072                     cls: 'form-hidden-field'
13073                 },
13074                 {
13075                     tag: 'ul',
13076                     cls: 'roo-select2-choices',
13077                     cn:[
13078                         {
13079                             tag: 'li',
13080                             cls: 'roo-select2-search-field',
13081                             cn: [
13082                                 buttons
13083                             ]
13084                         }
13085                     ]
13086                 }
13087             ]
13088         };
13089         
13090         var combobox = {
13091             cls: 'roo-select2-container input-group roo-select2-container-multi',
13092             cn: [
13093                 box
13094 //                {
13095 //                    tag: 'ul',
13096 //                    cls: 'typeahead typeahead-long dropdown-menu',
13097 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13098 //                }
13099             ]
13100         };
13101         
13102         if(this.hasFeedback && !this.allowBlank){
13103             
13104             var feedback = {
13105                 tag: 'span',
13106                 cls: 'glyphicon form-control-feedback'
13107             };
13108
13109             combobox.cn.push(feedback);
13110         }
13111         
13112         
13113         if (align ==='left' && this.fieldLabel.length) {
13114             
13115             cfg.cls += ' roo-form-group-label-left';
13116             
13117             cfg.cn = [
13118                 {
13119                     tag : 'i',
13120                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13121                     tooltip : 'This field is required'
13122                 },
13123                 {
13124                     tag: 'label',
13125                     'for' :  id,
13126                     cls : 'control-label',
13127                     html : this.fieldLabel
13128
13129                 },
13130                 {
13131                     cls : "", 
13132                     cn: [
13133                         combobox
13134                     ]
13135                 }
13136
13137             ];
13138             
13139             var labelCfg = cfg.cn[1];
13140             var contentCfg = cfg.cn[2];
13141             
13142
13143             if(this.indicatorpos == 'right'){
13144                 
13145                 cfg.cn = [
13146                     {
13147                         tag: 'label',
13148                         'for' :  id,
13149                         cls : 'control-label',
13150                         cn : [
13151                             {
13152                                 tag : 'span',
13153                                 html : this.fieldLabel
13154                             },
13155                             {
13156                                 tag : 'i',
13157                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13158                                 tooltip : 'This field is required'
13159                             }
13160                         ]
13161                     },
13162                     {
13163                         cls : "",
13164                         cn: [
13165                             combobox
13166                         ]
13167                     }
13168
13169                 ];
13170                 
13171                 
13172                 
13173                 labelCfg = cfg.cn[0];
13174                 contentCfg = cfg.cn[1];
13175             
13176             }
13177             
13178             if(this.labelWidth > 12){
13179                 labelCfg.style = "width: " + this.labelWidth + 'px';
13180             }
13181             
13182             if(this.labelWidth < 13 && this.labelmd == 0){
13183                 this.labelmd = this.labelWidth;
13184             }
13185             
13186             if(this.labellg > 0){
13187                 labelCfg.cls += ' col-lg-' + this.labellg;
13188                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13189             }
13190             
13191             if(this.labelmd > 0){
13192                 labelCfg.cls += ' col-md-' + this.labelmd;
13193                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13194             }
13195             
13196             if(this.labelsm > 0){
13197                 labelCfg.cls += ' col-sm-' + this.labelsm;
13198                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13199             }
13200             
13201             if(this.labelxs > 0){
13202                 labelCfg.cls += ' col-xs-' + this.labelxs;
13203                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13204             }
13205                 
13206                 
13207         } else if ( this.fieldLabel.length) {
13208 //                Roo.log(" label");
13209                  cfg.cn = [
13210                     {
13211                         tag : 'i',
13212                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13213                         tooltip : 'This field is required'
13214                     },
13215                     {
13216                         tag: 'label',
13217                         //cls : 'input-group-addon',
13218                         html : this.fieldLabel
13219                     },
13220                     combobox
13221                 ];
13222                 
13223                 if(this.indicatorpos == 'right'){
13224                     cfg.cn = [
13225                         {
13226                             tag: 'label',
13227                             //cls : 'input-group-addon',
13228                             html : this.fieldLabel
13229                         },
13230                         {
13231                             tag : 'i',
13232                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13233                             tooltip : 'This field is required'
13234                         },
13235                         combobox
13236                     ];
13237                     
13238                 }
13239
13240         } else {
13241             
13242 //                Roo.log(" no label && no align");
13243                 cfg = combobox
13244                      
13245                 
13246         }
13247          
13248         var settings=this;
13249         ['xs','sm','md','lg'].map(function(size){
13250             if (settings[size]) {
13251                 cfg.cls += ' col-' + size + '-' + settings[size];
13252             }
13253         });
13254         
13255         return cfg;
13256         
13257     },
13258     
13259     _initEventsCalled : false,
13260     
13261     // private
13262     initEvents: function()
13263     {   
13264         if (this._initEventsCalled) { // as we call render... prevent looping...
13265             return;
13266         }
13267         this._initEventsCalled = true;
13268         
13269         if (!this.store) {
13270             throw "can not find store for combo";
13271         }
13272         
13273         this.indicator = this.indicatorEl();
13274         
13275         this.store = Roo.factory(this.store, Roo.data);
13276         this.store.parent = this;
13277         
13278         // if we are building from html. then this element is so complex, that we can not really
13279         // use the rendered HTML.
13280         // so we have to trash and replace the previous code.
13281         if (Roo.XComponent.build_from_html) {
13282             // remove this element....
13283             var e = this.el.dom, k=0;
13284             while (e ) { e = e.previousSibling;  ++k;}
13285
13286             this.el.remove();
13287             
13288             this.el=false;
13289             this.rendered = false;
13290             
13291             this.render(this.parent().getChildContainer(true), k);
13292         }
13293         
13294         if(Roo.isIOS && this.useNativeIOS){
13295             this.initIOSView();
13296             return;
13297         }
13298         
13299         /*
13300          * Touch Devices
13301          */
13302         
13303         if(Roo.isTouch && this.mobileTouchView){
13304             this.initTouchView();
13305             return;
13306         }
13307         
13308         if(this.tickable){
13309             this.initTickableEvents();
13310             return;
13311         }
13312         
13313         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13314         
13315         if(this.hiddenName){
13316             
13317             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13318             
13319             this.hiddenField.dom.value =
13320                 this.hiddenValue !== undefined ? this.hiddenValue :
13321                 this.value !== undefined ? this.value : '';
13322
13323             // prevent input submission
13324             this.el.dom.removeAttribute('name');
13325             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13326              
13327              
13328         }
13329         //if(Roo.isGecko){
13330         //    this.el.dom.setAttribute('autocomplete', 'off');
13331         //}
13332         
13333         var cls = 'x-combo-list';
13334         
13335         //this.list = new Roo.Layer({
13336         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13337         //});
13338         
13339         var _this = this;
13340         
13341         (function(){
13342             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13343             _this.list.setWidth(lw);
13344         }).defer(100);
13345         
13346         this.list.on('mouseover', this.onViewOver, this);
13347         this.list.on('mousemove', this.onViewMove, this);
13348         this.list.on('scroll', this.onViewScroll, this);
13349         
13350         /*
13351         this.list.swallowEvent('mousewheel');
13352         this.assetHeight = 0;
13353
13354         if(this.title){
13355             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13356             this.assetHeight += this.header.getHeight();
13357         }
13358
13359         this.innerList = this.list.createChild({cls:cls+'-inner'});
13360         this.innerList.on('mouseover', this.onViewOver, this);
13361         this.innerList.on('mousemove', this.onViewMove, this);
13362         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13363         
13364         if(this.allowBlank && !this.pageSize && !this.disableClear){
13365             this.footer = this.list.createChild({cls:cls+'-ft'});
13366             this.pageTb = new Roo.Toolbar(this.footer);
13367            
13368         }
13369         if(this.pageSize){
13370             this.footer = this.list.createChild({cls:cls+'-ft'});
13371             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13372                     {pageSize: this.pageSize});
13373             
13374         }
13375         
13376         if (this.pageTb && this.allowBlank && !this.disableClear) {
13377             var _this = this;
13378             this.pageTb.add(new Roo.Toolbar.Fill(), {
13379                 cls: 'x-btn-icon x-btn-clear',
13380                 text: '&#160;',
13381                 handler: function()
13382                 {
13383                     _this.collapse();
13384                     _this.clearValue();
13385                     _this.onSelect(false, -1);
13386                 }
13387             });
13388         }
13389         if (this.footer) {
13390             this.assetHeight += this.footer.getHeight();
13391         }
13392         */
13393             
13394         if(!this.tpl){
13395             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13396         }
13397
13398         this.view = new Roo.View(this.list, this.tpl, {
13399             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13400         });
13401         //this.view.wrapEl.setDisplayed(false);
13402         this.view.on('click', this.onViewClick, this);
13403         
13404         
13405         this.store.on('beforeload', this.onBeforeLoad, this);
13406         this.store.on('load', this.onLoad, this);
13407         this.store.on('loadexception', this.onLoadException, this);
13408         /*
13409         if(this.resizable){
13410             this.resizer = new Roo.Resizable(this.list,  {
13411                pinned:true, handles:'se'
13412             });
13413             this.resizer.on('resize', function(r, w, h){
13414                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13415                 this.listWidth = w;
13416                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13417                 this.restrictHeight();
13418             }, this);
13419             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13420         }
13421         */
13422         if(!this.editable){
13423             this.editable = true;
13424             this.setEditable(false);
13425         }
13426         
13427         /*
13428         
13429         if (typeof(this.events.add.listeners) != 'undefined') {
13430             
13431             this.addicon = this.wrap.createChild(
13432                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13433        
13434             this.addicon.on('click', function(e) {
13435                 this.fireEvent('add', this);
13436             }, this);
13437         }
13438         if (typeof(this.events.edit.listeners) != 'undefined') {
13439             
13440             this.editicon = this.wrap.createChild(
13441                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13442             if (this.addicon) {
13443                 this.editicon.setStyle('margin-left', '40px');
13444             }
13445             this.editicon.on('click', function(e) {
13446                 
13447                 // we fire even  if inothing is selected..
13448                 this.fireEvent('edit', this, this.lastData );
13449                 
13450             }, this);
13451         }
13452         */
13453         
13454         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13455             "up" : function(e){
13456                 this.inKeyMode = true;
13457                 this.selectPrev();
13458             },
13459
13460             "down" : function(e){
13461                 if(!this.isExpanded()){
13462                     this.onTriggerClick();
13463                 }else{
13464                     this.inKeyMode = true;
13465                     this.selectNext();
13466                 }
13467             },
13468
13469             "enter" : function(e){
13470 //                this.onViewClick();
13471                 //return true;
13472                 this.collapse();
13473                 
13474                 if(this.fireEvent("specialkey", this, e)){
13475                     this.onViewClick(false);
13476                 }
13477                 
13478                 return true;
13479             },
13480
13481             "esc" : function(e){
13482                 this.collapse();
13483             },
13484
13485             "tab" : function(e){
13486                 this.collapse();
13487                 
13488                 if(this.fireEvent("specialkey", this, e)){
13489                     this.onViewClick(false);
13490                 }
13491                 
13492                 return true;
13493             },
13494
13495             scope : this,
13496
13497             doRelay : function(foo, bar, hname){
13498                 if(hname == 'down' || this.scope.isExpanded()){
13499                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13500                 }
13501                 return true;
13502             },
13503
13504             forceKeyDown: true
13505         });
13506         
13507         
13508         this.queryDelay = Math.max(this.queryDelay || 10,
13509                 this.mode == 'local' ? 10 : 250);
13510         
13511         
13512         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13513         
13514         if(this.typeAhead){
13515             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13516         }
13517         if(this.editable !== false){
13518             this.inputEl().on("keyup", this.onKeyUp, this);
13519         }
13520         if(this.forceSelection){
13521             this.inputEl().on('blur', this.doForce, this);
13522         }
13523         
13524         if(this.multiple){
13525             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13526             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13527         }
13528     },
13529     
13530     initTickableEvents: function()
13531     {   
13532         this.createList();
13533         
13534         if(this.hiddenName){
13535             
13536             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13537             
13538             this.hiddenField.dom.value =
13539                 this.hiddenValue !== undefined ? this.hiddenValue :
13540                 this.value !== undefined ? this.value : '';
13541
13542             // prevent input submission
13543             this.el.dom.removeAttribute('name');
13544             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13545              
13546              
13547         }
13548         
13549 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13550         
13551         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13552         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13553         if(this.triggerList){
13554             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13555         }
13556          
13557         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13558         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13559         
13560         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13561         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13562         
13563         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13564         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13565         
13566         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13567         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13568         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13569         
13570         this.okBtn.hide();
13571         this.cancelBtn.hide();
13572         
13573         var _this = this;
13574         
13575         (function(){
13576             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13577             _this.list.setWidth(lw);
13578         }).defer(100);
13579         
13580         this.list.on('mouseover', this.onViewOver, this);
13581         this.list.on('mousemove', this.onViewMove, this);
13582         
13583         this.list.on('scroll', this.onViewScroll, this);
13584         
13585         if(!this.tpl){
13586             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>';
13587         }
13588
13589         this.view = new Roo.View(this.list, this.tpl, {
13590             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13591         });
13592         
13593         //this.view.wrapEl.setDisplayed(false);
13594         this.view.on('click', this.onViewClick, this);
13595         
13596         
13597         
13598         this.store.on('beforeload', this.onBeforeLoad, this);
13599         this.store.on('load', this.onLoad, this);
13600         this.store.on('loadexception', this.onLoadException, this);
13601         
13602         if(this.editable){
13603             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13604                 "up" : function(e){
13605                     this.inKeyMode = true;
13606                     this.selectPrev();
13607                 },
13608
13609                 "down" : function(e){
13610                     this.inKeyMode = true;
13611                     this.selectNext();
13612                 },
13613
13614                 "enter" : function(e){
13615                     if(this.fireEvent("specialkey", this, e)){
13616                         this.onViewClick(false);
13617                     }
13618                     
13619                     return true;
13620                 },
13621
13622                 "esc" : function(e){
13623                     this.onTickableFooterButtonClick(e, false, false);
13624                 },
13625
13626                 "tab" : function(e){
13627                     this.fireEvent("specialkey", this, e);
13628                     
13629                     this.onTickableFooterButtonClick(e, false, false);
13630                     
13631                     return true;
13632                 },
13633
13634                 scope : this,
13635
13636                 doRelay : function(e, fn, key){
13637                     if(this.scope.isExpanded()){
13638                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13639                     }
13640                     return true;
13641                 },
13642
13643                 forceKeyDown: true
13644             });
13645         }
13646         
13647         this.queryDelay = Math.max(this.queryDelay || 10,
13648                 this.mode == 'local' ? 10 : 250);
13649         
13650         
13651         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13652         
13653         if(this.typeAhead){
13654             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13655         }
13656         
13657         if(this.editable !== false){
13658             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13659         }
13660         
13661         this.indicator = this.indicatorEl();
13662         
13663         if(this.indicator){
13664             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13665             this.indicator.hide();
13666         }
13667         
13668     },
13669
13670     onDestroy : function(){
13671         if(this.view){
13672             this.view.setStore(null);
13673             this.view.el.removeAllListeners();
13674             this.view.el.remove();
13675             this.view.purgeListeners();
13676         }
13677         if(this.list){
13678             this.list.dom.innerHTML  = '';
13679         }
13680         
13681         if(this.store){
13682             this.store.un('beforeload', this.onBeforeLoad, this);
13683             this.store.un('load', this.onLoad, this);
13684             this.store.un('loadexception', this.onLoadException, this);
13685         }
13686         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13687     },
13688
13689     // private
13690     fireKey : function(e){
13691         if(e.isNavKeyPress() && !this.list.isVisible()){
13692             this.fireEvent("specialkey", this, e);
13693         }
13694     },
13695
13696     // private
13697     onResize: function(w, h){
13698 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13699 //        
13700 //        if(typeof w != 'number'){
13701 //            // we do not handle it!?!?
13702 //            return;
13703 //        }
13704 //        var tw = this.trigger.getWidth();
13705 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13706 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13707 //        var x = w - tw;
13708 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13709 //            
13710 //        //this.trigger.setStyle('left', x+'px');
13711 //        
13712 //        if(this.list && this.listWidth === undefined){
13713 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13714 //            this.list.setWidth(lw);
13715 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13716 //        }
13717         
13718     
13719         
13720     },
13721
13722     /**
13723      * Allow or prevent the user from directly editing the field text.  If false is passed,
13724      * the user will only be able to select from the items defined in the dropdown list.  This method
13725      * is the runtime equivalent of setting the 'editable' config option at config time.
13726      * @param {Boolean} value True to allow the user to directly edit the field text
13727      */
13728     setEditable : function(value){
13729         if(value == this.editable){
13730             return;
13731         }
13732         this.editable = value;
13733         if(!value){
13734             this.inputEl().dom.setAttribute('readOnly', true);
13735             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13736             this.inputEl().addClass('x-combo-noedit');
13737         }else{
13738             this.inputEl().dom.setAttribute('readOnly', false);
13739             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13740             this.inputEl().removeClass('x-combo-noedit');
13741         }
13742     },
13743
13744     // private
13745     
13746     onBeforeLoad : function(combo,opts){
13747         if(!this.hasFocus){
13748             return;
13749         }
13750          if (!opts.add) {
13751             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13752          }
13753         this.restrictHeight();
13754         this.selectedIndex = -1;
13755     },
13756
13757     // private
13758     onLoad : function(){
13759         
13760         this.hasQuery = false;
13761         
13762         if(!this.hasFocus){
13763             return;
13764         }
13765         
13766         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13767             this.loading.hide();
13768         }
13769         
13770         if(this.store.getCount() > 0){
13771             
13772             this.expand();
13773             this.restrictHeight();
13774             if(this.lastQuery == this.allQuery){
13775                 if(this.editable && !this.tickable){
13776                     this.inputEl().dom.select();
13777                 }
13778                 
13779                 if(
13780                     !this.selectByValue(this.value, true) &&
13781                     this.autoFocus && 
13782                     (
13783                         !this.store.lastOptions ||
13784                         typeof(this.store.lastOptions.add) == 'undefined' || 
13785                         this.store.lastOptions.add != true
13786                     )
13787                 ){
13788                     this.select(0, true);
13789                 }
13790             }else{
13791                 if(this.autoFocus){
13792                     this.selectNext();
13793                 }
13794                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13795                     this.taTask.delay(this.typeAheadDelay);
13796                 }
13797             }
13798         }else{
13799             this.onEmptyResults();
13800         }
13801         
13802         //this.el.focus();
13803     },
13804     // private
13805     onLoadException : function()
13806     {
13807         this.hasQuery = false;
13808         
13809         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13810             this.loading.hide();
13811         }
13812         
13813         if(this.tickable && this.editable){
13814             return;
13815         }
13816         
13817         this.collapse();
13818         // only causes errors at present
13819         //Roo.log(this.store.reader.jsonData);
13820         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13821             // fixme
13822             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13823         //}
13824         
13825         
13826     },
13827     // private
13828     onTypeAhead : function(){
13829         if(this.store.getCount() > 0){
13830             var r = this.store.getAt(0);
13831             var newValue = r.data[this.displayField];
13832             var len = newValue.length;
13833             var selStart = this.getRawValue().length;
13834             
13835             if(selStart != len){
13836                 this.setRawValue(newValue);
13837                 this.selectText(selStart, newValue.length);
13838             }
13839         }
13840     },
13841
13842     // private
13843     onSelect : function(record, index){
13844         
13845         if(this.fireEvent('beforeselect', this, record, index) !== false){
13846         
13847             this.setFromData(index > -1 ? record.data : false);
13848             
13849             this.collapse();
13850             this.fireEvent('select', this, record, index);
13851         }
13852     },
13853
13854     /**
13855      * Returns the currently selected field value or empty string if no value is set.
13856      * @return {String} value The selected value
13857      */
13858     getValue : function()
13859     {
13860         if(Roo.isIOS && this.useNativeIOS){
13861             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13862         }
13863         
13864         if(this.multiple){
13865             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13866         }
13867         
13868         if(this.valueField){
13869             return typeof this.value != 'undefined' ? this.value : '';
13870         }else{
13871             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13872         }
13873     },
13874     
13875     getRawValue : function()
13876     {
13877         if(Roo.isIOS && this.useNativeIOS){
13878             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13879         }
13880         
13881         var v = this.inputEl().getValue();
13882         
13883         return v;
13884     },
13885
13886     /**
13887      * Clears any text/value currently set in the field
13888      */
13889     clearValue : function(){
13890         
13891         if(this.hiddenField){
13892             this.hiddenField.dom.value = '';
13893         }
13894         this.value = '';
13895         this.setRawValue('');
13896         this.lastSelectionText = '';
13897         this.lastData = false;
13898         
13899         var close = this.closeTriggerEl();
13900         
13901         if(close){
13902             close.hide();
13903         }
13904         
13905         this.validate();
13906         
13907     },
13908
13909     /**
13910      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13911      * will be displayed in the field.  If the value does not match the data value of an existing item,
13912      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13913      * Otherwise the field will be blank (although the value will still be set).
13914      * @param {String} value The value to match
13915      */
13916     setValue : function(v)
13917     {
13918         if(Roo.isIOS && this.useNativeIOS){
13919             this.setIOSValue(v);
13920             return;
13921         }
13922         
13923         if(this.multiple){
13924             this.syncValue();
13925             return;
13926         }
13927         
13928         var text = v;
13929         if(this.valueField){
13930             var r = this.findRecord(this.valueField, v);
13931             if(r){
13932                 text = r.data[this.displayField];
13933             }else if(this.valueNotFoundText !== undefined){
13934                 text = this.valueNotFoundText;
13935             }
13936         }
13937         this.lastSelectionText = text;
13938         if(this.hiddenField){
13939             this.hiddenField.dom.value = v;
13940         }
13941         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13942         this.value = v;
13943         
13944         var close = this.closeTriggerEl();
13945         
13946         if(close){
13947             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13948         }
13949         
13950         this.validate();
13951     },
13952     /**
13953      * @property {Object} the last set data for the element
13954      */
13955     
13956     lastData : false,
13957     /**
13958      * Sets the value of the field based on a object which is related to the record format for the store.
13959      * @param {Object} value the value to set as. or false on reset?
13960      */
13961     setFromData : function(o){
13962         
13963         if(this.multiple){
13964             this.addItem(o);
13965             return;
13966         }
13967             
13968         var dv = ''; // display value
13969         var vv = ''; // value value..
13970         this.lastData = o;
13971         if (this.displayField) {
13972             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13973         } else {
13974             // this is an error condition!!!
13975             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13976         }
13977         
13978         if(this.valueField){
13979             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13980         }
13981         
13982         var close = this.closeTriggerEl();
13983         
13984         if(close){
13985             if(dv.length || vv * 1 > 0){
13986                 close.show() ;
13987                 this.blockFocus=true;
13988             } else {
13989                 close.hide();
13990             }             
13991         }
13992         
13993         if(this.hiddenField){
13994             this.hiddenField.dom.value = vv;
13995             
13996             this.lastSelectionText = dv;
13997             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13998             this.value = vv;
13999             return;
14000         }
14001         // no hidden field.. - we store the value in 'value', but still display
14002         // display field!!!!
14003         this.lastSelectionText = dv;
14004         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14005         this.value = vv;
14006         
14007         
14008         
14009     },
14010     // private
14011     reset : function(){
14012         // overridden so that last data is reset..
14013         
14014         if(this.multiple){
14015             this.clearItem();
14016             return;
14017         }
14018         
14019         this.setValue(this.originalValue);
14020         //this.clearInvalid();
14021         this.lastData = false;
14022         if (this.view) {
14023             this.view.clearSelections();
14024         }
14025         
14026         this.validate();
14027     },
14028     // private
14029     findRecord : function(prop, value){
14030         var record;
14031         if(this.store.getCount() > 0){
14032             this.store.each(function(r){
14033                 if(r.data[prop] == value){
14034                     record = r;
14035                     return false;
14036                 }
14037                 return true;
14038             });
14039         }
14040         return record;
14041     },
14042     
14043     getName: function()
14044     {
14045         // returns hidden if it's set..
14046         if (!this.rendered) {return ''};
14047         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14048         
14049     },
14050     // private
14051     onViewMove : function(e, t){
14052         this.inKeyMode = false;
14053     },
14054
14055     // private
14056     onViewOver : function(e, t){
14057         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14058             return;
14059         }
14060         var item = this.view.findItemFromChild(t);
14061         
14062         if(item){
14063             var index = this.view.indexOf(item);
14064             this.select(index, false);
14065         }
14066     },
14067
14068     // private
14069     onViewClick : function(view, doFocus, el, e)
14070     {
14071         var index = this.view.getSelectedIndexes()[0];
14072         
14073         var r = this.store.getAt(index);
14074         
14075         if(this.tickable){
14076             
14077             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14078                 return;
14079             }
14080             
14081             var rm = false;
14082             var _this = this;
14083             
14084             Roo.each(this.tickItems, function(v,k){
14085                 
14086                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14087                     Roo.log(v);
14088                     _this.tickItems.splice(k, 1);
14089                     
14090                     if(typeof(e) == 'undefined' && view == false){
14091                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14092                     }
14093                     
14094                     rm = true;
14095                     return;
14096                 }
14097             });
14098             
14099             if(rm){
14100                 return;
14101             }
14102             
14103             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14104                 this.tickItems.push(r.data);
14105             }
14106             
14107             if(typeof(e) == 'undefined' && view == false){
14108                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14109             }
14110                     
14111             return;
14112         }
14113         
14114         if(r){
14115             this.onSelect(r, index);
14116         }
14117         if(doFocus !== false && !this.blockFocus){
14118             this.inputEl().focus();
14119         }
14120     },
14121
14122     // private
14123     restrictHeight : function(){
14124         //this.innerList.dom.style.height = '';
14125         //var inner = this.innerList.dom;
14126         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14127         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14128         //this.list.beginUpdate();
14129         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14130         this.list.alignTo(this.inputEl(), this.listAlign);
14131         this.list.alignTo(this.inputEl(), this.listAlign);
14132         //this.list.endUpdate();
14133     },
14134
14135     // private
14136     onEmptyResults : function(){
14137         
14138         if(this.tickable && this.editable){
14139             this.hasFocus = false;
14140             this.restrictHeight();
14141             return;
14142         }
14143         
14144         this.collapse();
14145     },
14146
14147     /**
14148      * Returns true if the dropdown list is expanded, else false.
14149      */
14150     isExpanded : function(){
14151         return this.list.isVisible();
14152     },
14153
14154     /**
14155      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14156      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14157      * @param {String} value The data value of the item to select
14158      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14159      * selected item if it is not currently in view (defaults to true)
14160      * @return {Boolean} True if the value matched an item in the list, else false
14161      */
14162     selectByValue : function(v, scrollIntoView){
14163         if(v !== undefined && v !== null){
14164             var r = this.findRecord(this.valueField || this.displayField, v);
14165             if(r){
14166                 this.select(this.store.indexOf(r), scrollIntoView);
14167                 return true;
14168             }
14169         }
14170         return false;
14171     },
14172
14173     /**
14174      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14175      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14176      * @param {Number} index The zero-based index of the list item to select
14177      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14178      * selected item if it is not currently in view (defaults to true)
14179      */
14180     select : function(index, scrollIntoView){
14181         this.selectedIndex = index;
14182         this.view.select(index);
14183         if(scrollIntoView !== false){
14184             var el = this.view.getNode(index);
14185             /*
14186              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14187              */
14188             if(el){
14189                 this.list.scrollChildIntoView(el, false);
14190             }
14191         }
14192     },
14193
14194     // private
14195     selectNext : function(){
14196         var ct = this.store.getCount();
14197         if(ct > 0){
14198             if(this.selectedIndex == -1){
14199                 this.select(0);
14200             }else if(this.selectedIndex < ct-1){
14201                 this.select(this.selectedIndex+1);
14202             }
14203         }
14204     },
14205
14206     // private
14207     selectPrev : function(){
14208         var ct = this.store.getCount();
14209         if(ct > 0){
14210             if(this.selectedIndex == -1){
14211                 this.select(0);
14212             }else if(this.selectedIndex != 0){
14213                 this.select(this.selectedIndex-1);
14214             }
14215         }
14216     },
14217
14218     // private
14219     onKeyUp : function(e){
14220         if(this.editable !== false && !e.isSpecialKey()){
14221             this.lastKey = e.getKey();
14222             this.dqTask.delay(this.queryDelay);
14223         }
14224     },
14225
14226     // private
14227     validateBlur : function(){
14228         return !this.list || !this.list.isVisible();   
14229     },
14230
14231     // private
14232     initQuery : function(){
14233         
14234         var v = this.getRawValue();
14235         
14236         if(this.tickable && this.editable){
14237             v = this.tickableInputEl().getValue();
14238         }
14239         
14240         this.doQuery(v);
14241     },
14242
14243     // private
14244     doForce : function(){
14245         if(this.inputEl().dom.value.length > 0){
14246             this.inputEl().dom.value =
14247                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14248              
14249         }
14250     },
14251
14252     /**
14253      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14254      * query allowing the query action to be canceled if needed.
14255      * @param {String} query The SQL query to execute
14256      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14257      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14258      * saved in the current store (defaults to false)
14259      */
14260     doQuery : function(q, forceAll){
14261         
14262         if(q === undefined || q === null){
14263             q = '';
14264         }
14265         var qe = {
14266             query: q,
14267             forceAll: forceAll,
14268             combo: this,
14269             cancel:false
14270         };
14271         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14272             return false;
14273         }
14274         q = qe.query;
14275         
14276         forceAll = qe.forceAll;
14277         if(forceAll === true || (q.length >= this.minChars)){
14278             
14279             this.hasQuery = true;
14280             
14281             if(this.lastQuery != q || this.alwaysQuery){
14282                 this.lastQuery = q;
14283                 if(this.mode == 'local'){
14284                     this.selectedIndex = -1;
14285                     if(forceAll){
14286                         this.store.clearFilter();
14287                     }else{
14288                         
14289                         if(this.specialFilter){
14290                             this.fireEvent('specialfilter', this);
14291                             this.onLoad();
14292                             return;
14293                         }
14294                         
14295                         this.store.filter(this.displayField, q);
14296                     }
14297                     
14298                     this.store.fireEvent("datachanged", this.store);
14299                     
14300                     this.onLoad();
14301                     
14302                     
14303                 }else{
14304                     
14305                     this.store.baseParams[this.queryParam] = q;
14306                     
14307                     var options = {params : this.getParams(q)};
14308                     
14309                     if(this.loadNext){
14310                         options.add = true;
14311                         options.params.start = this.page * this.pageSize;
14312                     }
14313                     
14314                     this.store.load(options);
14315                     
14316                     /*
14317                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14318                      *  we should expand the list on onLoad
14319                      *  so command out it
14320                      */
14321 //                    this.expand();
14322                 }
14323             }else{
14324                 this.selectedIndex = -1;
14325                 this.onLoad();   
14326             }
14327         }
14328         
14329         this.loadNext = false;
14330     },
14331     
14332     // private
14333     getParams : function(q){
14334         var p = {};
14335         //p[this.queryParam] = q;
14336         
14337         if(this.pageSize){
14338             p.start = 0;
14339             p.limit = this.pageSize;
14340         }
14341         return p;
14342     },
14343
14344     /**
14345      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14346      */
14347     collapse : function(){
14348         if(!this.isExpanded()){
14349             return;
14350         }
14351         
14352         this.list.hide();
14353         
14354         this.hasFocus = false;
14355         
14356         if(this.tickable){
14357             this.okBtn.hide();
14358             this.cancelBtn.hide();
14359             this.trigger.show();
14360             
14361             if(this.editable){
14362                 this.tickableInputEl().dom.value = '';
14363                 this.tickableInputEl().blur();
14364             }
14365             
14366         }
14367         
14368         Roo.get(document).un('mousedown', this.collapseIf, this);
14369         Roo.get(document).un('mousewheel', this.collapseIf, this);
14370         if (!this.editable) {
14371             Roo.get(document).un('keydown', this.listKeyPress, this);
14372         }
14373         this.fireEvent('collapse', this);
14374         
14375         this.validate();
14376     },
14377
14378     // private
14379     collapseIf : function(e){
14380         var in_combo  = e.within(this.el);
14381         var in_list =  e.within(this.list);
14382         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14383         
14384         if (in_combo || in_list || is_list) {
14385             //e.stopPropagation();
14386             return;
14387         }
14388         
14389         if(this.tickable){
14390             this.onTickableFooterButtonClick(e, false, false);
14391         }
14392
14393         this.collapse();
14394         
14395     },
14396
14397     /**
14398      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14399      */
14400     expand : function(){
14401        
14402         if(this.isExpanded() || !this.hasFocus){
14403             return;
14404         }
14405         
14406         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14407         this.list.setWidth(lw);
14408         
14409         Roo.log('expand');
14410         
14411         this.list.show();
14412         
14413         this.restrictHeight();
14414         
14415         if(this.tickable){
14416             
14417             this.tickItems = Roo.apply([], this.item);
14418             
14419             this.okBtn.show();
14420             this.cancelBtn.show();
14421             this.trigger.hide();
14422             
14423             if(this.editable){
14424                 this.tickableInputEl().focus();
14425             }
14426             
14427         }
14428         
14429         Roo.get(document).on('mousedown', this.collapseIf, this);
14430         Roo.get(document).on('mousewheel', this.collapseIf, this);
14431         if (!this.editable) {
14432             Roo.get(document).on('keydown', this.listKeyPress, this);
14433         }
14434         
14435         this.fireEvent('expand', this);
14436     },
14437
14438     // private
14439     // Implements the default empty TriggerField.onTriggerClick function
14440     onTriggerClick : function(e)
14441     {
14442         Roo.log('trigger click');
14443         
14444         if(this.disabled || !this.triggerList){
14445             return;
14446         }
14447         
14448         this.page = 0;
14449         this.loadNext = false;
14450         
14451         if(this.isExpanded()){
14452             this.collapse();
14453             if (!this.blockFocus) {
14454                 this.inputEl().focus();
14455             }
14456             
14457         }else {
14458             this.hasFocus = true;
14459             if(this.triggerAction == 'all') {
14460                 this.doQuery(this.allQuery, true);
14461             } else {
14462                 this.doQuery(this.getRawValue());
14463             }
14464             if (!this.blockFocus) {
14465                 this.inputEl().focus();
14466             }
14467         }
14468     },
14469     
14470     onTickableTriggerClick : function(e)
14471     {
14472         if(this.disabled){
14473             return;
14474         }
14475         
14476         this.page = 0;
14477         this.loadNext = false;
14478         this.hasFocus = true;
14479         
14480         if(this.triggerAction == 'all') {
14481             this.doQuery(this.allQuery, true);
14482         } else {
14483             this.doQuery(this.getRawValue());
14484         }
14485     },
14486     
14487     onSearchFieldClick : function(e)
14488     {
14489         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14490             this.onTickableFooterButtonClick(e, false, false);
14491             return;
14492         }
14493         
14494         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14495             return;
14496         }
14497         
14498         this.page = 0;
14499         this.loadNext = false;
14500         this.hasFocus = true;
14501         
14502         if(this.triggerAction == 'all') {
14503             this.doQuery(this.allQuery, true);
14504         } else {
14505             this.doQuery(this.getRawValue());
14506         }
14507     },
14508     
14509     listKeyPress : function(e)
14510     {
14511         //Roo.log('listkeypress');
14512         // scroll to first matching element based on key pres..
14513         if (e.isSpecialKey()) {
14514             return false;
14515         }
14516         var k = String.fromCharCode(e.getKey()).toUpperCase();
14517         //Roo.log(k);
14518         var match  = false;
14519         var csel = this.view.getSelectedNodes();
14520         var cselitem = false;
14521         if (csel.length) {
14522             var ix = this.view.indexOf(csel[0]);
14523             cselitem  = this.store.getAt(ix);
14524             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14525                 cselitem = false;
14526             }
14527             
14528         }
14529         
14530         this.store.each(function(v) { 
14531             if (cselitem) {
14532                 // start at existing selection.
14533                 if (cselitem.id == v.id) {
14534                     cselitem = false;
14535                 }
14536                 return true;
14537             }
14538                 
14539             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14540                 match = this.store.indexOf(v);
14541                 return false;
14542             }
14543             return true;
14544         }, this);
14545         
14546         if (match === false) {
14547             return true; // no more action?
14548         }
14549         // scroll to?
14550         this.view.select(match);
14551         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14552         sn.scrollIntoView(sn.dom.parentNode, false);
14553     },
14554     
14555     onViewScroll : function(e, t){
14556         
14557         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){
14558             return;
14559         }
14560         
14561         this.hasQuery = true;
14562         
14563         this.loading = this.list.select('.loading', true).first();
14564         
14565         if(this.loading === null){
14566             this.list.createChild({
14567                 tag: 'div',
14568                 cls: 'loading roo-select2-more-results roo-select2-active',
14569                 html: 'Loading more results...'
14570             });
14571             
14572             this.loading = this.list.select('.loading', true).first();
14573             
14574             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14575             
14576             this.loading.hide();
14577         }
14578         
14579         this.loading.show();
14580         
14581         var _combo = this;
14582         
14583         this.page++;
14584         this.loadNext = true;
14585         
14586         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14587         
14588         return;
14589     },
14590     
14591     addItem : function(o)
14592     {   
14593         var dv = ''; // display value
14594         
14595         if (this.displayField) {
14596             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14597         } else {
14598             // this is an error condition!!!
14599             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14600         }
14601         
14602         if(!dv.length){
14603             return;
14604         }
14605         
14606         var choice = this.choices.createChild({
14607             tag: 'li',
14608             cls: 'roo-select2-search-choice',
14609             cn: [
14610                 {
14611                     tag: 'div',
14612                     html: dv
14613                 },
14614                 {
14615                     tag: 'a',
14616                     href: '#',
14617                     cls: 'roo-select2-search-choice-close fa fa-times',
14618                     tabindex: '-1'
14619                 }
14620             ]
14621             
14622         }, this.searchField);
14623         
14624         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14625         
14626         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14627         
14628         this.item.push(o);
14629         
14630         this.lastData = o;
14631         
14632         this.syncValue();
14633         
14634         this.inputEl().dom.value = '';
14635         
14636         this.validate();
14637     },
14638     
14639     onRemoveItem : function(e, _self, o)
14640     {
14641         e.preventDefault();
14642         
14643         this.lastItem = Roo.apply([], this.item);
14644         
14645         var index = this.item.indexOf(o.data) * 1;
14646         
14647         if( index < 0){
14648             Roo.log('not this item?!');
14649             return;
14650         }
14651         
14652         this.item.splice(index, 1);
14653         o.item.remove();
14654         
14655         this.syncValue();
14656         
14657         this.fireEvent('remove', this, e);
14658         
14659         this.validate();
14660         
14661     },
14662     
14663     syncValue : function()
14664     {
14665         if(!this.item.length){
14666             this.clearValue();
14667             return;
14668         }
14669             
14670         var value = [];
14671         var _this = this;
14672         Roo.each(this.item, function(i){
14673             if(_this.valueField){
14674                 value.push(i[_this.valueField]);
14675                 return;
14676             }
14677
14678             value.push(i);
14679         });
14680
14681         this.value = value.join(',');
14682
14683         if(this.hiddenField){
14684             this.hiddenField.dom.value = this.value;
14685         }
14686         
14687         this.store.fireEvent("datachanged", this.store);
14688         
14689         this.validate();
14690     },
14691     
14692     clearItem : function()
14693     {
14694         if(!this.multiple){
14695             return;
14696         }
14697         
14698         this.item = [];
14699         
14700         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14701            c.remove();
14702         });
14703         
14704         this.syncValue();
14705         
14706         this.validate();
14707         
14708         if(this.tickable && !Roo.isTouch){
14709             this.view.refresh();
14710         }
14711     },
14712     
14713     inputEl: function ()
14714     {
14715         if(Roo.isIOS && this.useNativeIOS){
14716             return this.el.select('select.roo-ios-select', true).first();
14717         }
14718         
14719         if(Roo.isTouch && this.mobileTouchView){
14720             return this.el.select('input.form-control',true).first();
14721         }
14722         
14723         if(this.tickable){
14724             return this.searchField;
14725         }
14726         
14727         return this.el.select('input.form-control',true).first();
14728     },
14729     
14730     onTickableFooterButtonClick : function(e, btn, el)
14731     {
14732         e.preventDefault();
14733         
14734         this.lastItem = Roo.apply([], this.item);
14735         
14736         if(btn && btn.name == 'cancel'){
14737             this.tickItems = Roo.apply([], this.item);
14738             this.collapse();
14739             return;
14740         }
14741         
14742         this.clearItem();
14743         
14744         var _this = this;
14745         
14746         Roo.each(this.tickItems, function(o){
14747             _this.addItem(o);
14748         });
14749         
14750         this.collapse();
14751         
14752     },
14753     
14754     validate : function()
14755     {
14756         if(this.getVisibilityEl().hasClass('hidden')){
14757             return true;
14758         }
14759         
14760         var v = this.getRawValue();
14761         
14762         if(this.multiple){
14763             v = this.getValue();
14764         }
14765         
14766         if(this.disabled || this.allowBlank || v.length){
14767             this.markValid();
14768             return true;
14769         }
14770         
14771         this.markInvalid();
14772         return false;
14773     },
14774     
14775     tickableInputEl : function()
14776     {
14777         if(!this.tickable || !this.editable){
14778             return this.inputEl();
14779         }
14780         
14781         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14782     },
14783     
14784     
14785     getAutoCreateTouchView : function()
14786     {
14787         var id = Roo.id();
14788         
14789         var cfg = {
14790             cls: 'form-group' //input-group
14791         };
14792         
14793         var input =  {
14794             tag: 'input',
14795             id : id,
14796             type : this.inputType,
14797             cls : 'form-control x-combo-noedit',
14798             autocomplete: 'new-password',
14799             placeholder : this.placeholder || '',
14800             readonly : true
14801         };
14802         
14803         if (this.name) {
14804             input.name = this.name;
14805         }
14806         
14807         if (this.size) {
14808             input.cls += ' input-' + this.size;
14809         }
14810         
14811         if (this.disabled) {
14812             input.disabled = true;
14813         }
14814         
14815         var inputblock = {
14816             cls : '',
14817             cn : [
14818                 input
14819             ]
14820         };
14821         
14822         if(this.before){
14823             inputblock.cls += ' input-group';
14824             
14825             inputblock.cn.unshift({
14826                 tag :'span',
14827                 cls : 'input-group-addon',
14828                 html : this.before
14829             });
14830         }
14831         
14832         if(this.removable && !this.multiple){
14833             inputblock.cls += ' roo-removable';
14834             
14835             inputblock.cn.push({
14836                 tag: 'button',
14837                 html : 'x',
14838                 cls : 'roo-combo-removable-btn close'
14839             });
14840         }
14841
14842         if(this.hasFeedback && !this.allowBlank){
14843             
14844             inputblock.cls += ' has-feedback';
14845             
14846             inputblock.cn.push({
14847                 tag: 'span',
14848                 cls: 'glyphicon form-control-feedback'
14849             });
14850             
14851         }
14852         
14853         if (this.after) {
14854             
14855             inputblock.cls += (this.before) ? '' : ' input-group';
14856             
14857             inputblock.cn.push({
14858                 tag :'span',
14859                 cls : 'input-group-addon',
14860                 html : this.after
14861             });
14862         }
14863
14864         var box = {
14865             tag: 'div',
14866             cn: [
14867                 {
14868                     tag: 'input',
14869                     type : 'hidden',
14870                     cls: 'form-hidden-field'
14871                 },
14872                 inputblock
14873             ]
14874             
14875         };
14876         
14877         if(this.multiple){
14878             box = {
14879                 tag: 'div',
14880                 cn: [
14881                     {
14882                         tag: 'input',
14883                         type : 'hidden',
14884                         cls: 'form-hidden-field'
14885                     },
14886                     {
14887                         tag: 'ul',
14888                         cls: 'roo-select2-choices',
14889                         cn:[
14890                             {
14891                                 tag: 'li',
14892                                 cls: 'roo-select2-search-field',
14893                                 cn: [
14894
14895                                     inputblock
14896                                 ]
14897                             }
14898                         ]
14899                     }
14900                 ]
14901             }
14902         };
14903         
14904         var combobox = {
14905             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14906             cn: [
14907                 box
14908             ]
14909         };
14910         
14911         if(!this.multiple && this.showToggleBtn){
14912             
14913             var caret = {
14914                         tag: 'span',
14915                         cls: 'caret'
14916             };
14917             
14918             if (this.caret != false) {
14919                 caret = {
14920                      tag: 'i',
14921                      cls: 'fa fa-' + this.caret
14922                 };
14923                 
14924             }
14925             
14926             combobox.cn.push({
14927                 tag :'span',
14928                 cls : 'input-group-addon btn dropdown-toggle',
14929                 cn : [
14930                     caret,
14931                     {
14932                         tag: 'span',
14933                         cls: 'combobox-clear',
14934                         cn  : [
14935                             {
14936                                 tag : 'i',
14937                                 cls: 'icon-remove'
14938                             }
14939                         ]
14940                     }
14941                 ]
14942
14943             })
14944         }
14945         
14946         if(this.multiple){
14947             combobox.cls += ' roo-select2-container-multi';
14948         }
14949         
14950         var align = this.labelAlign || this.parentLabelAlign();
14951         
14952         if (align ==='left' && this.fieldLabel.length) {
14953
14954             cfg.cn = [
14955                 {
14956                    tag : 'i',
14957                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14958                    tooltip : 'This field is required'
14959                 },
14960                 {
14961                     tag: 'label',
14962                     cls : 'control-label',
14963                     html : this.fieldLabel
14964
14965                 },
14966                 {
14967                     cls : '', 
14968                     cn: [
14969                         combobox
14970                     ]
14971                 }
14972             ];
14973             
14974             var labelCfg = cfg.cn[1];
14975             var contentCfg = cfg.cn[2];
14976             
14977
14978             if(this.indicatorpos == 'right'){
14979                 cfg.cn = [
14980                     {
14981                         tag: 'label',
14982                         'for' :  id,
14983                         cls : 'control-label',
14984                         cn : [
14985                             {
14986                                 tag : 'span',
14987                                 html : this.fieldLabel
14988                             },
14989                             {
14990                                 tag : 'i',
14991                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14992                                 tooltip : 'This field is required'
14993                             }
14994                         ]
14995                     },
14996                     {
14997                         cls : "",
14998                         cn: [
14999                             combobox
15000                         ]
15001                     }
15002
15003                 ];
15004                 
15005                 labelCfg = cfg.cn[0];
15006                 contentCfg = cfg.cn[1];
15007             }
15008             
15009            
15010             
15011             if(this.labelWidth > 12){
15012                 labelCfg.style = "width: " + this.labelWidth + 'px';
15013             }
15014             
15015             if(this.labelWidth < 13 && this.labelmd == 0){
15016                 this.labelmd = this.labelWidth;
15017             }
15018             
15019             if(this.labellg > 0){
15020                 labelCfg.cls += ' col-lg-' + this.labellg;
15021                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15022             }
15023             
15024             if(this.labelmd > 0){
15025                 labelCfg.cls += ' col-md-' + this.labelmd;
15026                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15027             }
15028             
15029             if(this.labelsm > 0){
15030                 labelCfg.cls += ' col-sm-' + this.labelsm;
15031                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15032             }
15033             
15034             if(this.labelxs > 0){
15035                 labelCfg.cls += ' col-xs-' + this.labelxs;
15036                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15037             }
15038                 
15039                 
15040         } else if ( this.fieldLabel.length) {
15041             cfg.cn = [
15042                 {
15043                    tag : 'i',
15044                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15045                    tooltip : 'This field is required'
15046                 },
15047                 {
15048                     tag: 'label',
15049                     cls : 'control-label',
15050                     html : this.fieldLabel
15051
15052                 },
15053                 {
15054                     cls : '', 
15055                     cn: [
15056                         combobox
15057                     ]
15058                 }
15059             ];
15060             
15061             if(this.indicatorpos == 'right'){
15062                 cfg.cn = [
15063                     {
15064                         tag: 'label',
15065                         cls : 'control-label',
15066                         html : this.fieldLabel,
15067                         cn : [
15068                             {
15069                                tag : 'i',
15070                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15071                                tooltip : 'This field is required'
15072                             }
15073                         ]
15074                     },
15075                     {
15076                         cls : '', 
15077                         cn: [
15078                             combobox
15079                         ]
15080                     }
15081                 ];
15082             }
15083         } else {
15084             cfg.cn = combobox;    
15085         }
15086         
15087         
15088         var settings = this;
15089         
15090         ['xs','sm','md','lg'].map(function(size){
15091             if (settings[size]) {
15092                 cfg.cls += ' col-' + size + '-' + settings[size];
15093             }
15094         });
15095         
15096         return cfg;
15097     },
15098     
15099     initTouchView : function()
15100     {
15101         this.renderTouchView();
15102         
15103         this.touchViewEl.on('scroll', function(){
15104             this.el.dom.scrollTop = 0;
15105         }, this);
15106         
15107         this.originalValue = this.getValue();
15108         
15109         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15110         
15111         this.inputEl().on("click", this.showTouchView, this);
15112         if (this.triggerEl) {
15113             this.triggerEl.on("click", this.showTouchView, this);
15114         }
15115         
15116         
15117         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15118         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15119         
15120         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15121         
15122         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15123         this.store.on('load', this.onTouchViewLoad, this);
15124         this.store.on('loadexception', this.onTouchViewLoadException, this);
15125         
15126         if(this.hiddenName){
15127             
15128             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15129             
15130             this.hiddenField.dom.value =
15131                 this.hiddenValue !== undefined ? this.hiddenValue :
15132                 this.value !== undefined ? this.value : '';
15133         
15134             this.el.dom.removeAttribute('name');
15135             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15136         }
15137         
15138         if(this.multiple){
15139             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15140             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15141         }
15142         
15143         if(this.removable && !this.multiple){
15144             var close = this.closeTriggerEl();
15145             if(close){
15146                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15147                 close.on('click', this.removeBtnClick, this, close);
15148             }
15149         }
15150         /*
15151          * fix the bug in Safari iOS8
15152          */
15153         this.inputEl().on("focus", function(e){
15154             document.activeElement.blur();
15155         }, this);
15156         
15157         return;
15158         
15159         
15160     },
15161     
15162     renderTouchView : function()
15163     {
15164         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15165         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15166         
15167         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15168         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15169         
15170         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15171         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15172         this.touchViewBodyEl.setStyle('overflow', 'auto');
15173         
15174         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15175         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15176         
15177         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15178         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15179         
15180     },
15181     
15182     showTouchView : function()
15183     {
15184         if(this.disabled){
15185             return;
15186         }
15187         
15188         this.touchViewHeaderEl.hide();
15189
15190         if(this.modalTitle.length){
15191             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15192             this.touchViewHeaderEl.show();
15193         }
15194
15195         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15196         this.touchViewEl.show();
15197
15198         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15199         
15200         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15201         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15202
15203         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15204
15205         if(this.modalTitle.length){
15206             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15207         }
15208         
15209         this.touchViewBodyEl.setHeight(bodyHeight);
15210
15211         if(this.animate){
15212             var _this = this;
15213             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15214         }else{
15215             this.touchViewEl.addClass('in');
15216         }
15217
15218         this.doTouchViewQuery();
15219         
15220     },
15221     
15222     hideTouchView : function()
15223     {
15224         this.touchViewEl.removeClass('in');
15225
15226         if(this.animate){
15227             var _this = this;
15228             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15229         }else{
15230             this.touchViewEl.setStyle('display', 'none');
15231         }
15232         
15233     },
15234     
15235     setTouchViewValue : function()
15236     {
15237         if(this.multiple){
15238             this.clearItem();
15239         
15240             var _this = this;
15241
15242             Roo.each(this.tickItems, function(o){
15243                 this.addItem(o);
15244             }, this);
15245         }
15246         
15247         this.hideTouchView();
15248     },
15249     
15250     doTouchViewQuery : function()
15251     {
15252         var qe = {
15253             query: '',
15254             forceAll: true,
15255             combo: this,
15256             cancel:false
15257         };
15258         
15259         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15260             return false;
15261         }
15262         
15263         if(!this.alwaysQuery || this.mode == 'local'){
15264             this.onTouchViewLoad();
15265             return;
15266         }
15267         
15268         this.store.load();
15269     },
15270     
15271     onTouchViewBeforeLoad : function(combo,opts)
15272     {
15273         return;
15274     },
15275
15276     // private
15277     onTouchViewLoad : function()
15278     {
15279         if(this.store.getCount() < 1){
15280             this.onTouchViewEmptyResults();
15281             return;
15282         }
15283         
15284         this.clearTouchView();
15285         
15286         var rawValue = this.getRawValue();
15287         
15288         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15289         
15290         this.tickItems = [];
15291         
15292         this.store.data.each(function(d, rowIndex){
15293             var row = this.touchViewListGroup.createChild(template);
15294             
15295             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15296                 row.addClass(d.data.cls);
15297             }
15298             
15299             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15300                 var cfg = {
15301                     data : d.data,
15302                     html : d.data[this.displayField]
15303                 };
15304                 
15305                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15306                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15307                 }
15308             }
15309             row.removeClass('selected');
15310             if(!this.multiple && this.valueField &&
15311                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15312             {
15313                 // radio buttons..
15314                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15315                 row.addClass('selected');
15316             }
15317             
15318             if(this.multiple && this.valueField &&
15319                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15320             {
15321                 
15322                 // checkboxes...
15323                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15324                 this.tickItems.push(d.data);
15325             }
15326             
15327             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15328             
15329         }, this);
15330         
15331         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15332         
15333         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15334
15335         if(this.modalTitle.length){
15336             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15337         }
15338
15339         var listHeight = this.touchViewListGroup.getHeight();
15340         
15341         var _this = this;
15342         
15343         if(firstChecked && listHeight > bodyHeight){
15344             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15345         }
15346         
15347     },
15348     
15349     onTouchViewLoadException : function()
15350     {
15351         this.hideTouchView();
15352     },
15353     
15354     onTouchViewEmptyResults : function()
15355     {
15356         this.clearTouchView();
15357         
15358         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15359         
15360         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15361         
15362     },
15363     
15364     clearTouchView : function()
15365     {
15366         this.touchViewListGroup.dom.innerHTML = '';
15367     },
15368     
15369     onTouchViewClick : function(e, el, o)
15370     {
15371         e.preventDefault();
15372         
15373         var row = o.row;
15374         var rowIndex = o.rowIndex;
15375         
15376         var r = this.store.getAt(rowIndex);
15377         
15378         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15379             
15380             if(!this.multiple){
15381                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15382                     c.dom.removeAttribute('checked');
15383                 }, this);
15384
15385                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15386
15387                 this.setFromData(r.data);
15388
15389                 var close = this.closeTriggerEl();
15390
15391                 if(close){
15392                     close.show();
15393                 }
15394
15395                 this.hideTouchView();
15396
15397                 this.fireEvent('select', this, r, rowIndex);
15398
15399                 return;
15400             }
15401
15402             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15403                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15404                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15405                 return;
15406             }
15407
15408             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15409             this.addItem(r.data);
15410             this.tickItems.push(r.data);
15411         }
15412     },
15413     
15414     getAutoCreateNativeIOS : function()
15415     {
15416         var cfg = {
15417             cls: 'form-group' //input-group,
15418         };
15419         
15420         var combobox =  {
15421             tag: 'select',
15422             cls : 'roo-ios-select'
15423         };
15424         
15425         if (this.name) {
15426             combobox.name = this.name;
15427         }
15428         
15429         if (this.disabled) {
15430             combobox.disabled = true;
15431         }
15432         
15433         var settings = this;
15434         
15435         ['xs','sm','md','lg'].map(function(size){
15436             if (settings[size]) {
15437                 cfg.cls += ' col-' + size + '-' + settings[size];
15438             }
15439         });
15440         
15441         cfg.cn = combobox;
15442         
15443         return cfg;
15444         
15445     },
15446     
15447     initIOSView : function()
15448     {
15449         this.store.on('load', this.onIOSViewLoad, this);
15450         
15451         return;
15452     },
15453     
15454     onIOSViewLoad : function()
15455     {
15456         if(this.store.getCount() < 1){
15457             return;
15458         }
15459         
15460         this.clearIOSView();
15461         
15462         if(this.allowBlank) {
15463             
15464             var default_text = '-- SELECT --';
15465             
15466             if(this.placeholder.length){
15467                 default_text = this.placeholder;
15468             }
15469             
15470             if(this.emptyTitle.length){
15471                 default_text += ' - ' + this.emptyTitle + ' -';
15472             }
15473             
15474             var opt = this.inputEl().createChild({
15475                 tag: 'option',
15476                 value : 0,
15477                 html : default_text
15478             });
15479             
15480             var o = {};
15481             o[this.valueField] = 0;
15482             o[this.displayField] = default_text;
15483             
15484             this.ios_options.push({
15485                 data : o,
15486                 el : opt
15487             });
15488             
15489         }
15490         
15491         this.store.data.each(function(d, rowIndex){
15492             
15493             var html = '';
15494             
15495             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15496                 html = d.data[this.displayField];
15497             }
15498             
15499             var value = '';
15500             
15501             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15502                 value = d.data[this.valueField];
15503             }
15504             
15505             var option = {
15506                 tag: 'option',
15507                 value : value,
15508                 html : html
15509             };
15510             
15511             if(this.value == d.data[this.valueField]){
15512                 option['selected'] = true;
15513             }
15514             
15515             var opt = this.inputEl().createChild(option);
15516             
15517             this.ios_options.push({
15518                 data : d.data,
15519                 el : opt
15520             });
15521             
15522         }, this);
15523         
15524         this.inputEl().on('change', function(){
15525            this.fireEvent('select', this);
15526         }, this);
15527         
15528     },
15529     
15530     clearIOSView: function()
15531     {
15532         this.inputEl().dom.innerHTML = '';
15533         
15534         this.ios_options = [];
15535     },
15536     
15537     setIOSValue: function(v)
15538     {
15539         this.value = v;
15540         
15541         if(!this.ios_options){
15542             return;
15543         }
15544         
15545         Roo.each(this.ios_options, function(opts){
15546            
15547            opts.el.dom.removeAttribute('selected');
15548            
15549            if(opts.data[this.valueField] != v){
15550                return;
15551            }
15552            
15553            opts.el.dom.setAttribute('selected', true);
15554            
15555         }, this);
15556     }
15557
15558     /** 
15559     * @cfg {Boolean} grow 
15560     * @hide 
15561     */
15562     /** 
15563     * @cfg {Number} growMin 
15564     * @hide 
15565     */
15566     /** 
15567     * @cfg {Number} growMax 
15568     * @hide 
15569     */
15570     /**
15571      * @hide
15572      * @method autoSize
15573      */
15574 });
15575
15576 Roo.apply(Roo.bootstrap.ComboBox,  {
15577     
15578     header : {
15579         tag: 'div',
15580         cls: 'modal-header',
15581         cn: [
15582             {
15583                 tag: 'h4',
15584                 cls: 'modal-title'
15585             }
15586         ]
15587     },
15588     
15589     body : {
15590         tag: 'div',
15591         cls: 'modal-body',
15592         cn: [
15593             {
15594                 tag: 'ul',
15595                 cls: 'list-group'
15596             }
15597         ]
15598     },
15599     
15600     listItemRadio : {
15601         tag: 'li',
15602         cls: 'list-group-item',
15603         cn: [
15604             {
15605                 tag: 'span',
15606                 cls: 'roo-combobox-list-group-item-value'
15607             },
15608             {
15609                 tag: 'div',
15610                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15611                 cn: [
15612                     {
15613                         tag: 'input',
15614                         type: 'radio'
15615                     },
15616                     {
15617                         tag: 'label'
15618                     }
15619                 ]
15620             }
15621         ]
15622     },
15623     
15624     listItemCheckbox : {
15625         tag: 'li',
15626         cls: 'list-group-item',
15627         cn: [
15628             {
15629                 tag: 'span',
15630                 cls: 'roo-combobox-list-group-item-value'
15631             },
15632             {
15633                 tag: 'div',
15634                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15635                 cn: [
15636                     {
15637                         tag: 'input',
15638                         type: 'checkbox'
15639                     },
15640                     {
15641                         tag: 'label'
15642                     }
15643                 ]
15644             }
15645         ]
15646     },
15647     
15648     emptyResult : {
15649         tag: 'div',
15650         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15651     },
15652     
15653     footer : {
15654         tag: 'div',
15655         cls: 'modal-footer',
15656         cn: [
15657             {
15658                 tag: 'div',
15659                 cls: 'row',
15660                 cn: [
15661                     {
15662                         tag: 'div',
15663                         cls: 'col-xs-6 text-left',
15664                         cn: {
15665                             tag: 'button',
15666                             cls: 'btn btn-danger roo-touch-view-cancel',
15667                             html: 'Cancel'
15668                         }
15669                     },
15670                     {
15671                         tag: 'div',
15672                         cls: 'col-xs-6 text-right',
15673                         cn: {
15674                             tag: 'button',
15675                             cls: 'btn btn-success roo-touch-view-ok',
15676                             html: 'OK'
15677                         }
15678                     }
15679                 ]
15680             }
15681         ]
15682         
15683     }
15684 });
15685
15686 Roo.apply(Roo.bootstrap.ComboBox,  {
15687     
15688     touchViewTemplate : {
15689         tag: 'div',
15690         cls: 'modal fade roo-combobox-touch-view',
15691         cn: [
15692             {
15693                 tag: 'div',
15694                 cls: 'modal-dialog',
15695                 style : 'position:fixed', // we have to fix position....
15696                 cn: [
15697                     {
15698                         tag: 'div',
15699                         cls: 'modal-content',
15700                         cn: [
15701                             Roo.bootstrap.ComboBox.header,
15702                             Roo.bootstrap.ComboBox.body,
15703                             Roo.bootstrap.ComboBox.footer
15704                         ]
15705                     }
15706                 ]
15707             }
15708         ]
15709     }
15710 });/*
15711  * Based on:
15712  * Ext JS Library 1.1.1
15713  * Copyright(c) 2006-2007, Ext JS, LLC.
15714  *
15715  * Originally Released Under LGPL - original licence link has changed is not relivant.
15716  *
15717  * Fork - LGPL
15718  * <script type="text/javascript">
15719  */
15720
15721 /**
15722  * @class Roo.View
15723  * @extends Roo.util.Observable
15724  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15725  * This class also supports single and multi selection modes. <br>
15726  * Create a data model bound view:
15727  <pre><code>
15728  var store = new Roo.data.Store(...);
15729
15730  var view = new Roo.View({
15731     el : "my-element",
15732     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15733  
15734     singleSelect: true,
15735     selectedClass: "ydataview-selected",
15736     store: store
15737  });
15738
15739  // listen for node click?
15740  view.on("click", function(vw, index, node, e){
15741  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15742  });
15743
15744  // load XML data
15745  dataModel.load("foobar.xml");
15746  </code></pre>
15747  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15748  * <br><br>
15749  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15750  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15751  * 
15752  * Note: old style constructor is still suported (container, template, config)
15753  * 
15754  * @constructor
15755  * Create a new View
15756  * @param {Object} config The config object
15757  * 
15758  */
15759 Roo.View = function(config, depreciated_tpl, depreciated_config){
15760     
15761     this.parent = false;
15762     
15763     if (typeof(depreciated_tpl) == 'undefined') {
15764         // new way.. - universal constructor.
15765         Roo.apply(this, config);
15766         this.el  = Roo.get(this.el);
15767     } else {
15768         // old format..
15769         this.el  = Roo.get(config);
15770         this.tpl = depreciated_tpl;
15771         Roo.apply(this, depreciated_config);
15772     }
15773     this.wrapEl  = this.el.wrap().wrap();
15774     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15775     
15776     
15777     if(typeof(this.tpl) == "string"){
15778         this.tpl = new Roo.Template(this.tpl);
15779     } else {
15780         // support xtype ctors..
15781         this.tpl = new Roo.factory(this.tpl, Roo);
15782     }
15783     
15784     
15785     this.tpl.compile();
15786     
15787     /** @private */
15788     this.addEvents({
15789         /**
15790          * @event beforeclick
15791          * Fires before a click is processed. Returns false to cancel the default action.
15792          * @param {Roo.View} this
15793          * @param {Number} index The index of the target node
15794          * @param {HTMLElement} node The target node
15795          * @param {Roo.EventObject} e The raw event object
15796          */
15797             "beforeclick" : true,
15798         /**
15799          * @event click
15800          * Fires when a template node is clicked.
15801          * @param {Roo.View} this
15802          * @param {Number} index The index of the target node
15803          * @param {HTMLElement} node The target node
15804          * @param {Roo.EventObject} e The raw event object
15805          */
15806             "click" : true,
15807         /**
15808          * @event dblclick
15809          * Fires when a template node is double clicked.
15810          * @param {Roo.View} this
15811          * @param {Number} index The index of the target node
15812          * @param {HTMLElement} node The target node
15813          * @param {Roo.EventObject} e The raw event object
15814          */
15815             "dblclick" : true,
15816         /**
15817          * @event contextmenu
15818          * Fires when a template node is right clicked.
15819          * @param {Roo.View} this
15820          * @param {Number} index The index of the target node
15821          * @param {HTMLElement} node The target node
15822          * @param {Roo.EventObject} e The raw event object
15823          */
15824             "contextmenu" : true,
15825         /**
15826          * @event selectionchange
15827          * Fires when the selected nodes change.
15828          * @param {Roo.View} this
15829          * @param {Array} selections Array of the selected nodes
15830          */
15831             "selectionchange" : true,
15832     
15833         /**
15834          * @event beforeselect
15835          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15836          * @param {Roo.View} this
15837          * @param {HTMLElement} node The node to be selected
15838          * @param {Array} selections Array of currently selected nodes
15839          */
15840             "beforeselect" : true,
15841         /**
15842          * @event preparedata
15843          * Fires on every row to render, to allow you to change the data.
15844          * @param {Roo.View} this
15845          * @param {Object} data to be rendered (change this)
15846          */
15847           "preparedata" : true
15848           
15849           
15850         });
15851
15852
15853
15854     this.el.on({
15855         "click": this.onClick,
15856         "dblclick": this.onDblClick,
15857         "contextmenu": this.onContextMenu,
15858         scope:this
15859     });
15860
15861     this.selections = [];
15862     this.nodes = [];
15863     this.cmp = new Roo.CompositeElementLite([]);
15864     if(this.store){
15865         this.store = Roo.factory(this.store, Roo.data);
15866         this.setStore(this.store, true);
15867     }
15868     
15869     if ( this.footer && this.footer.xtype) {
15870            
15871          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15872         
15873         this.footer.dataSource = this.store;
15874         this.footer.container = fctr;
15875         this.footer = Roo.factory(this.footer, Roo);
15876         fctr.insertFirst(this.el);
15877         
15878         // this is a bit insane - as the paging toolbar seems to detach the el..
15879 //        dom.parentNode.parentNode.parentNode
15880          // they get detached?
15881     }
15882     
15883     
15884     Roo.View.superclass.constructor.call(this);
15885     
15886     
15887 };
15888
15889 Roo.extend(Roo.View, Roo.util.Observable, {
15890     
15891      /**
15892      * @cfg {Roo.data.Store} store Data store to load data from.
15893      */
15894     store : false,
15895     
15896     /**
15897      * @cfg {String|Roo.Element} el The container element.
15898      */
15899     el : '',
15900     
15901     /**
15902      * @cfg {String|Roo.Template} tpl The template used by this View 
15903      */
15904     tpl : false,
15905     /**
15906      * @cfg {String} dataName the named area of the template to use as the data area
15907      *                          Works with domtemplates roo-name="name"
15908      */
15909     dataName: false,
15910     /**
15911      * @cfg {String} selectedClass The css class to add to selected nodes
15912      */
15913     selectedClass : "x-view-selected",
15914      /**
15915      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15916      */
15917     emptyText : "",
15918     
15919     /**
15920      * @cfg {String} text to display on mask (default Loading)
15921      */
15922     mask : false,
15923     /**
15924      * @cfg {Boolean} multiSelect Allow multiple selection
15925      */
15926     multiSelect : false,
15927     /**
15928      * @cfg {Boolean} singleSelect Allow single selection
15929      */
15930     singleSelect:  false,
15931     
15932     /**
15933      * @cfg {Boolean} toggleSelect - selecting 
15934      */
15935     toggleSelect : false,
15936     
15937     /**
15938      * @cfg {Boolean} tickable - selecting 
15939      */
15940     tickable : false,
15941     
15942     /**
15943      * Returns the element this view is bound to.
15944      * @return {Roo.Element}
15945      */
15946     getEl : function(){
15947         return this.wrapEl;
15948     },
15949     
15950     
15951
15952     /**
15953      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15954      */
15955     refresh : function(){
15956         //Roo.log('refresh');
15957         var t = this.tpl;
15958         
15959         // if we are using something like 'domtemplate', then
15960         // the what gets used is:
15961         // t.applySubtemplate(NAME, data, wrapping data..)
15962         // the outer template then get' applied with
15963         //     the store 'extra data'
15964         // and the body get's added to the
15965         //      roo-name="data" node?
15966         //      <span class='roo-tpl-{name}'></span> ?????
15967         
15968         
15969         
15970         this.clearSelections();
15971         this.el.update("");
15972         var html = [];
15973         var records = this.store.getRange();
15974         if(records.length < 1) {
15975             
15976             // is this valid??  = should it render a template??
15977             
15978             this.el.update(this.emptyText);
15979             return;
15980         }
15981         var el = this.el;
15982         if (this.dataName) {
15983             this.el.update(t.apply(this.store.meta)); //????
15984             el = this.el.child('.roo-tpl-' + this.dataName);
15985         }
15986         
15987         for(var i = 0, len = records.length; i < len; i++){
15988             var data = this.prepareData(records[i].data, i, records[i]);
15989             this.fireEvent("preparedata", this, data, i, records[i]);
15990             
15991             var d = Roo.apply({}, data);
15992             
15993             if(this.tickable){
15994                 Roo.apply(d, {'roo-id' : Roo.id()});
15995                 
15996                 var _this = this;
15997             
15998                 Roo.each(this.parent.item, function(item){
15999                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16000                         return;
16001                     }
16002                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16003                 });
16004             }
16005             
16006             html[html.length] = Roo.util.Format.trim(
16007                 this.dataName ?
16008                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16009                     t.apply(d)
16010             );
16011         }
16012         
16013         
16014         
16015         el.update(html.join(""));
16016         this.nodes = el.dom.childNodes;
16017         this.updateIndexes(0);
16018     },
16019     
16020
16021     /**
16022      * Function to override to reformat the data that is sent to
16023      * the template for each node.
16024      * DEPRICATED - use the preparedata event handler.
16025      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16026      * a JSON object for an UpdateManager bound view).
16027      */
16028     prepareData : function(data, index, record)
16029     {
16030         this.fireEvent("preparedata", this, data, index, record);
16031         return data;
16032     },
16033
16034     onUpdate : function(ds, record){
16035         // Roo.log('on update');   
16036         this.clearSelections();
16037         var index = this.store.indexOf(record);
16038         var n = this.nodes[index];
16039         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16040         n.parentNode.removeChild(n);
16041         this.updateIndexes(index, index);
16042     },
16043
16044     
16045     
16046 // --------- FIXME     
16047     onAdd : function(ds, records, index)
16048     {
16049         //Roo.log(['on Add', ds, records, index] );        
16050         this.clearSelections();
16051         if(this.nodes.length == 0){
16052             this.refresh();
16053             return;
16054         }
16055         var n = this.nodes[index];
16056         for(var i = 0, len = records.length; i < len; i++){
16057             var d = this.prepareData(records[i].data, i, records[i]);
16058             if(n){
16059                 this.tpl.insertBefore(n, d);
16060             }else{
16061                 
16062                 this.tpl.append(this.el, d);
16063             }
16064         }
16065         this.updateIndexes(index);
16066     },
16067
16068     onRemove : function(ds, record, index){
16069        // Roo.log('onRemove');
16070         this.clearSelections();
16071         var el = this.dataName  ?
16072             this.el.child('.roo-tpl-' + this.dataName) :
16073             this.el; 
16074         
16075         el.dom.removeChild(this.nodes[index]);
16076         this.updateIndexes(index);
16077     },
16078
16079     /**
16080      * Refresh an individual node.
16081      * @param {Number} index
16082      */
16083     refreshNode : function(index){
16084         this.onUpdate(this.store, this.store.getAt(index));
16085     },
16086
16087     updateIndexes : function(startIndex, endIndex){
16088         var ns = this.nodes;
16089         startIndex = startIndex || 0;
16090         endIndex = endIndex || ns.length - 1;
16091         for(var i = startIndex; i <= endIndex; i++){
16092             ns[i].nodeIndex = i;
16093         }
16094     },
16095
16096     /**
16097      * Changes the data store this view uses and refresh the view.
16098      * @param {Store} store
16099      */
16100     setStore : function(store, initial){
16101         if(!initial && this.store){
16102             this.store.un("datachanged", this.refresh);
16103             this.store.un("add", this.onAdd);
16104             this.store.un("remove", this.onRemove);
16105             this.store.un("update", this.onUpdate);
16106             this.store.un("clear", this.refresh);
16107             this.store.un("beforeload", this.onBeforeLoad);
16108             this.store.un("load", this.onLoad);
16109             this.store.un("loadexception", this.onLoad);
16110         }
16111         if(store){
16112           
16113             store.on("datachanged", this.refresh, this);
16114             store.on("add", this.onAdd, this);
16115             store.on("remove", this.onRemove, this);
16116             store.on("update", this.onUpdate, this);
16117             store.on("clear", this.refresh, this);
16118             store.on("beforeload", this.onBeforeLoad, this);
16119             store.on("load", this.onLoad, this);
16120             store.on("loadexception", this.onLoad, this);
16121         }
16122         
16123         if(store){
16124             this.refresh();
16125         }
16126     },
16127     /**
16128      * onbeforeLoad - masks the loading area.
16129      *
16130      */
16131     onBeforeLoad : function(store,opts)
16132     {
16133          //Roo.log('onBeforeLoad');   
16134         if (!opts.add) {
16135             this.el.update("");
16136         }
16137         this.el.mask(this.mask ? this.mask : "Loading" ); 
16138     },
16139     onLoad : function ()
16140     {
16141         this.el.unmask();
16142     },
16143     
16144
16145     /**
16146      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16147      * @param {HTMLElement} node
16148      * @return {HTMLElement} The template node
16149      */
16150     findItemFromChild : function(node){
16151         var el = this.dataName  ?
16152             this.el.child('.roo-tpl-' + this.dataName,true) :
16153             this.el.dom; 
16154         
16155         if(!node || node.parentNode == el){
16156                     return node;
16157             }
16158             var p = node.parentNode;
16159             while(p && p != el){
16160             if(p.parentNode == el){
16161                 return p;
16162             }
16163             p = p.parentNode;
16164         }
16165             return null;
16166     },
16167
16168     /** @ignore */
16169     onClick : function(e){
16170         var item = this.findItemFromChild(e.getTarget());
16171         if(item){
16172             var index = this.indexOf(item);
16173             if(this.onItemClick(item, index, e) !== false){
16174                 this.fireEvent("click", this, index, item, e);
16175             }
16176         }else{
16177             this.clearSelections();
16178         }
16179     },
16180
16181     /** @ignore */
16182     onContextMenu : function(e){
16183         var item = this.findItemFromChild(e.getTarget());
16184         if(item){
16185             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16186         }
16187     },
16188
16189     /** @ignore */
16190     onDblClick : function(e){
16191         var item = this.findItemFromChild(e.getTarget());
16192         if(item){
16193             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16194         }
16195     },
16196
16197     onItemClick : function(item, index, e)
16198     {
16199         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16200             return false;
16201         }
16202         if (this.toggleSelect) {
16203             var m = this.isSelected(item) ? 'unselect' : 'select';
16204             //Roo.log(m);
16205             var _t = this;
16206             _t[m](item, true, false);
16207             return true;
16208         }
16209         if(this.multiSelect || this.singleSelect){
16210             if(this.multiSelect && e.shiftKey && this.lastSelection){
16211                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16212             }else{
16213                 this.select(item, this.multiSelect && e.ctrlKey);
16214                 this.lastSelection = item;
16215             }
16216             
16217             if(!this.tickable){
16218                 e.preventDefault();
16219             }
16220             
16221         }
16222         return true;
16223     },
16224
16225     /**
16226      * Get the number of selected nodes.
16227      * @return {Number}
16228      */
16229     getSelectionCount : function(){
16230         return this.selections.length;
16231     },
16232
16233     /**
16234      * Get the currently selected nodes.
16235      * @return {Array} An array of HTMLElements
16236      */
16237     getSelectedNodes : function(){
16238         return this.selections;
16239     },
16240
16241     /**
16242      * Get the indexes of the selected nodes.
16243      * @return {Array}
16244      */
16245     getSelectedIndexes : function(){
16246         var indexes = [], s = this.selections;
16247         for(var i = 0, len = s.length; i < len; i++){
16248             indexes.push(s[i].nodeIndex);
16249         }
16250         return indexes;
16251     },
16252
16253     /**
16254      * Clear all selections
16255      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16256      */
16257     clearSelections : function(suppressEvent){
16258         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16259             this.cmp.elements = this.selections;
16260             this.cmp.removeClass(this.selectedClass);
16261             this.selections = [];
16262             if(!suppressEvent){
16263                 this.fireEvent("selectionchange", this, this.selections);
16264             }
16265         }
16266     },
16267
16268     /**
16269      * Returns true if the passed node is selected
16270      * @param {HTMLElement/Number} node The node or node index
16271      * @return {Boolean}
16272      */
16273     isSelected : function(node){
16274         var s = this.selections;
16275         if(s.length < 1){
16276             return false;
16277         }
16278         node = this.getNode(node);
16279         return s.indexOf(node) !== -1;
16280     },
16281
16282     /**
16283      * Selects nodes.
16284      * @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
16285      * @param {Boolean} keepExisting (optional) true to keep existing selections
16286      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16287      */
16288     select : function(nodeInfo, keepExisting, suppressEvent){
16289         if(nodeInfo instanceof Array){
16290             if(!keepExisting){
16291                 this.clearSelections(true);
16292             }
16293             for(var i = 0, len = nodeInfo.length; i < len; i++){
16294                 this.select(nodeInfo[i], true, true);
16295             }
16296             return;
16297         } 
16298         var node = this.getNode(nodeInfo);
16299         if(!node || this.isSelected(node)){
16300             return; // already selected.
16301         }
16302         if(!keepExisting){
16303             this.clearSelections(true);
16304         }
16305         
16306         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16307             Roo.fly(node).addClass(this.selectedClass);
16308             this.selections.push(node);
16309             if(!suppressEvent){
16310                 this.fireEvent("selectionchange", this, this.selections);
16311             }
16312         }
16313         
16314         
16315     },
16316       /**
16317      * Unselects nodes.
16318      * @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
16319      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16320      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16321      */
16322     unselect : function(nodeInfo, keepExisting, suppressEvent)
16323     {
16324         if(nodeInfo instanceof Array){
16325             Roo.each(this.selections, function(s) {
16326                 this.unselect(s, nodeInfo);
16327             }, this);
16328             return;
16329         }
16330         var node = this.getNode(nodeInfo);
16331         if(!node || !this.isSelected(node)){
16332             //Roo.log("not selected");
16333             return; // not selected.
16334         }
16335         // fireevent???
16336         var ns = [];
16337         Roo.each(this.selections, function(s) {
16338             if (s == node ) {
16339                 Roo.fly(node).removeClass(this.selectedClass);
16340
16341                 return;
16342             }
16343             ns.push(s);
16344         },this);
16345         
16346         this.selections= ns;
16347         this.fireEvent("selectionchange", this, this.selections);
16348     },
16349
16350     /**
16351      * Gets a template node.
16352      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16353      * @return {HTMLElement} The node or null if it wasn't found
16354      */
16355     getNode : function(nodeInfo){
16356         if(typeof nodeInfo == "string"){
16357             return document.getElementById(nodeInfo);
16358         }else if(typeof nodeInfo == "number"){
16359             return this.nodes[nodeInfo];
16360         }
16361         return nodeInfo;
16362     },
16363
16364     /**
16365      * Gets a range template nodes.
16366      * @param {Number} startIndex
16367      * @param {Number} endIndex
16368      * @return {Array} An array of nodes
16369      */
16370     getNodes : function(start, end){
16371         var ns = this.nodes;
16372         start = start || 0;
16373         end = typeof end == "undefined" ? ns.length - 1 : end;
16374         var nodes = [];
16375         if(start <= end){
16376             for(var i = start; i <= end; i++){
16377                 nodes.push(ns[i]);
16378             }
16379         } else{
16380             for(var i = start; i >= end; i--){
16381                 nodes.push(ns[i]);
16382             }
16383         }
16384         return nodes;
16385     },
16386
16387     /**
16388      * Finds the index of the passed node
16389      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16390      * @return {Number} The index of the node or -1
16391      */
16392     indexOf : function(node){
16393         node = this.getNode(node);
16394         if(typeof node.nodeIndex == "number"){
16395             return node.nodeIndex;
16396         }
16397         var ns = this.nodes;
16398         for(var i = 0, len = ns.length; i < len; i++){
16399             if(ns[i] == node){
16400                 return i;
16401             }
16402         }
16403         return -1;
16404     }
16405 });
16406 /*
16407  * - LGPL
16408  *
16409  * based on jquery fullcalendar
16410  * 
16411  */
16412
16413 Roo.bootstrap = Roo.bootstrap || {};
16414 /**
16415  * @class Roo.bootstrap.Calendar
16416  * @extends Roo.bootstrap.Component
16417  * Bootstrap Calendar class
16418  * @cfg {Boolean} loadMask (true|false) default false
16419  * @cfg {Object} header generate the user specific header of the calendar, default false
16420
16421  * @constructor
16422  * Create a new Container
16423  * @param {Object} config The config object
16424  */
16425
16426
16427
16428 Roo.bootstrap.Calendar = function(config){
16429     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16430      this.addEvents({
16431         /**
16432              * @event select
16433              * Fires when a date is selected
16434              * @param {DatePicker} this
16435              * @param {Date} date The selected date
16436              */
16437         'select': true,
16438         /**
16439              * @event monthchange
16440              * Fires when the displayed month changes 
16441              * @param {DatePicker} this
16442              * @param {Date} date The selected month
16443              */
16444         'monthchange': true,
16445         /**
16446              * @event evententer
16447              * Fires when mouse over an event
16448              * @param {Calendar} this
16449              * @param {event} Event
16450              */
16451         'evententer': true,
16452         /**
16453              * @event eventleave
16454              * Fires when the mouse leaves an
16455              * @param {Calendar} this
16456              * @param {event}
16457              */
16458         'eventleave': true,
16459         /**
16460              * @event eventclick
16461              * Fires when the mouse click an
16462              * @param {Calendar} this
16463              * @param {event}
16464              */
16465         'eventclick': true
16466         
16467     });
16468
16469 };
16470
16471 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16472     
16473      /**
16474      * @cfg {Number} startDay
16475      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16476      */
16477     startDay : 0,
16478     
16479     loadMask : false,
16480     
16481     header : false,
16482       
16483     getAutoCreate : function(){
16484         
16485         
16486         var fc_button = function(name, corner, style, content ) {
16487             return Roo.apply({},{
16488                 tag : 'span',
16489                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16490                          (corner.length ?
16491                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16492                             ''
16493                         ),
16494                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16495                 unselectable: 'on'
16496             });
16497         };
16498         
16499         var header = {};
16500         
16501         if(!this.header){
16502             header = {
16503                 tag : 'table',
16504                 cls : 'fc-header',
16505                 style : 'width:100%',
16506                 cn : [
16507                     {
16508                         tag: 'tr',
16509                         cn : [
16510                             {
16511                                 tag : 'td',
16512                                 cls : 'fc-header-left',
16513                                 cn : [
16514                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16515                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16516                                     { tag: 'span', cls: 'fc-header-space' },
16517                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16518
16519
16520                                 ]
16521                             },
16522
16523                             {
16524                                 tag : 'td',
16525                                 cls : 'fc-header-center',
16526                                 cn : [
16527                                     {
16528                                         tag: 'span',
16529                                         cls: 'fc-header-title',
16530                                         cn : {
16531                                             tag: 'H2',
16532                                             html : 'month / year'
16533                                         }
16534                                     }
16535
16536                                 ]
16537                             },
16538                             {
16539                                 tag : 'td',
16540                                 cls : 'fc-header-right',
16541                                 cn : [
16542                               /*      fc_button('month', 'left', '', 'month' ),
16543                                     fc_button('week', '', '', 'week' ),
16544                                     fc_button('day', 'right', '', 'day' )
16545                                 */    
16546
16547                                 ]
16548                             }
16549
16550                         ]
16551                     }
16552                 ]
16553             };
16554         }
16555         
16556         header = this.header;
16557         
16558        
16559         var cal_heads = function() {
16560             var ret = [];
16561             // fixme - handle this.
16562             
16563             for (var i =0; i < Date.dayNames.length; i++) {
16564                 var d = Date.dayNames[i];
16565                 ret.push({
16566                     tag: 'th',
16567                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16568                     html : d.substring(0,3)
16569                 });
16570                 
16571             }
16572             ret[0].cls += ' fc-first';
16573             ret[6].cls += ' fc-last';
16574             return ret;
16575         };
16576         var cal_cell = function(n) {
16577             return  {
16578                 tag: 'td',
16579                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16580                 cn : [
16581                     {
16582                         cn : [
16583                             {
16584                                 cls: 'fc-day-number',
16585                                 html: 'D'
16586                             },
16587                             {
16588                                 cls: 'fc-day-content',
16589                              
16590                                 cn : [
16591                                      {
16592                                         style: 'position: relative;' // height: 17px;
16593                                     }
16594                                 ]
16595                             }
16596                             
16597                             
16598                         ]
16599                     }
16600                 ]
16601                 
16602             }
16603         };
16604         var cal_rows = function() {
16605             
16606             var ret = [];
16607             for (var r = 0; r < 6; r++) {
16608                 var row= {
16609                     tag : 'tr',
16610                     cls : 'fc-week',
16611                     cn : []
16612                 };
16613                 
16614                 for (var i =0; i < Date.dayNames.length; i++) {
16615                     var d = Date.dayNames[i];
16616                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16617
16618                 }
16619                 row.cn[0].cls+=' fc-first';
16620                 row.cn[0].cn[0].style = 'min-height:90px';
16621                 row.cn[6].cls+=' fc-last';
16622                 ret.push(row);
16623                 
16624             }
16625             ret[0].cls += ' fc-first';
16626             ret[4].cls += ' fc-prev-last';
16627             ret[5].cls += ' fc-last';
16628             return ret;
16629             
16630         };
16631         
16632         var cal_table = {
16633             tag: 'table',
16634             cls: 'fc-border-separate',
16635             style : 'width:100%',
16636             cellspacing  : 0,
16637             cn : [
16638                 { 
16639                     tag: 'thead',
16640                     cn : [
16641                         { 
16642                             tag: 'tr',
16643                             cls : 'fc-first fc-last',
16644                             cn : cal_heads()
16645                         }
16646                     ]
16647                 },
16648                 { 
16649                     tag: 'tbody',
16650                     cn : cal_rows()
16651                 }
16652                   
16653             ]
16654         };
16655          
16656          var cfg = {
16657             cls : 'fc fc-ltr',
16658             cn : [
16659                 header,
16660                 {
16661                     cls : 'fc-content',
16662                     style : "position: relative;",
16663                     cn : [
16664                         {
16665                             cls : 'fc-view fc-view-month fc-grid',
16666                             style : 'position: relative',
16667                             unselectable : 'on',
16668                             cn : [
16669                                 {
16670                                     cls : 'fc-event-container',
16671                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16672                                 },
16673                                 cal_table
16674                             ]
16675                         }
16676                     ]
16677     
16678                 }
16679            ] 
16680             
16681         };
16682         
16683          
16684         
16685         return cfg;
16686     },
16687     
16688     
16689     initEvents : function()
16690     {
16691         if(!this.store){
16692             throw "can not find store for calendar";
16693         }
16694         
16695         var mark = {
16696             tag: "div",
16697             cls:"x-dlg-mask",
16698             style: "text-align:center",
16699             cn: [
16700                 {
16701                     tag: "div",
16702                     style: "background-color:white;width:50%;margin:250 auto",
16703                     cn: [
16704                         {
16705                             tag: "img",
16706                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16707                         },
16708                         {
16709                             tag: "span",
16710                             html: "Loading"
16711                         }
16712                         
16713                     ]
16714                 }
16715             ]
16716         };
16717         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16718         
16719         var size = this.el.select('.fc-content', true).first().getSize();
16720         this.maskEl.setSize(size.width, size.height);
16721         this.maskEl.enableDisplayMode("block");
16722         if(!this.loadMask){
16723             this.maskEl.hide();
16724         }
16725         
16726         this.store = Roo.factory(this.store, Roo.data);
16727         this.store.on('load', this.onLoad, this);
16728         this.store.on('beforeload', this.onBeforeLoad, this);
16729         
16730         this.resize();
16731         
16732         this.cells = this.el.select('.fc-day',true);
16733         //Roo.log(this.cells);
16734         this.textNodes = this.el.query('.fc-day-number');
16735         this.cells.addClassOnOver('fc-state-hover');
16736         
16737         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16738         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16739         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16740         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16741         
16742         this.on('monthchange', this.onMonthChange, this);
16743         
16744         this.update(new Date().clearTime());
16745     },
16746     
16747     resize : function() {
16748         var sz  = this.el.getSize();
16749         
16750         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16751         this.el.select('.fc-day-content div',true).setHeight(34);
16752     },
16753     
16754     
16755     // private
16756     showPrevMonth : function(e){
16757         this.update(this.activeDate.add("mo", -1));
16758     },
16759     showToday : function(e){
16760         this.update(new Date().clearTime());
16761     },
16762     // private
16763     showNextMonth : function(e){
16764         this.update(this.activeDate.add("mo", 1));
16765     },
16766
16767     // private
16768     showPrevYear : function(){
16769         this.update(this.activeDate.add("y", -1));
16770     },
16771
16772     // private
16773     showNextYear : function(){
16774         this.update(this.activeDate.add("y", 1));
16775     },
16776
16777     
16778    // private
16779     update : function(date)
16780     {
16781         var vd = this.activeDate;
16782         this.activeDate = date;
16783 //        if(vd && this.el){
16784 //            var t = date.getTime();
16785 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16786 //                Roo.log('using add remove');
16787 //                
16788 //                this.fireEvent('monthchange', this, date);
16789 //                
16790 //                this.cells.removeClass("fc-state-highlight");
16791 //                this.cells.each(function(c){
16792 //                   if(c.dateValue == t){
16793 //                       c.addClass("fc-state-highlight");
16794 //                       setTimeout(function(){
16795 //                            try{c.dom.firstChild.focus();}catch(e){}
16796 //                       }, 50);
16797 //                       return false;
16798 //                   }
16799 //                   return true;
16800 //                });
16801 //                return;
16802 //            }
16803 //        }
16804         
16805         var days = date.getDaysInMonth();
16806         
16807         var firstOfMonth = date.getFirstDateOfMonth();
16808         var startingPos = firstOfMonth.getDay()-this.startDay;
16809         
16810         if(startingPos < this.startDay){
16811             startingPos += 7;
16812         }
16813         
16814         var pm = date.add(Date.MONTH, -1);
16815         var prevStart = pm.getDaysInMonth()-startingPos;
16816 //        
16817         this.cells = this.el.select('.fc-day',true);
16818         this.textNodes = this.el.query('.fc-day-number');
16819         this.cells.addClassOnOver('fc-state-hover');
16820         
16821         var cells = this.cells.elements;
16822         var textEls = this.textNodes;
16823         
16824         Roo.each(cells, function(cell){
16825             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16826         });
16827         
16828         days += startingPos;
16829
16830         // convert everything to numbers so it's fast
16831         var day = 86400000;
16832         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16833         //Roo.log(d);
16834         //Roo.log(pm);
16835         //Roo.log(prevStart);
16836         
16837         var today = new Date().clearTime().getTime();
16838         var sel = date.clearTime().getTime();
16839         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16840         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16841         var ddMatch = this.disabledDatesRE;
16842         var ddText = this.disabledDatesText;
16843         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16844         var ddaysText = this.disabledDaysText;
16845         var format = this.format;
16846         
16847         var setCellClass = function(cal, cell){
16848             cell.row = 0;
16849             cell.events = [];
16850             cell.more = [];
16851             //Roo.log('set Cell Class');
16852             cell.title = "";
16853             var t = d.getTime();
16854             
16855             //Roo.log(d);
16856             
16857             cell.dateValue = t;
16858             if(t == today){
16859                 cell.className += " fc-today";
16860                 cell.className += " fc-state-highlight";
16861                 cell.title = cal.todayText;
16862             }
16863             if(t == sel){
16864                 // disable highlight in other month..
16865                 //cell.className += " fc-state-highlight";
16866                 
16867             }
16868             // disabling
16869             if(t < min) {
16870                 cell.className = " fc-state-disabled";
16871                 cell.title = cal.minText;
16872                 return;
16873             }
16874             if(t > max) {
16875                 cell.className = " fc-state-disabled";
16876                 cell.title = cal.maxText;
16877                 return;
16878             }
16879             if(ddays){
16880                 if(ddays.indexOf(d.getDay()) != -1){
16881                     cell.title = ddaysText;
16882                     cell.className = " fc-state-disabled";
16883                 }
16884             }
16885             if(ddMatch && format){
16886                 var fvalue = d.dateFormat(format);
16887                 if(ddMatch.test(fvalue)){
16888                     cell.title = ddText.replace("%0", fvalue);
16889                     cell.className = " fc-state-disabled";
16890                 }
16891             }
16892             
16893             if (!cell.initialClassName) {
16894                 cell.initialClassName = cell.dom.className;
16895             }
16896             
16897             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16898         };
16899
16900         var i = 0;
16901         
16902         for(; i < startingPos; i++) {
16903             textEls[i].innerHTML = (++prevStart);
16904             d.setDate(d.getDate()+1);
16905             
16906             cells[i].className = "fc-past fc-other-month";
16907             setCellClass(this, cells[i]);
16908         }
16909         
16910         var intDay = 0;
16911         
16912         for(; i < days; i++){
16913             intDay = i - startingPos + 1;
16914             textEls[i].innerHTML = (intDay);
16915             d.setDate(d.getDate()+1);
16916             
16917             cells[i].className = ''; // "x-date-active";
16918             setCellClass(this, cells[i]);
16919         }
16920         var extraDays = 0;
16921         
16922         for(; i < 42; i++) {
16923             textEls[i].innerHTML = (++extraDays);
16924             d.setDate(d.getDate()+1);
16925             
16926             cells[i].className = "fc-future fc-other-month";
16927             setCellClass(this, cells[i]);
16928         }
16929         
16930         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16931         
16932         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16933         
16934         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16935         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16936         
16937         if(totalRows != 6){
16938             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16939             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16940         }
16941         
16942         this.fireEvent('monthchange', this, date);
16943         
16944         
16945         /*
16946         if(!this.internalRender){
16947             var main = this.el.dom.firstChild;
16948             var w = main.offsetWidth;
16949             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16950             Roo.fly(main).setWidth(w);
16951             this.internalRender = true;
16952             // opera does not respect the auto grow header center column
16953             // then, after it gets a width opera refuses to recalculate
16954             // without a second pass
16955             if(Roo.isOpera && !this.secondPass){
16956                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16957                 this.secondPass = true;
16958                 this.update.defer(10, this, [date]);
16959             }
16960         }
16961         */
16962         
16963     },
16964     
16965     findCell : function(dt) {
16966         dt = dt.clearTime().getTime();
16967         var ret = false;
16968         this.cells.each(function(c){
16969             //Roo.log("check " +c.dateValue + '?=' + dt);
16970             if(c.dateValue == dt){
16971                 ret = c;
16972                 return false;
16973             }
16974             return true;
16975         });
16976         
16977         return ret;
16978     },
16979     
16980     findCells : function(ev) {
16981         var s = ev.start.clone().clearTime().getTime();
16982        // Roo.log(s);
16983         var e= ev.end.clone().clearTime().getTime();
16984        // Roo.log(e);
16985         var ret = [];
16986         this.cells.each(function(c){
16987              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16988             
16989             if(c.dateValue > e){
16990                 return ;
16991             }
16992             if(c.dateValue < s){
16993                 return ;
16994             }
16995             ret.push(c);
16996         });
16997         
16998         return ret;    
16999     },
17000     
17001 //    findBestRow: function(cells)
17002 //    {
17003 //        var ret = 0;
17004 //        
17005 //        for (var i =0 ; i < cells.length;i++) {
17006 //            ret  = Math.max(cells[i].rows || 0,ret);
17007 //        }
17008 //        return ret;
17009 //        
17010 //    },
17011     
17012     
17013     addItem : function(ev)
17014     {
17015         // look for vertical location slot in
17016         var cells = this.findCells(ev);
17017         
17018 //        ev.row = this.findBestRow(cells);
17019         
17020         // work out the location.
17021         
17022         var crow = false;
17023         var rows = [];
17024         for(var i =0; i < cells.length; i++) {
17025             
17026             cells[i].row = cells[0].row;
17027             
17028             if(i == 0){
17029                 cells[i].row = cells[i].row + 1;
17030             }
17031             
17032             if (!crow) {
17033                 crow = {
17034                     start : cells[i],
17035                     end :  cells[i]
17036                 };
17037                 continue;
17038             }
17039             if (crow.start.getY() == cells[i].getY()) {
17040                 // on same row.
17041                 crow.end = cells[i];
17042                 continue;
17043             }
17044             // different row.
17045             rows.push(crow);
17046             crow = {
17047                 start: cells[i],
17048                 end : cells[i]
17049             };
17050             
17051         }
17052         
17053         rows.push(crow);
17054         ev.els = [];
17055         ev.rows = rows;
17056         ev.cells = cells;
17057         
17058         cells[0].events.push(ev);
17059         
17060         this.calevents.push(ev);
17061     },
17062     
17063     clearEvents: function() {
17064         
17065         if(!this.calevents){
17066             return;
17067         }
17068         
17069         Roo.each(this.cells.elements, function(c){
17070             c.row = 0;
17071             c.events = [];
17072             c.more = [];
17073         });
17074         
17075         Roo.each(this.calevents, function(e) {
17076             Roo.each(e.els, function(el) {
17077                 el.un('mouseenter' ,this.onEventEnter, this);
17078                 el.un('mouseleave' ,this.onEventLeave, this);
17079                 el.remove();
17080             },this);
17081         },this);
17082         
17083         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17084             e.remove();
17085         });
17086         
17087     },
17088     
17089     renderEvents: function()
17090     {   
17091         var _this = this;
17092         
17093         this.cells.each(function(c) {
17094             
17095             if(c.row < 5){
17096                 return;
17097             }
17098             
17099             var ev = c.events;
17100             
17101             var r = 4;
17102             if(c.row != c.events.length){
17103                 r = 4 - (4 - (c.row - c.events.length));
17104             }
17105             
17106             c.events = ev.slice(0, r);
17107             c.more = ev.slice(r);
17108             
17109             if(c.more.length && c.more.length == 1){
17110                 c.events.push(c.more.pop());
17111             }
17112             
17113             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17114             
17115         });
17116             
17117         this.cells.each(function(c) {
17118             
17119             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17120             
17121             
17122             for (var e = 0; e < c.events.length; e++){
17123                 var ev = c.events[e];
17124                 var rows = ev.rows;
17125                 
17126                 for(var i = 0; i < rows.length; i++) {
17127                 
17128                     // how many rows should it span..
17129
17130                     var  cfg = {
17131                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17132                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17133
17134                         unselectable : "on",
17135                         cn : [
17136                             {
17137                                 cls: 'fc-event-inner',
17138                                 cn : [
17139     //                                {
17140     //                                  tag:'span',
17141     //                                  cls: 'fc-event-time',
17142     //                                  html : cells.length > 1 ? '' : ev.time
17143     //                                },
17144                                     {
17145                                       tag:'span',
17146                                       cls: 'fc-event-title',
17147                                       html : String.format('{0}', ev.title)
17148                                     }
17149
17150
17151                                 ]
17152                             },
17153                             {
17154                                 cls: 'ui-resizable-handle ui-resizable-e',
17155                                 html : '&nbsp;&nbsp;&nbsp'
17156                             }
17157
17158                         ]
17159                     };
17160
17161                     if (i == 0) {
17162                         cfg.cls += ' fc-event-start';
17163                     }
17164                     if ((i+1) == rows.length) {
17165                         cfg.cls += ' fc-event-end';
17166                     }
17167
17168                     var ctr = _this.el.select('.fc-event-container',true).first();
17169                     var cg = ctr.createChild(cfg);
17170
17171                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17172                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17173
17174                     var r = (c.more.length) ? 1 : 0;
17175                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17176                     cg.setWidth(ebox.right - sbox.x -2);
17177
17178                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17179                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17180                     cg.on('click', _this.onEventClick, _this, ev);
17181
17182                     ev.els.push(cg);
17183                     
17184                 }
17185                 
17186             }
17187             
17188             
17189             if(c.more.length){
17190                 var  cfg = {
17191                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17192                     style : 'position: absolute',
17193                     unselectable : "on",
17194                     cn : [
17195                         {
17196                             cls: 'fc-event-inner',
17197                             cn : [
17198                                 {
17199                                   tag:'span',
17200                                   cls: 'fc-event-title',
17201                                   html : 'More'
17202                                 }
17203
17204
17205                             ]
17206                         },
17207                         {
17208                             cls: 'ui-resizable-handle ui-resizable-e',
17209                             html : '&nbsp;&nbsp;&nbsp'
17210                         }
17211
17212                     ]
17213                 };
17214
17215                 var ctr = _this.el.select('.fc-event-container',true).first();
17216                 var cg = ctr.createChild(cfg);
17217
17218                 var sbox = c.select('.fc-day-content',true).first().getBox();
17219                 var ebox = c.select('.fc-day-content',true).first().getBox();
17220                 //Roo.log(cg);
17221                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17222                 cg.setWidth(ebox.right - sbox.x -2);
17223
17224                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17225                 
17226             }
17227             
17228         });
17229         
17230         
17231         
17232     },
17233     
17234     onEventEnter: function (e, el,event,d) {
17235         this.fireEvent('evententer', this, el, event);
17236     },
17237     
17238     onEventLeave: function (e, el,event,d) {
17239         this.fireEvent('eventleave', this, el, event);
17240     },
17241     
17242     onEventClick: function (e, el,event,d) {
17243         this.fireEvent('eventclick', this, el, event);
17244     },
17245     
17246     onMonthChange: function () {
17247         this.store.load();
17248     },
17249     
17250     onMoreEventClick: function(e, el, more)
17251     {
17252         var _this = this;
17253         
17254         this.calpopover.placement = 'right';
17255         this.calpopover.setTitle('More');
17256         
17257         this.calpopover.setContent('');
17258         
17259         var ctr = this.calpopover.el.select('.popover-content', true).first();
17260         
17261         Roo.each(more, function(m){
17262             var cfg = {
17263                 cls : 'fc-event-hori fc-event-draggable',
17264                 html : m.title
17265             };
17266             var cg = ctr.createChild(cfg);
17267             
17268             cg.on('click', _this.onEventClick, _this, m);
17269         });
17270         
17271         this.calpopover.show(el);
17272         
17273         
17274     },
17275     
17276     onLoad: function () 
17277     {   
17278         this.calevents = [];
17279         var cal = this;
17280         
17281         if(this.store.getCount() > 0){
17282             this.store.data.each(function(d){
17283                cal.addItem({
17284                     id : d.data.id,
17285                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17286                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17287                     time : d.data.start_time,
17288                     title : d.data.title,
17289                     description : d.data.description,
17290                     venue : d.data.venue
17291                 });
17292             });
17293         }
17294         
17295         this.renderEvents();
17296         
17297         if(this.calevents.length && this.loadMask){
17298             this.maskEl.hide();
17299         }
17300     },
17301     
17302     onBeforeLoad: function()
17303     {
17304         this.clearEvents();
17305         if(this.loadMask){
17306             this.maskEl.show();
17307         }
17308     }
17309 });
17310
17311  
17312  /*
17313  * - LGPL
17314  *
17315  * element
17316  * 
17317  */
17318
17319 /**
17320  * @class Roo.bootstrap.Popover
17321  * @extends Roo.bootstrap.Component
17322  * Bootstrap Popover class
17323  * @cfg {String} html contents of the popover   (or false to use children..)
17324  * @cfg {String} title of popover (or false to hide)
17325  * @cfg {String} placement how it is placed
17326  * @cfg {String} trigger click || hover (or false to trigger manually)
17327  * @cfg {String} over what (parent or false to trigger manually.)
17328  * @cfg {Number} delay - delay before showing
17329  
17330  * @constructor
17331  * Create a new Popover
17332  * @param {Object} config The config object
17333  */
17334
17335 Roo.bootstrap.Popover = function(config){
17336     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17337     
17338     this.addEvents({
17339         // raw events
17340          /**
17341          * @event show
17342          * After the popover show
17343          * 
17344          * @param {Roo.bootstrap.Popover} this
17345          */
17346         "show" : true,
17347         /**
17348          * @event hide
17349          * After the popover hide
17350          * 
17351          * @param {Roo.bootstrap.Popover} this
17352          */
17353         "hide" : true
17354     });
17355 };
17356
17357 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17358     
17359     title: 'Fill in a title',
17360     html: false,
17361     
17362     placement : 'right',
17363     trigger : 'hover', // hover
17364     
17365     delay : 0,
17366     
17367     over: 'parent',
17368     
17369     can_build_overlaid : false,
17370     
17371     getChildContainer : function()
17372     {
17373         return this.el.select('.popover-content',true).first();
17374     },
17375     
17376     getAutoCreate : function(){
17377          
17378         var cfg = {
17379            cls : 'popover roo-dynamic',
17380            style: 'display:block',
17381            cn : [
17382                 {
17383                     cls : 'arrow'
17384                 },
17385                 {
17386                     cls : 'popover-inner',
17387                     cn : [
17388                         {
17389                             tag: 'h3',
17390                             cls: 'popover-title',
17391                             html : this.title
17392                         },
17393                         {
17394                             cls : 'popover-content',
17395                             html : this.html
17396                         }
17397                     ]
17398                     
17399                 }
17400            ]
17401         };
17402         
17403         return cfg;
17404     },
17405     setTitle: function(str)
17406     {
17407         this.title = str;
17408         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17409     },
17410     setContent: function(str)
17411     {
17412         this.html = str;
17413         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17414     },
17415     // as it get's added to the bottom of the page.
17416     onRender : function(ct, position)
17417     {
17418         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17419         if(!this.el){
17420             var cfg = Roo.apply({},  this.getAutoCreate());
17421             cfg.id = Roo.id();
17422             
17423             if (this.cls) {
17424                 cfg.cls += ' ' + this.cls;
17425             }
17426             if (this.style) {
17427                 cfg.style = this.style;
17428             }
17429             //Roo.log("adding to ");
17430             this.el = Roo.get(document.body).createChild(cfg, position);
17431 //            Roo.log(this.el);
17432         }
17433         this.initEvents();
17434     },
17435     
17436     initEvents : function()
17437     {
17438         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17439         this.el.enableDisplayMode('block');
17440         this.el.hide();
17441         if (this.over === false) {
17442             return; 
17443         }
17444         if (this.triggers === false) {
17445             return;
17446         }
17447         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17448         var triggers = this.trigger ? this.trigger.split(' ') : [];
17449         Roo.each(triggers, function(trigger) {
17450         
17451             if (trigger == 'click') {
17452                 on_el.on('click', this.toggle, this);
17453             } else if (trigger != 'manual') {
17454                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17455                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17456       
17457                 on_el.on(eventIn  ,this.enter, this);
17458                 on_el.on(eventOut, this.leave, this);
17459             }
17460         }, this);
17461         
17462     },
17463     
17464     
17465     // private
17466     timeout : null,
17467     hoverState : null,
17468     
17469     toggle : function () {
17470         this.hoverState == 'in' ? this.leave() : this.enter();
17471     },
17472     
17473     enter : function () {
17474         
17475         clearTimeout(this.timeout);
17476     
17477         this.hoverState = 'in';
17478     
17479         if (!this.delay || !this.delay.show) {
17480             this.show();
17481             return;
17482         }
17483         var _t = this;
17484         this.timeout = setTimeout(function () {
17485             if (_t.hoverState == 'in') {
17486                 _t.show();
17487             }
17488         }, this.delay.show)
17489     },
17490     
17491     leave : function() {
17492         clearTimeout(this.timeout);
17493     
17494         this.hoverState = 'out';
17495     
17496         if (!this.delay || !this.delay.hide) {
17497             this.hide();
17498             return;
17499         }
17500         var _t = this;
17501         this.timeout = setTimeout(function () {
17502             if (_t.hoverState == 'out') {
17503                 _t.hide();
17504             }
17505         }, this.delay.hide)
17506     },
17507     
17508     show : function (on_el)
17509     {
17510         if (!on_el) {
17511             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17512         }
17513         
17514         // set content.
17515         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17516         if (this.html !== false) {
17517             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17518         }
17519         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17520         if (!this.title.length) {
17521             this.el.select('.popover-title',true).hide();
17522         }
17523         
17524         var placement = typeof this.placement == 'function' ?
17525             this.placement.call(this, this.el, on_el) :
17526             this.placement;
17527             
17528         var autoToken = /\s?auto?\s?/i;
17529         var autoPlace = autoToken.test(placement);
17530         if (autoPlace) {
17531             placement = placement.replace(autoToken, '') || 'top';
17532         }
17533         
17534         //this.el.detach()
17535         //this.el.setXY([0,0]);
17536         this.el.show();
17537         this.el.dom.style.display='block';
17538         this.el.addClass(placement);
17539         
17540         //this.el.appendTo(on_el);
17541         
17542         var p = this.getPosition();
17543         var box = this.el.getBox();
17544         
17545         if (autoPlace) {
17546             // fixme..
17547         }
17548         var align = Roo.bootstrap.Popover.alignment[placement];
17549         
17550 //        Roo.log(align);
17551         this.el.alignTo(on_el, align[0],align[1]);
17552         //var arrow = this.el.select('.arrow',true).first();
17553         //arrow.set(align[2], 
17554         
17555         this.el.addClass('in');
17556         
17557         
17558         if (this.el.hasClass('fade')) {
17559             // fade it?
17560         }
17561         
17562         this.hoverState = 'in';
17563         
17564         this.fireEvent('show', this);
17565         
17566     },
17567     hide : function()
17568     {
17569         this.el.setXY([0,0]);
17570         this.el.removeClass('in');
17571         this.el.hide();
17572         this.hoverState = null;
17573         
17574         this.fireEvent('hide', this);
17575     }
17576     
17577 });
17578
17579 Roo.bootstrap.Popover.alignment = {
17580     'left' : ['r-l', [-10,0], 'right'],
17581     'right' : ['l-r', [10,0], 'left'],
17582     'bottom' : ['t-b', [0,10], 'top'],
17583     'top' : [ 'b-t', [0,-10], 'bottom']
17584 };
17585
17586  /*
17587  * - LGPL
17588  *
17589  * Progress
17590  * 
17591  */
17592
17593 /**
17594  * @class Roo.bootstrap.Progress
17595  * @extends Roo.bootstrap.Component
17596  * Bootstrap Progress class
17597  * @cfg {Boolean} striped striped of the progress bar
17598  * @cfg {Boolean} active animated of the progress bar
17599  * 
17600  * 
17601  * @constructor
17602  * Create a new Progress
17603  * @param {Object} config The config object
17604  */
17605
17606 Roo.bootstrap.Progress = function(config){
17607     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17608 };
17609
17610 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17611     
17612     striped : false,
17613     active: false,
17614     
17615     getAutoCreate : function(){
17616         var cfg = {
17617             tag: 'div',
17618             cls: 'progress'
17619         };
17620         
17621         
17622         if(this.striped){
17623             cfg.cls += ' progress-striped';
17624         }
17625       
17626         if(this.active){
17627             cfg.cls += ' active';
17628         }
17629         
17630         
17631         return cfg;
17632     }
17633    
17634 });
17635
17636  
17637
17638  /*
17639  * - LGPL
17640  *
17641  * ProgressBar
17642  * 
17643  */
17644
17645 /**
17646  * @class Roo.bootstrap.ProgressBar
17647  * @extends Roo.bootstrap.Component
17648  * Bootstrap ProgressBar class
17649  * @cfg {Number} aria_valuenow aria-value now
17650  * @cfg {Number} aria_valuemin aria-value min
17651  * @cfg {Number} aria_valuemax aria-value max
17652  * @cfg {String} label label for the progress bar
17653  * @cfg {String} panel (success | info | warning | danger )
17654  * @cfg {String} role role of the progress bar
17655  * @cfg {String} sr_only text
17656  * 
17657  * 
17658  * @constructor
17659  * Create a new ProgressBar
17660  * @param {Object} config The config object
17661  */
17662
17663 Roo.bootstrap.ProgressBar = function(config){
17664     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17665 };
17666
17667 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17668     
17669     aria_valuenow : 0,
17670     aria_valuemin : 0,
17671     aria_valuemax : 100,
17672     label : false,
17673     panel : false,
17674     role : false,
17675     sr_only: false,
17676     
17677     getAutoCreate : function()
17678     {
17679         
17680         var cfg = {
17681             tag: 'div',
17682             cls: 'progress-bar',
17683             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17684         };
17685         
17686         if(this.sr_only){
17687             cfg.cn = {
17688                 tag: 'span',
17689                 cls: 'sr-only',
17690                 html: this.sr_only
17691             }
17692         }
17693         
17694         if(this.role){
17695             cfg.role = this.role;
17696         }
17697         
17698         if(this.aria_valuenow){
17699             cfg['aria-valuenow'] = this.aria_valuenow;
17700         }
17701         
17702         if(this.aria_valuemin){
17703             cfg['aria-valuemin'] = this.aria_valuemin;
17704         }
17705         
17706         if(this.aria_valuemax){
17707             cfg['aria-valuemax'] = this.aria_valuemax;
17708         }
17709         
17710         if(this.label && !this.sr_only){
17711             cfg.html = this.label;
17712         }
17713         
17714         if(this.panel){
17715             cfg.cls += ' progress-bar-' + this.panel;
17716         }
17717         
17718         return cfg;
17719     },
17720     
17721     update : function(aria_valuenow)
17722     {
17723         this.aria_valuenow = aria_valuenow;
17724         
17725         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17726     }
17727    
17728 });
17729
17730  
17731
17732  /*
17733  * - LGPL
17734  *
17735  * column
17736  * 
17737  */
17738
17739 /**
17740  * @class Roo.bootstrap.TabGroup
17741  * @extends Roo.bootstrap.Column
17742  * Bootstrap Column class
17743  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17744  * @cfg {Boolean} carousel true to make the group behave like a carousel
17745  * @cfg {Boolean} bullets show bullets for the panels
17746  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17747  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17748  * @cfg {Boolean} showarrow (true|false) show arrow default true
17749  * 
17750  * @constructor
17751  * Create a new TabGroup
17752  * @param {Object} config The config object
17753  */
17754
17755 Roo.bootstrap.TabGroup = function(config){
17756     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17757     if (!this.navId) {
17758         this.navId = Roo.id();
17759     }
17760     this.tabs = [];
17761     Roo.bootstrap.TabGroup.register(this);
17762     
17763 };
17764
17765 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17766     
17767     carousel : false,
17768     transition : false,
17769     bullets : 0,
17770     timer : 0,
17771     autoslide : false,
17772     slideFn : false,
17773     slideOnTouch : false,
17774     showarrow : true,
17775     
17776     getAutoCreate : function()
17777     {
17778         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17779         
17780         cfg.cls += ' tab-content';
17781         
17782         if (this.carousel) {
17783             cfg.cls += ' carousel slide';
17784             
17785             cfg.cn = [{
17786                cls : 'carousel-inner',
17787                cn : []
17788             }];
17789         
17790             if(this.bullets  && !Roo.isTouch){
17791                 
17792                 var bullets = {
17793                     cls : 'carousel-bullets',
17794                     cn : []
17795                 };
17796                
17797                 if(this.bullets_cls){
17798                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17799                 }
17800                 
17801                 bullets.cn.push({
17802                     cls : 'clear'
17803                 });
17804                 
17805                 cfg.cn[0].cn.push(bullets);
17806             }
17807             
17808             if(this.showarrow){
17809                 cfg.cn[0].cn.push({
17810                     tag : 'div',
17811                     class : 'carousel-arrow',
17812                     cn : [
17813                         {
17814                             tag : 'div',
17815                             class : 'carousel-prev',
17816                             cn : [
17817                                 {
17818                                     tag : 'i',
17819                                     class : 'fa fa-chevron-left'
17820                                 }
17821                             ]
17822                         },
17823                         {
17824                             tag : 'div',
17825                             class : 'carousel-next',
17826                             cn : [
17827                                 {
17828                                     tag : 'i',
17829                                     class : 'fa fa-chevron-right'
17830                                 }
17831                             ]
17832                         }
17833                     ]
17834                 });
17835             }
17836             
17837         }
17838         
17839         return cfg;
17840     },
17841     
17842     initEvents:  function()
17843     {
17844 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17845 //            this.el.on("touchstart", this.onTouchStart, this);
17846 //        }
17847         
17848         if(this.autoslide){
17849             var _this = this;
17850             
17851             this.slideFn = window.setInterval(function() {
17852                 _this.showPanelNext();
17853             }, this.timer);
17854         }
17855         
17856         if(this.showarrow){
17857             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17858             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17859         }
17860         
17861         
17862     },
17863     
17864 //    onTouchStart : function(e, el, o)
17865 //    {
17866 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17867 //            return;
17868 //        }
17869 //        
17870 //        this.showPanelNext();
17871 //    },
17872     
17873     
17874     getChildContainer : function()
17875     {
17876         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17877     },
17878     
17879     /**
17880     * register a Navigation item
17881     * @param {Roo.bootstrap.NavItem} the navitem to add
17882     */
17883     register : function(item)
17884     {
17885         this.tabs.push( item);
17886         item.navId = this.navId; // not really needed..
17887         this.addBullet();
17888     
17889     },
17890     
17891     getActivePanel : function()
17892     {
17893         var r = false;
17894         Roo.each(this.tabs, function(t) {
17895             if (t.active) {
17896                 r = t;
17897                 return false;
17898             }
17899             return null;
17900         });
17901         return r;
17902         
17903     },
17904     getPanelByName : function(n)
17905     {
17906         var r = false;
17907         Roo.each(this.tabs, function(t) {
17908             if (t.tabId == n) {
17909                 r = t;
17910                 return false;
17911             }
17912             return null;
17913         });
17914         return r;
17915     },
17916     indexOfPanel : function(p)
17917     {
17918         var r = false;
17919         Roo.each(this.tabs, function(t,i) {
17920             if (t.tabId == p.tabId) {
17921                 r = i;
17922                 return false;
17923             }
17924             return null;
17925         });
17926         return r;
17927     },
17928     /**
17929      * show a specific panel
17930      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17931      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17932      */
17933     showPanel : function (pan)
17934     {
17935         if(this.transition || typeof(pan) == 'undefined'){
17936             Roo.log("waiting for the transitionend");
17937             return;
17938         }
17939         
17940         if (typeof(pan) == 'number') {
17941             pan = this.tabs[pan];
17942         }
17943         
17944         if (typeof(pan) == 'string') {
17945             pan = this.getPanelByName(pan);
17946         }
17947         
17948         var cur = this.getActivePanel();
17949         
17950         if(!pan || !cur){
17951             Roo.log('pan or acitve pan is undefined');
17952             return false;
17953         }
17954         
17955         if (pan.tabId == this.getActivePanel().tabId) {
17956             return true;
17957         }
17958         
17959         if (false === cur.fireEvent('beforedeactivate')) {
17960             return false;
17961         }
17962         
17963         if(this.bullets > 0 && !Roo.isTouch){
17964             this.setActiveBullet(this.indexOfPanel(pan));
17965         }
17966         
17967         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17968             
17969             this.transition = true;
17970             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17971             var lr = dir == 'next' ? 'left' : 'right';
17972             pan.el.addClass(dir); // or prev
17973             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17974             cur.el.addClass(lr); // or right
17975             pan.el.addClass(lr);
17976             
17977             var _this = this;
17978             cur.el.on('transitionend', function() {
17979                 Roo.log("trans end?");
17980                 
17981                 pan.el.removeClass([lr,dir]);
17982                 pan.setActive(true);
17983                 
17984                 cur.el.removeClass([lr]);
17985                 cur.setActive(false);
17986                 
17987                 _this.transition = false;
17988                 
17989             }, this, { single:  true } );
17990             
17991             return true;
17992         }
17993         
17994         cur.setActive(false);
17995         pan.setActive(true);
17996         
17997         return true;
17998         
17999     },
18000     showPanelNext : function()
18001     {
18002         var i = this.indexOfPanel(this.getActivePanel());
18003         
18004         if (i >= this.tabs.length - 1 && !this.autoslide) {
18005             return;
18006         }
18007         
18008         if (i >= this.tabs.length - 1 && this.autoslide) {
18009             i = -1;
18010         }
18011         
18012         this.showPanel(this.tabs[i+1]);
18013     },
18014     
18015     showPanelPrev : function()
18016     {
18017         var i = this.indexOfPanel(this.getActivePanel());
18018         
18019         if (i  < 1 && !this.autoslide) {
18020             return;
18021         }
18022         
18023         if (i < 1 && this.autoslide) {
18024             i = this.tabs.length;
18025         }
18026         
18027         this.showPanel(this.tabs[i-1]);
18028     },
18029     
18030     
18031     addBullet: function()
18032     {
18033         if(!this.bullets || Roo.isTouch){
18034             return;
18035         }
18036         var ctr = this.el.select('.carousel-bullets',true).first();
18037         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18038         var bullet = ctr.createChild({
18039             cls : 'bullet bullet-' + i
18040         },ctr.dom.lastChild);
18041         
18042         
18043         var _this = this;
18044         
18045         bullet.on('click', (function(e, el, o, ii, t){
18046
18047             e.preventDefault();
18048
18049             this.showPanel(ii);
18050
18051             if(this.autoslide && this.slideFn){
18052                 clearInterval(this.slideFn);
18053                 this.slideFn = window.setInterval(function() {
18054                     _this.showPanelNext();
18055                 }, this.timer);
18056             }
18057
18058         }).createDelegate(this, [i, bullet], true));
18059                 
18060         
18061     },
18062      
18063     setActiveBullet : function(i)
18064     {
18065         if(Roo.isTouch){
18066             return;
18067         }
18068         
18069         Roo.each(this.el.select('.bullet', true).elements, function(el){
18070             el.removeClass('selected');
18071         });
18072
18073         var bullet = this.el.select('.bullet-' + i, true).first();
18074         
18075         if(!bullet){
18076             return;
18077         }
18078         
18079         bullet.addClass('selected');
18080     }
18081     
18082     
18083   
18084 });
18085
18086  
18087
18088  
18089  
18090 Roo.apply(Roo.bootstrap.TabGroup, {
18091     
18092     groups: {},
18093      /**
18094     * register a Navigation Group
18095     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18096     */
18097     register : function(navgrp)
18098     {
18099         this.groups[navgrp.navId] = navgrp;
18100         
18101     },
18102     /**
18103     * fetch a Navigation Group based on the navigation ID
18104     * if one does not exist , it will get created.
18105     * @param {string} the navgroup to add
18106     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18107     */
18108     get: function(navId) {
18109         if (typeof(this.groups[navId]) == 'undefined') {
18110             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18111         }
18112         return this.groups[navId] ;
18113     }
18114     
18115     
18116     
18117 });
18118
18119  /*
18120  * - LGPL
18121  *
18122  * TabPanel
18123  * 
18124  */
18125
18126 /**
18127  * @class Roo.bootstrap.TabPanel
18128  * @extends Roo.bootstrap.Component
18129  * Bootstrap TabPanel class
18130  * @cfg {Boolean} active panel active
18131  * @cfg {String} html panel content
18132  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18133  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18134  * @cfg {String} href click to link..
18135  * 
18136  * 
18137  * @constructor
18138  * Create a new TabPanel
18139  * @param {Object} config The config object
18140  */
18141
18142 Roo.bootstrap.TabPanel = function(config){
18143     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18144     this.addEvents({
18145         /**
18146              * @event changed
18147              * Fires when the active status changes
18148              * @param {Roo.bootstrap.TabPanel} this
18149              * @param {Boolean} state the new state
18150             
18151          */
18152         'changed': true,
18153         /**
18154              * @event beforedeactivate
18155              * Fires before a tab is de-activated - can be used to do validation on a form.
18156              * @param {Roo.bootstrap.TabPanel} this
18157              * @return {Boolean} false if there is an error
18158             
18159          */
18160         'beforedeactivate': true
18161      });
18162     
18163     this.tabId = this.tabId || Roo.id();
18164   
18165 };
18166
18167 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18168     
18169     active: false,
18170     html: false,
18171     tabId: false,
18172     navId : false,
18173     href : '',
18174     
18175     getAutoCreate : function(){
18176         var cfg = {
18177             tag: 'div',
18178             // item is needed for carousel - not sure if it has any effect otherwise
18179             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18180             html: this.html || ''
18181         };
18182         
18183         if(this.active){
18184             cfg.cls += ' active';
18185         }
18186         
18187         if(this.tabId){
18188             cfg.tabId = this.tabId;
18189         }
18190         
18191         
18192         return cfg;
18193     },
18194     
18195     initEvents:  function()
18196     {
18197         var p = this.parent();
18198         
18199         this.navId = this.navId || p.navId;
18200         
18201         if (typeof(this.navId) != 'undefined') {
18202             // not really needed.. but just in case.. parent should be a NavGroup.
18203             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18204             
18205             tg.register(this);
18206             
18207             var i = tg.tabs.length - 1;
18208             
18209             if(this.active && tg.bullets > 0 && i < tg.bullets){
18210                 tg.setActiveBullet(i);
18211             }
18212         }
18213         
18214         this.el.on('click', this.onClick, this);
18215         
18216         if(Roo.isTouch){
18217             this.el.on("touchstart", this.onTouchStart, this);
18218             this.el.on("touchmove", this.onTouchMove, this);
18219             this.el.on("touchend", this.onTouchEnd, this);
18220         }
18221         
18222     },
18223     
18224     onRender : function(ct, position)
18225     {
18226         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18227     },
18228     
18229     setActive : function(state)
18230     {
18231         Roo.log("panel - set active " + this.tabId + "=" + state);
18232         
18233         this.active = state;
18234         if (!state) {
18235             this.el.removeClass('active');
18236             
18237         } else  if (!this.el.hasClass('active')) {
18238             this.el.addClass('active');
18239         }
18240         
18241         this.fireEvent('changed', this, state);
18242     },
18243     
18244     onClick : function(e)
18245     {
18246         e.preventDefault();
18247         
18248         if(!this.href.length){
18249             return;
18250         }
18251         
18252         window.location.href = this.href;
18253     },
18254     
18255     startX : 0,
18256     startY : 0,
18257     endX : 0,
18258     endY : 0,
18259     swiping : false,
18260     
18261     onTouchStart : function(e)
18262     {
18263         this.swiping = false;
18264         
18265         this.startX = e.browserEvent.touches[0].clientX;
18266         this.startY = e.browserEvent.touches[0].clientY;
18267     },
18268     
18269     onTouchMove : function(e)
18270     {
18271         this.swiping = true;
18272         
18273         this.endX = e.browserEvent.touches[0].clientX;
18274         this.endY = e.browserEvent.touches[0].clientY;
18275     },
18276     
18277     onTouchEnd : function(e)
18278     {
18279         if(!this.swiping){
18280             this.onClick(e);
18281             return;
18282         }
18283         
18284         var tabGroup = this.parent();
18285         
18286         if(this.endX > this.startX){ // swiping right
18287             tabGroup.showPanelPrev();
18288             return;
18289         }
18290         
18291         if(this.startX > this.endX){ // swiping left
18292             tabGroup.showPanelNext();
18293             return;
18294         }
18295     }
18296     
18297     
18298 });
18299  
18300
18301  
18302
18303  /*
18304  * - LGPL
18305  *
18306  * DateField
18307  * 
18308  */
18309
18310 /**
18311  * @class Roo.bootstrap.DateField
18312  * @extends Roo.bootstrap.Input
18313  * Bootstrap DateField class
18314  * @cfg {Number} weekStart default 0
18315  * @cfg {String} viewMode default empty, (months|years)
18316  * @cfg {String} minViewMode default empty, (months|years)
18317  * @cfg {Number} startDate default -Infinity
18318  * @cfg {Number} endDate default Infinity
18319  * @cfg {Boolean} todayHighlight default false
18320  * @cfg {Boolean} todayBtn default false
18321  * @cfg {Boolean} calendarWeeks default false
18322  * @cfg {Object} daysOfWeekDisabled default empty
18323  * @cfg {Boolean} singleMode default false (true | false)
18324  * 
18325  * @cfg {Boolean} keyboardNavigation default true
18326  * @cfg {String} language default en
18327  * 
18328  * @constructor
18329  * Create a new DateField
18330  * @param {Object} config The config object
18331  */
18332
18333 Roo.bootstrap.DateField = function(config){
18334     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18335      this.addEvents({
18336             /**
18337              * @event show
18338              * Fires when this field show.
18339              * @param {Roo.bootstrap.DateField} this
18340              * @param {Mixed} date The date value
18341              */
18342             show : true,
18343             /**
18344              * @event show
18345              * Fires when this field hide.
18346              * @param {Roo.bootstrap.DateField} this
18347              * @param {Mixed} date The date value
18348              */
18349             hide : true,
18350             /**
18351              * @event select
18352              * Fires when select a date.
18353              * @param {Roo.bootstrap.DateField} this
18354              * @param {Mixed} date The date value
18355              */
18356             select : true,
18357             /**
18358              * @event beforeselect
18359              * Fires when before select a date.
18360              * @param {Roo.bootstrap.DateField} this
18361              * @param {Mixed} date The date value
18362              */
18363             beforeselect : true
18364         });
18365 };
18366
18367 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18368     
18369     /**
18370      * @cfg {String} format
18371      * The default date format string which can be overriden for localization support.  The format must be
18372      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18373      */
18374     format : "m/d/y",
18375     /**
18376      * @cfg {String} altFormats
18377      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18378      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18379      */
18380     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18381     
18382     weekStart : 0,
18383     
18384     viewMode : '',
18385     
18386     minViewMode : '',
18387     
18388     todayHighlight : false,
18389     
18390     todayBtn: false,
18391     
18392     language: 'en',
18393     
18394     keyboardNavigation: true,
18395     
18396     calendarWeeks: false,
18397     
18398     startDate: -Infinity,
18399     
18400     endDate: Infinity,
18401     
18402     daysOfWeekDisabled: [],
18403     
18404     _events: [],
18405     
18406     singleMode : false,
18407     
18408     UTCDate: function()
18409     {
18410         return new Date(Date.UTC.apply(Date, arguments));
18411     },
18412     
18413     UTCToday: function()
18414     {
18415         var today = new Date();
18416         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18417     },
18418     
18419     getDate: function() {
18420             var d = this.getUTCDate();
18421             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18422     },
18423     
18424     getUTCDate: function() {
18425             return this.date;
18426     },
18427     
18428     setDate: function(d) {
18429             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18430     },
18431     
18432     setUTCDate: function(d) {
18433             this.date = d;
18434             this.setValue(this.formatDate(this.date));
18435     },
18436         
18437     onRender: function(ct, position)
18438     {
18439         
18440         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18441         
18442         this.language = this.language || 'en';
18443         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18444         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18445         
18446         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18447         this.format = this.format || 'm/d/y';
18448         this.isInline = false;
18449         this.isInput = true;
18450         this.component = this.el.select('.add-on', true).first() || false;
18451         this.component = (this.component && this.component.length === 0) ? false : this.component;
18452         this.hasInput = this.component && this.inputEl().length;
18453         
18454         if (typeof(this.minViewMode === 'string')) {
18455             switch (this.minViewMode) {
18456                 case 'months':
18457                     this.minViewMode = 1;
18458                     break;
18459                 case 'years':
18460                     this.minViewMode = 2;
18461                     break;
18462                 default:
18463                     this.minViewMode = 0;
18464                     break;
18465             }
18466         }
18467         
18468         if (typeof(this.viewMode === 'string')) {
18469             switch (this.viewMode) {
18470                 case 'months':
18471                     this.viewMode = 1;
18472                     break;
18473                 case 'years':
18474                     this.viewMode = 2;
18475                     break;
18476                 default:
18477                     this.viewMode = 0;
18478                     break;
18479             }
18480         }
18481                 
18482         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18483         
18484 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18485         
18486         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18487         
18488         this.picker().on('mousedown', this.onMousedown, this);
18489         this.picker().on('click', this.onClick, this);
18490         
18491         this.picker().addClass('datepicker-dropdown');
18492         
18493         this.startViewMode = this.viewMode;
18494         
18495         if(this.singleMode){
18496             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18497                 v.setVisibilityMode(Roo.Element.DISPLAY);
18498                 v.hide();
18499             });
18500             
18501             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18502                 v.setStyle('width', '189px');
18503             });
18504         }
18505         
18506         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18507             if(!this.calendarWeeks){
18508                 v.remove();
18509                 return;
18510             }
18511             
18512             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18513             v.attr('colspan', function(i, val){
18514                 return parseInt(val) + 1;
18515             });
18516         });
18517                         
18518         
18519         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18520         
18521         this.setStartDate(this.startDate);
18522         this.setEndDate(this.endDate);
18523         
18524         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18525         
18526         this.fillDow();
18527         this.fillMonths();
18528         this.update();
18529         this.showMode();
18530         
18531         if(this.isInline) {
18532             this.show();
18533         }
18534     },
18535     
18536     picker : function()
18537     {
18538         return this.pickerEl;
18539 //        return this.el.select('.datepicker', true).first();
18540     },
18541     
18542     fillDow: function()
18543     {
18544         var dowCnt = this.weekStart;
18545         
18546         var dow = {
18547             tag: 'tr',
18548             cn: [
18549                 
18550             ]
18551         };
18552         
18553         if(this.calendarWeeks){
18554             dow.cn.push({
18555                 tag: 'th',
18556                 cls: 'cw',
18557                 html: '&nbsp;'
18558             })
18559         }
18560         
18561         while (dowCnt < this.weekStart + 7) {
18562             dow.cn.push({
18563                 tag: 'th',
18564                 cls: 'dow',
18565                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18566             });
18567         }
18568         
18569         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18570     },
18571     
18572     fillMonths: function()
18573     {    
18574         var i = 0;
18575         var months = this.picker().select('>.datepicker-months td', true).first();
18576         
18577         months.dom.innerHTML = '';
18578         
18579         while (i < 12) {
18580             var month = {
18581                 tag: 'span',
18582                 cls: 'month',
18583                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18584             };
18585             
18586             months.createChild(month);
18587         }
18588         
18589     },
18590     
18591     update: function()
18592     {
18593         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;
18594         
18595         if (this.date < this.startDate) {
18596             this.viewDate = new Date(this.startDate);
18597         } else if (this.date > this.endDate) {
18598             this.viewDate = new Date(this.endDate);
18599         } else {
18600             this.viewDate = new Date(this.date);
18601         }
18602         
18603         this.fill();
18604     },
18605     
18606     fill: function() 
18607     {
18608         var d = new Date(this.viewDate),
18609                 year = d.getUTCFullYear(),
18610                 month = d.getUTCMonth(),
18611                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18612                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18613                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18614                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18615                 currentDate = this.date && this.date.valueOf(),
18616                 today = this.UTCToday();
18617         
18618         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18619         
18620 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18621         
18622 //        this.picker.select('>tfoot th.today').
18623 //                                              .text(dates[this.language].today)
18624 //                                              .toggle(this.todayBtn !== false);
18625     
18626         this.updateNavArrows();
18627         this.fillMonths();
18628                                                 
18629         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18630         
18631         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18632          
18633         prevMonth.setUTCDate(day);
18634         
18635         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18636         
18637         var nextMonth = new Date(prevMonth);
18638         
18639         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18640         
18641         nextMonth = nextMonth.valueOf();
18642         
18643         var fillMonths = false;
18644         
18645         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18646         
18647         while(prevMonth.valueOf() < nextMonth) {
18648             var clsName = '';
18649             
18650             if (prevMonth.getUTCDay() === this.weekStart) {
18651                 if(fillMonths){
18652                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18653                 }
18654                     
18655                 fillMonths = {
18656                     tag: 'tr',
18657                     cn: []
18658                 };
18659                 
18660                 if(this.calendarWeeks){
18661                     // ISO 8601: First week contains first thursday.
18662                     // ISO also states week starts on Monday, but we can be more abstract here.
18663                     var
18664                     // Start of current week: based on weekstart/current date
18665                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18666                     // Thursday of this week
18667                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18668                     // First Thursday of year, year from thursday
18669                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18670                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18671                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18672                     
18673                     fillMonths.cn.push({
18674                         tag: 'td',
18675                         cls: 'cw',
18676                         html: calWeek
18677                     });
18678                 }
18679             }
18680             
18681             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18682                 clsName += ' old';
18683             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18684                 clsName += ' new';
18685             }
18686             if (this.todayHighlight &&
18687                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18688                 prevMonth.getUTCMonth() == today.getMonth() &&
18689                 prevMonth.getUTCDate() == today.getDate()) {
18690                 clsName += ' today';
18691             }
18692             
18693             if (currentDate && prevMonth.valueOf() === currentDate) {
18694                 clsName += ' active';
18695             }
18696             
18697             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18698                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18699                     clsName += ' disabled';
18700             }
18701             
18702             fillMonths.cn.push({
18703                 tag: 'td',
18704                 cls: 'day ' + clsName,
18705                 html: prevMonth.getDate()
18706             });
18707             
18708             prevMonth.setDate(prevMonth.getDate()+1);
18709         }
18710           
18711         var currentYear = this.date && this.date.getUTCFullYear();
18712         var currentMonth = this.date && this.date.getUTCMonth();
18713         
18714         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18715         
18716         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18717             v.removeClass('active');
18718             
18719             if(currentYear === year && k === currentMonth){
18720                 v.addClass('active');
18721             }
18722             
18723             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18724                 v.addClass('disabled');
18725             }
18726             
18727         });
18728         
18729         
18730         year = parseInt(year/10, 10) * 10;
18731         
18732         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18733         
18734         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18735         
18736         year -= 1;
18737         for (var i = -1; i < 11; i++) {
18738             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18739                 tag: 'span',
18740                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18741                 html: year
18742             });
18743             
18744             year += 1;
18745         }
18746     },
18747     
18748     showMode: function(dir) 
18749     {
18750         if (dir) {
18751             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18752         }
18753         
18754         Roo.each(this.picker().select('>div',true).elements, function(v){
18755             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18756             v.hide();
18757         });
18758         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18759     },
18760     
18761     place: function()
18762     {
18763         if(this.isInline) {
18764             return;
18765         }
18766         
18767         this.picker().removeClass(['bottom', 'top']);
18768         
18769         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18770             /*
18771              * place to the top of element!
18772              *
18773              */
18774             
18775             this.picker().addClass('top');
18776             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18777             
18778             return;
18779         }
18780         
18781         this.picker().addClass('bottom');
18782         
18783         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18784     },
18785     
18786     parseDate : function(value)
18787     {
18788         if(!value || value instanceof Date){
18789             return value;
18790         }
18791         var v = Date.parseDate(value, this.format);
18792         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18793             v = Date.parseDate(value, 'Y-m-d');
18794         }
18795         if(!v && this.altFormats){
18796             if(!this.altFormatsArray){
18797                 this.altFormatsArray = this.altFormats.split("|");
18798             }
18799             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18800                 v = Date.parseDate(value, this.altFormatsArray[i]);
18801             }
18802         }
18803         return v;
18804     },
18805     
18806     formatDate : function(date, fmt)
18807     {   
18808         return (!date || !(date instanceof Date)) ?
18809         date : date.dateFormat(fmt || this.format);
18810     },
18811     
18812     onFocus : function()
18813     {
18814         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18815         this.show();
18816     },
18817     
18818     onBlur : function()
18819     {
18820         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18821         
18822         var d = this.inputEl().getValue();
18823         
18824         this.setValue(d);
18825                 
18826         this.hide();
18827     },
18828     
18829     show : function()
18830     {
18831         this.picker().show();
18832         this.update();
18833         this.place();
18834         
18835         this.fireEvent('show', this, this.date);
18836     },
18837     
18838     hide : function()
18839     {
18840         if(this.isInline) {
18841             return;
18842         }
18843         this.picker().hide();
18844         this.viewMode = this.startViewMode;
18845         this.showMode();
18846         
18847         this.fireEvent('hide', this, this.date);
18848         
18849     },
18850     
18851     onMousedown: function(e)
18852     {
18853         e.stopPropagation();
18854         e.preventDefault();
18855     },
18856     
18857     keyup: function(e)
18858     {
18859         Roo.bootstrap.DateField.superclass.keyup.call(this);
18860         this.update();
18861     },
18862
18863     setValue: function(v)
18864     {
18865         if(this.fireEvent('beforeselect', this, v) !== false){
18866             var d = new Date(this.parseDate(v) ).clearTime();
18867         
18868             if(isNaN(d.getTime())){
18869                 this.date = this.viewDate = '';
18870                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18871                 return;
18872             }
18873
18874             v = this.formatDate(d);
18875
18876             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18877
18878             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18879
18880             this.update();
18881
18882             this.fireEvent('select', this, this.date);
18883         }
18884     },
18885     
18886     getValue: function()
18887     {
18888         return this.formatDate(this.date);
18889     },
18890     
18891     fireKey: function(e)
18892     {
18893         if (!this.picker().isVisible()){
18894             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18895                 this.show();
18896             }
18897             return;
18898         }
18899         
18900         var dateChanged = false,
18901         dir, day, month,
18902         newDate, newViewDate;
18903         
18904         switch(e.keyCode){
18905             case 27: // escape
18906                 this.hide();
18907                 e.preventDefault();
18908                 break;
18909             case 37: // left
18910             case 39: // right
18911                 if (!this.keyboardNavigation) {
18912                     break;
18913                 }
18914                 dir = e.keyCode == 37 ? -1 : 1;
18915                 
18916                 if (e.ctrlKey){
18917                     newDate = this.moveYear(this.date, dir);
18918                     newViewDate = this.moveYear(this.viewDate, dir);
18919                 } else if (e.shiftKey){
18920                     newDate = this.moveMonth(this.date, dir);
18921                     newViewDate = this.moveMonth(this.viewDate, dir);
18922                 } else {
18923                     newDate = new Date(this.date);
18924                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18925                     newViewDate = new Date(this.viewDate);
18926                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18927                 }
18928                 if (this.dateWithinRange(newDate)){
18929                     this.date = newDate;
18930                     this.viewDate = newViewDate;
18931                     this.setValue(this.formatDate(this.date));
18932 //                    this.update();
18933                     e.preventDefault();
18934                     dateChanged = true;
18935                 }
18936                 break;
18937             case 38: // up
18938             case 40: // down
18939                 if (!this.keyboardNavigation) {
18940                     break;
18941                 }
18942                 dir = e.keyCode == 38 ? -1 : 1;
18943                 if (e.ctrlKey){
18944                     newDate = this.moveYear(this.date, dir);
18945                     newViewDate = this.moveYear(this.viewDate, dir);
18946                 } else if (e.shiftKey){
18947                     newDate = this.moveMonth(this.date, dir);
18948                     newViewDate = this.moveMonth(this.viewDate, dir);
18949                 } else {
18950                     newDate = new Date(this.date);
18951                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18952                     newViewDate = new Date(this.viewDate);
18953                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18954                 }
18955                 if (this.dateWithinRange(newDate)){
18956                     this.date = newDate;
18957                     this.viewDate = newViewDate;
18958                     this.setValue(this.formatDate(this.date));
18959 //                    this.update();
18960                     e.preventDefault();
18961                     dateChanged = true;
18962                 }
18963                 break;
18964             case 13: // enter
18965                 this.setValue(this.formatDate(this.date));
18966                 this.hide();
18967                 e.preventDefault();
18968                 break;
18969             case 9: // tab
18970                 this.setValue(this.formatDate(this.date));
18971                 this.hide();
18972                 break;
18973             case 16: // shift
18974             case 17: // ctrl
18975             case 18: // alt
18976                 break;
18977             default :
18978                 this.hide();
18979                 
18980         }
18981     },
18982     
18983     
18984     onClick: function(e) 
18985     {
18986         e.stopPropagation();
18987         e.preventDefault();
18988         
18989         var target = e.getTarget();
18990         
18991         if(target.nodeName.toLowerCase() === 'i'){
18992             target = Roo.get(target).dom.parentNode;
18993         }
18994         
18995         var nodeName = target.nodeName;
18996         var className = target.className;
18997         var html = target.innerHTML;
18998         //Roo.log(nodeName);
18999         
19000         switch(nodeName.toLowerCase()) {
19001             case 'th':
19002                 switch(className) {
19003                     case 'switch':
19004                         this.showMode(1);
19005                         break;
19006                     case 'prev':
19007                     case 'next':
19008                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19009                         switch(this.viewMode){
19010                                 case 0:
19011                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19012                                         break;
19013                                 case 1:
19014                                 case 2:
19015                                         this.viewDate = this.moveYear(this.viewDate, dir);
19016                                         break;
19017                         }
19018                         this.fill();
19019                         break;
19020                     case 'today':
19021                         var date = new Date();
19022                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19023 //                        this.fill()
19024                         this.setValue(this.formatDate(this.date));
19025                         
19026                         this.hide();
19027                         break;
19028                 }
19029                 break;
19030             case 'span':
19031                 if (className.indexOf('disabled') < 0) {
19032                     this.viewDate.setUTCDate(1);
19033                     if (className.indexOf('month') > -1) {
19034                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19035                     } else {
19036                         var year = parseInt(html, 10) || 0;
19037                         this.viewDate.setUTCFullYear(year);
19038                         
19039                     }
19040                     
19041                     if(this.singleMode){
19042                         this.setValue(this.formatDate(this.viewDate));
19043                         this.hide();
19044                         return;
19045                     }
19046                     
19047                     this.showMode(-1);
19048                     this.fill();
19049                 }
19050                 break;
19051                 
19052             case 'td':
19053                 //Roo.log(className);
19054                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19055                     var day = parseInt(html, 10) || 1;
19056                     var year = this.viewDate.getUTCFullYear(),
19057                         month = this.viewDate.getUTCMonth();
19058
19059                     if (className.indexOf('old') > -1) {
19060                         if(month === 0 ){
19061                             month = 11;
19062                             year -= 1;
19063                         }else{
19064                             month -= 1;
19065                         }
19066                     } else if (className.indexOf('new') > -1) {
19067                         if (month == 11) {
19068                             month = 0;
19069                             year += 1;
19070                         } else {
19071                             month += 1;
19072                         }
19073                     }
19074                     //Roo.log([year,month,day]);
19075                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19076                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19077 //                    this.fill();
19078                     //Roo.log(this.formatDate(this.date));
19079                     this.setValue(this.formatDate(this.date));
19080                     this.hide();
19081                 }
19082                 break;
19083         }
19084     },
19085     
19086     setStartDate: function(startDate)
19087     {
19088         this.startDate = startDate || -Infinity;
19089         if (this.startDate !== -Infinity) {
19090             this.startDate = this.parseDate(this.startDate);
19091         }
19092         this.update();
19093         this.updateNavArrows();
19094     },
19095
19096     setEndDate: function(endDate)
19097     {
19098         this.endDate = endDate || Infinity;
19099         if (this.endDate !== Infinity) {
19100             this.endDate = this.parseDate(this.endDate);
19101         }
19102         this.update();
19103         this.updateNavArrows();
19104     },
19105     
19106     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19107     {
19108         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19109         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19110             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19111         }
19112         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19113             return parseInt(d, 10);
19114         });
19115         this.update();
19116         this.updateNavArrows();
19117     },
19118     
19119     updateNavArrows: function() 
19120     {
19121         if(this.singleMode){
19122             return;
19123         }
19124         
19125         var d = new Date(this.viewDate),
19126         year = d.getUTCFullYear(),
19127         month = d.getUTCMonth();
19128         
19129         Roo.each(this.picker().select('.prev', true).elements, function(v){
19130             v.show();
19131             switch (this.viewMode) {
19132                 case 0:
19133
19134                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19135                         v.hide();
19136                     }
19137                     break;
19138                 case 1:
19139                 case 2:
19140                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19141                         v.hide();
19142                     }
19143                     break;
19144             }
19145         });
19146         
19147         Roo.each(this.picker().select('.next', true).elements, function(v){
19148             v.show();
19149             switch (this.viewMode) {
19150                 case 0:
19151
19152                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19153                         v.hide();
19154                     }
19155                     break;
19156                 case 1:
19157                 case 2:
19158                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19159                         v.hide();
19160                     }
19161                     break;
19162             }
19163         })
19164     },
19165     
19166     moveMonth: function(date, dir)
19167     {
19168         if (!dir) {
19169             return date;
19170         }
19171         var new_date = new Date(date.valueOf()),
19172         day = new_date.getUTCDate(),
19173         month = new_date.getUTCMonth(),
19174         mag = Math.abs(dir),
19175         new_month, test;
19176         dir = dir > 0 ? 1 : -1;
19177         if (mag == 1){
19178             test = dir == -1
19179             // If going back one month, make sure month is not current month
19180             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19181             ? function(){
19182                 return new_date.getUTCMonth() == month;
19183             }
19184             // If going forward one month, make sure month is as expected
19185             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19186             : function(){
19187                 return new_date.getUTCMonth() != new_month;
19188             };
19189             new_month = month + dir;
19190             new_date.setUTCMonth(new_month);
19191             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19192             if (new_month < 0 || new_month > 11) {
19193                 new_month = (new_month + 12) % 12;
19194             }
19195         } else {
19196             // For magnitudes >1, move one month at a time...
19197             for (var i=0; i<mag; i++) {
19198                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19199                 new_date = this.moveMonth(new_date, dir);
19200             }
19201             // ...then reset the day, keeping it in the new month
19202             new_month = new_date.getUTCMonth();
19203             new_date.setUTCDate(day);
19204             test = function(){
19205                 return new_month != new_date.getUTCMonth();
19206             };
19207         }
19208         // Common date-resetting loop -- if date is beyond end of month, make it
19209         // end of month
19210         while (test()){
19211             new_date.setUTCDate(--day);
19212             new_date.setUTCMonth(new_month);
19213         }
19214         return new_date;
19215     },
19216
19217     moveYear: function(date, dir)
19218     {
19219         return this.moveMonth(date, dir*12);
19220     },
19221
19222     dateWithinRange: function(date)
19223     {
19224         return date >= this.startDate && date <= this.endDate;
19225     },
19226
19227     
19228     remove: function() 
19229     {
19230         this.picker().remove();
19231     },
19232     
19233     validateValue : function(value)
19234     {
19235         if(this.getVisibilityEl().hasClass('hidden')){
19236             return true;
19237         }
19238         
19239         if(value.length < 1)  {
19240             if(this.allowBlank){
19241                 return true;
19242             }
19243             return false;
19244         }
19245         
19246         if(value.length < this.minLength){
19247             return false;
19248         }
19249         if(value.length > this.maxLength){
19250             return false;
19251         }
19252         if(this.vtype){
19253             var vt = Roo.form.VTypes;
19254             if(!vt[this.vtype](value, this)){
19255                 return false;
19256             }
19257         }
19258         if(typeof this.validator == "function"){
19259             var msg = this.validator(value);
19260             if(msg !== true){
19261                 return false;
19262             }
19263         }
19264         
19265         if(this.regex && !this.regex.test(value)){
19266             return false;
19267         }
19268         
19269         if(typeof(this.parseDate(value)) == 'undefined'){
19270             return false;
19271         }
19272         
19273         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19274             return false;
19275         }      
19276         
19277         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19278             return false;
19279         } 
19280         
19281         
19282         return true;
19283     },
19284     
19285     setVisible : function(visible)
19286     {
19287         if(!this.getEl()){
19288             return;
19289         }
19290         
19291         this.getEl().removeClass('hidden');
19292         
19293         if(visible){
19294             return;
19295         }
19296         
19297         this.getEl().addClass('hidden');
19298     }
19299    
19300 });
19301
19302 Roo.apply(Roo.bootstrap.DateField,  {
19303     
19304     head : {
19305         tag: 'thead',
19306         cn: [
19307         {
19308             tag: 'tr',
19309             cn: [
19310             {
19311                 tag: 'th',
19312                 cls: 'prev',
19313                 html: '<i class="fa fa-arrow-left"/>'
19314             },
19315             {
19316                 tag: 'th',
19317                 cls: 'switch',
19318                 colspan: '5'
19319             },
19320             {
19321                 tag: 'th',
19322                 cls: 'next',
19323                 html: '<i class="fa fa-arrow-right"/>'
19324             }
19325
19326             ]
19327         }
19328         ]
19329     },
19330     
19331     content : {
19332         tag: 'tbody',
19333         cn: [
19334         {
19335             tag: 'tr',
19336             cn: [
19337             {
19338                 tag: 'td',
19339                 colspan: '7'
19340             }
19341             ]
19342         }
19343         ]
19344     },
19345     
19346     footer : {
19347         tag: 'tfoot',
19348         cn: [
19349         {
19350             tag: 'tr',
19351             cn: [
19352             {
19353                 tag: 'th',
19354                 colspan: '7',
19355                 cls: 'today'
19356             }
19357                     
19358             ]
19359         }
19360         ]
19361     },
19362     
19363     dates:{
19364         en: {
19365             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19366             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19367             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19368             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19369             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19370             today: "Today"
19371         }
19372     },
19373     
19374     modes: [
19375     {
19376         clsName: 'days',
19377         navFnc: 'Month',
19378         navStep: 1
19379     },
19380     {
19381         clsName: 'months',
19382         navFnc: 'FullYear',
19383         navStep: 1
19384     },
19385     {
19386         clsName: 'years',
19387         navFnc: 'FullYear',
19388         navStep: 10
19389     }]
19390 });
19391
19392 Roo.apply(Roo.bootstrap.DateField,  {
19393   
19394     template : {
19395         tag: 'div',
19396         cls: 'datepicker dropdown-menu roo-dynamic',
19397         cn: [
19398         {
19399             tag: 'div',
19400             cls: 'datepicker-days',
19401             cn: [
19402             {
19403                 tag: 'table',
19404                 cls: 'table-condensed',
19405                 cn:[
19406                 Roo.bootstrap.DateField.head,
19407                 {
19408                     tag: 'tbody'
19409                 },
19410                 Roo.bootstrap.DateField.footer
19411                 ]
19412             }
19413             ]
19414         },
19415         {
19416             tag: 'div',
19417             cls: 'datepicker-months',
19418             cn: [
19419             {
19420                 tag: 'table',
19421                 cls: 'table-condensed',
19422                 cn:[
19423                 Roo.bootstrap.DateField.head,
19424                 Roo.bootstrap.DateField.content,
19425                 Roo.bootstrap.DateField.footer
19426                 ]
19427             }
19428             ]
19429         },
19430         {
19431             tag: 'div',
19432             cls: 'datepicker-years',
19433             cn: [
19434             {
19435                 tag: 'table',
19436                 cls: 'table-condensed',
19437                 cn:[
19438                 Roo.bootstrap.DateField.head,
19439                 Roo.bootstrap.DateField.content,
19440                 Roo.bootstrap.DateField.footer
19441                 ]
19442             }
19443             ]
19444         }
19445         ]
19446     }
19447 });
19448
19449  
19450
19451  /*
19452  * - LGPL
19453  *
19454  * TimeField
19455  * 
19456  */
19457
19458 /**
19459  * @class Roo.bootstrap.TimeField
19460  * @extends Roo.bootstrap.Input
19461  * Bootstrap DateField class
19462  * 
19463  * 
19464  * @constructor
19465  * Create a new TimeField
19466  * @param {Object} config The config object
19467  */
19468
19469 Roo.bootstrap.TimeField = function(config){
19470     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19471     this.addEvents({
19472             /**
19473              * @event show
19474              * Fires when this field show.
19475              * @param {Roo.bootstrap.DateField} thisthis
19476              * @param {Mixed} date The date value
19477              */
19478             show : true,
19479             /**
19480              * @event show
19481              * Fires when this field hide.
19482              * @param {Roo.bootstrap.DateField} this
19483              * @param {Mixed} date The date value
19484              */
19485             hide : true,
19486             /**
19487              * @event select
19488              * Fires when select a date.
19489              * @param {Roo.bootstrap.DateField} this
19490              * @param {Mixed} date The date value
19491              */
19492             select : true
19493         });
19494 };
19495
19496 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19497     
19498     /**
19499      * @cfg {String} format
19500      * The default time format string which can be overriden for localization support.  The format must be
19501      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19502      */
19503     format : "H:i",
19504        
19505     onRender: function(ct, position)
19506     {
19507         
19508         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19509                 
19510         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19511         
19512         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19513         
19514         this.pop = this.picker().select('>.datepicker-time',true).first();
19515         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19516         
19517         this.picker().on('mousedown', this.onMousedown, this);
19518         this.picker().on('click', this.onClick, this);
19519         
19520         this.picker().addClass('datepicker-dropdown');
19521     
19522         this.fillTime();
19523         this.update();
19524             
19525         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19526         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19527         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19528         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19529         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19530         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19531
19532     },
19533     
19534     fireKey: function(e){
19535         if (!this.picker().isVisible()){
19536             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19537                 this.show();
19538             }
19539             return;
19540         }
19541
19542         e.preventDefault();
19543         
19544         switch(e.keyCode){
19545             case 27: // escape
19546                 this.hide();
19547                 break;
19548             case 37: // left
19549             case 39: // right
19550                 this.onTogglePeriod();
19551                 break;
19552             case 38: // up
19553                 this.onIncrementMinutes();
19554                 break;
19555             case 40: // down
19556                 this.onDecrementMinutes();
19557                 break;
19558             case 13: // enter
19559             case 9: // tab
19560                 this.setTime();
19561                 break;
19562         }
19563     },
19564     
19565     onClick: function(e) {
19566         e.stopPropagation();
19567         e.preventDefault();
19568     },
19569     
19570     picker : function()
19571     {
19572         return this.el.select('.datepicker', true).first();
19573     },
19574     
19575     fillTime: function()
19576     {    
19577         var time = this.pop.select('tbody', true).first();
19578         
19579         time.dom.innerHTML = '';
19580         
19581         time.createChild({
19582             tag: 'tr',
19583             cn: [
19584                 {
19585                     tag: 'td',
19586                     cn: [
19587                         {
19588                             tag: 'a',
19589                             href: '#',
19590                             cls: 'btn',
19591                             cn: [
19592                                 {
19593                                     tag: 'span',
19594                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19595                                 }
19596                             ]
19597                         } 
19598                     ]
19599                 },
19600                 {
19601                     tag: 'td',
19602                     cls: 'separator'
19603                 },
19604                 {
19605                     tag: 'td',
19606                     cn: [
19607                         {
19608                             tag: 'a',
19609                             href: '#',
19610                             cls: 'btn',
19611                             cn: [
19612                                 {
19613                                     tag: 'span',
19614                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19615                                 }
19616                             ]
19617                         }
19618                     ]
19619                 },
19620                 {
19621                     tag: 'td',
19622                     cls: 'separator'
19623                 }
19624             ]
19625         });
19626         
19627         time.createChild({
19628             tag: 'tr',
19629             cn: [
19630                 {
19631                     tag: 'td',
19632                     cn: [
19633                         {
19634                             tag: 'span',
19635                             cls: 'timepicker-hour',
19636                             html: '00'
19637                         }  
19638                     ]
19639                 },
19640                 {
19641                     tag: 'td',
19642                     cls: 'separator',
19643                     html: ':'
19644                 },
19645                 {
19646                     tag: 'td',
19647                     cn: [
19648                         {
19649                             tag: 'span',
19650                             cls: 'timepicker-minute',
19651                             html: '00'
19652                         }  
19653                     ]
19654                 },
19655                 {
19656                     tag: 'td',
19657                     cls: 'separator'
19658                 },
19659                 {
19660                     tag: 'td',
19661                     cn: [
19662                         {
19663                             tag: 'button',
19664                             type: 'button',
19665                             cls: 'btn btn-primary period',
19666                             html: 'AM'
19667                             
19668                         }
19669                     ]
19670                 }
19671             ]
19672         });
19673         
19674         time.createChild({
19675             tag: 'tr',
19676             cn: [
19677                 {
19678                     tag: 'td',
19679                     cn: [
19680                         {
19681                             tag: 'a',
19682                             href: '#',
19683                             cls: 'btn',
19684                             cn: [
19685                                 {
19686                                     tag: 'span',
19687                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19688                                 }
19689                             ]
19690                         }
19691                     ]
19692                 },
19693                 {
19694                     tag: 'td',
19695                     cls: 'separator'
19696                 },
19697                 {
19698                     tag: 'td',
19699                     cn: [
19700                         {
19701                             tag: 'a',
19702                             href: '#',
19703                             cls: 'btn',
19704                             cn: [
19705                                 {
19706                                     tag: 'span',
19707                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19708                                 }
19709                             ]
19710                         }
19711                     ]
19712                 },
19713                 {
19714                     tag: 'td',
19715                     cls: 'separator'
19716                 }
19717             ]
19718         });
19719         
19720     },
19721     
19722     update: function()
19723     {
19724         
19725         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19726         
19727         this.fill();
19728     },
19729     
19730     fill: function() 
19731     {
19732         var hours = this.time.getHours();
19733         var minutes = this.time.getMinutes();
19734         var period = 'AM';
19735         
19736         if(hours > 11){
19737             period = 'PM';
19738         }
19739         
19740         if(hours == 0){
19741             hours = 12;
19742         }
19743         
19744         
19745         if(hours > 12){
19746             hours = hours - 12;
19747         }
19748         
19749         if(hours < 10){
19750             hours = '0' + hours;
19751         }
19752         
19753         if(minutes < 10){
19754             minutes = '0' + minutes;
19755         }
19756         
19757         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19758         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19759         this.pop.select('button', true).first().dom.innerHTML = period;
19760         
19761     },
19762     
19763     place: function()
19764     {   
19765         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19766         
19767         var cls = ['bottom'];
19768         
19769         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19770             cls.pop();
19771             cls.push('top');
19772         }
19773         
19774         cls.push('right');
19775         
19776         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19777             cls.pop();
19778             cls.push('left');
19779         }
19780         
19781         this.picker().addClass(cls.join('-'));
19782         
19783         var _this = this;
19784         
19785         Roo.each(cls, function(c){
19786             if(c == 'bottom'){
19787                 _this.picker().setTop(_this.inputEl().getHeight());
19788                 return;
19789             }
19790             if(c == 'top'){
19791                 _this.picker().setTop(0 - _this.picker().getHeight());
19792                 return;
19793             }
19794             
19795             if(c == 'left'){
19796                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19797                 return;
19798             }
19799             if(c == 'right'){
19800                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19801                 return;
19802             }
19803         });
19804         
19805     },
19806   
19807     onFocus : function()
19808     {
19809         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19810         this.show();
19811     },
19812     
19813     onBlur : function()
19814     {
19815         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19816         this.hide();
19817     },
19818     
19819     show : function()
19820     {
19821         this.picker().show();
19822         this.pop.show();
19823         this.update();
19824         this.place();
19825         
19826         this.fireEvent('show', this, this.date);
19827     },
19828     
19829     hide : function()
19830     {
19831         this.picker().hide();
19832         this.pop.hide();
19833         
19834         this.fireEvent('hide', this, this.date);
19835     },
19836     
19837     setTime : function()
19838     {
19839         this.hide();
19840         this.setValue(this.time.format(this.format));
19841         
19842         this.fireEvent('select', this, this.date);
19843         
19844         
19845     },
19846     
19847     onMousedown: function(e){
19848         e.stopPropagation();
19849         e.preventDefault();
19850     },
19851     
19852     onIncrementHours: function()
19853     {
19854         Roo.log('onIncrementHours');
19855         this.time = this.time.add(Date.HOUR, 1);
19856         this.update();
19857         
19858     },
19859     
19860     onDecrementHours: function()
19861     {
19862         Roo.log('onDecrementHours');
19863         this.time = this.time.add(Date.HOUR, -1);
19864         this.update();
19865     },
19866     
19867     onIncrementMinutes: function()
19868     {
19869         Roo.log('onIncrementMinutes');
19870         this.time = this.time.add(Date.MINUTE, 1);
19871         this.update();
19872     },
19873     
19874     onDecrementMinutes: function()
19875     {
19876         Roo.log('onDecrementMinutes');
19877         this.time = this.time.add(Date.MINUTE, -1);
19878         this.update();
19879     },
19880     
19881     onTogglePeriod: function()
19882     {
19883         Roo.log('onTogglePeriod');
19884         this.time = this.time.add(Date.HOUR, 12);
19885         this.update();
19886     }
19887     
19888    
19889 });
19890
19891 Roo.apply(Roo.bootstrap.TimeField,  {
19892     
19893     content : {
19894         tag: 'tbody',
19895         cn: [
19896             {
19897                 tag: 'tr',
19898                 cn: [
19899                 {
19900                     tag: 'td',
19901                     colspan: '7'
19902                 }
19903                 ]
19904             }
19905         ]
19906     },
19907     
19908     footer : {
19909         tag: 'tfoot',
19910         cn: [
19911             {
19912                 tag: 'tr',
19913                 cn: [
19914                 {
19915                     tag: 'th',
19916                     colspan: '7',
19917                     cls: '',
19918                     cn: [
19919                         {
19920                             tag: 'button',
19921                             cls: 'btn btn-info ok',
19922                             html: 'OK'
19923                         }
19924                     ]
19925                 }
19926
19927                 ]
19928             }
19929         ]
19930     }
19931 });
19932
19933 Roo.apply(Roo.bootstrap.TimeField,  {
19934   
19935     template : {
19936         tag: 'div',
19937         cls: 'datepicker dropdown-menu',
19938         cn: [
19939             {
19940                 tag: 'div',
19941                 cls: 'datepicker-time',
19942                 cn: [
19943                 {
19944                     tag: 'table',
19945                     cls: 'table-condensed',
19946                     cn:[
19947                     Roo.bootstrap.TimeField.content,
19948                     Roo.bootstrap.TimeField.footer
19949                     ]
19950                 }
19951                 ]
19952             }
19953         ]
19954     }
19955 });
19956
19957  
19958
19959  /*
19960  * - LGPL
19961  *
19962  * MonthField
19963  * 
19964  */
19965
19966 /**
19967  * @class Roo.bootstrap.MonthField
19968  * @extends Roo.bootstrap.Input
19969  * Bootstrap MonthField class
19970  * 
19971  * @cfg {String} language default en
19972  * 
19973  * @constructor
19974  * Create a new MonthField
19975  * @param {Object} config The config object
19976  */
19977
19978 Roo.bootstrap.MonthField = function(config){
19979     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19980     
19981     this.addEvents({
19982         /**
19983          * @event show
19984          * Fires when this field show.
19985          * @param {Roo.bootstrap.MonthField} this
19986          * @param {Mixed} date The date value
19987          */
19988         show : true,
19989         /**
19990          * @event show
19991          * Fires when this field hide.
19992          * @param {Roo.bootstrap.MonthField} this
19993          * @param {Mixed} date The date value
19994          */
19995         hide : true,
19996         /**
19997          * @event select
19998          * Fires when select a date.
19999          * @param {Roo.bootstrap.MonthField} this
20000          * @param {String} oldvalue The old value
20001          * @param {String} newvalue The new value
20002          */
20003         select : true
20004     });
20005 };
20006
20007 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20008     
20009     onRender: function(ct, position)
20010     {
20011         
20012         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20013         
20014         this.language = this.language || 'en';
20015         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20016         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20017         
20018         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20019         this.isInline = false;
20020         this.isInput = true;
20021         this.component = this.el.select('.add-on', true).first() || false;
20022         this.component = (this.component && this.component.length === 0) ? false : this.component;
20023         this.hasInput = this.component && this.inputEL().length;
20024         
20025         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20026         
20027         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20028         
20029         this.picker().on('mousedown', this.onMousedown, this);
20030         this.picker().on('click', this.onClick, this);
20031         
20032         this.picker().addClass('datepicker-dropdown');
20033         
20034         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20035             v.setStyle('width', '189px');
20036         });
20037         
20038         this.fillMonths();
20039         
20040         this.update();
20041         
20042         if(this.isInline) {
20043             this.show();
20044         }
20045         
20046     },
20047     
20048     setValue: function(v, suppressEvent)
20049     {   
20050         var o = this.getValue();
20051         
20052         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20053         
20054         this.update();
20055
20056         if(suppressEvent !== true){
20057             this.fireEvent('select', this, o, v);
20058         }
20059         
20060     },
20061     
20062     getValue: function()
20063     {
20064         return this.value;
20065     },
20066     
20067     onClick: function(e) 
20068     {
20069         e.stopPropagation();
20070         e.preventDefault();
20071         
20072         var target = e.getTarget();
20073         
20074         if(target.nodeName.toLowerCase() === 'i'){
20075             target = Roo.get(target).dom.parentNode;
20076         }
20077         
20078         var nodeName = target.nodeName;
20079         var className = target.className;
20080         var html = target.innerHTML;
20081         
20082         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20083             return;
20084         }
20085         
20086         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20087         
20088         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20089         
20090         this.hide();
20091                         
20092     },
20093     
20094     picker : function()
20095     {
20096         return this.pickerEl;
20097     },
20098     
20099     fillMonths: function()
20100     {    
20101         var i = 0;
20102         var months = this.picker().select('>.datepicker-months td', true).first();
20103         
20104         months.dom.innerHTML = '';
20105         
20106         while (i < 12) {
20107             var month = {
20108                 tag: 'span',
20109                 cls: 'month',
20110                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20111             };
20112             
20113             months.createChild(month);
20114         }
20115         
20116     },
20117     
20118     update: function()
20119     {
20120         var _this = this;
20121         
20122         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20123             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20124         }
20125         
20126         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20127             e.removeClass('active');
20128             
20129             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20130                 e.addClass('active');
20131             }
20132         })
20133     },
20134     
20135     place: function()
20136     {
20137         if(this.isInline) {
20138             return;
20139         }
20140         
20141         this.picker().removeClass(['bottom', 'top']);
20142         
20143         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20144             /*
20145              * place to the top of element!
20146              *
20147              */
20148             
20149             this.picker().addClass('top');
20150             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20151             
20152             return;
20153         }
20154         
20155         this.picker().addClass('bottom');
20156         
20157         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20158     },
20159     
20160     onFocus : function()
20161     {
20162         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20163         this.show();
20164     },
20165     
20166     onBlur : function()
20167     {
20168         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20169         
20170         var d = this.inputEl().getValue();
20171         
20172         this.setValue(d);
20173                 
20174         this.hide();
20175     },
20176     
20177     show : function()
20178     {
20179         this.picker().show();
20180         this.picker().select('>.datepicker-months', true).first().show();
20181         this.update();
20182         this.place();
20183         
20184         this.fireEvent('show', this, this.date);
20185     },
20186     
20187     hide : function()
20188     {
20189         if(this.isInline) {
20190             return;
20191         }
20192         this.picker().hide();
20193         this.fireEvent('hide', this, this.date);
20194         
20195     },
20196     
20197     onMousedown: function(e)
20198     {
20199         e.stopPropagation();
20200         e.preventDefault();
20201     },
20202     
20203     keyup: function(e)
20204     {
20205         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20206         this.update();
20207     },
20208
20209     fireKey: function(e)
20210     {
20211         if (!this.picker().isVisible()){
20212             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20213                 this.show();
20214             }
20215             return;
20216         }
20217         
20218         var dir;
20219         
20220         switch(e.keyCode){
20221             case 27: // escape
20222                 this.hide();
20223                 e.preventDefault();
20224                 break;
20225             case 37: // left
20226             case 39: // right
20227                 dir = e.keyCode == 37 ? -1 : 1;
20228                 
20229                 this.vIndex = this.vIndex + dir;
20230                 
20231                 if(this.vIndex < 0){
20232                     this.vIndex = 0;
20233                 }
20234                 
20235                 if(this.vIndex > 11){
20236                     this.vIndex = 11;
20237                 }
20238                 
20239                 if(isNaN(this.vIndex)){
20240                     this.vIndex = 0;
20241                 }
20242                 
20243                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20244                 
20245                 break;
20246             case 38: // up
20247             case 40: // down
20248                 
20249                 dir = e.keyCode == 38 ? -1 : 1;
20250                 
20251                 this.vIndex = this.vIndex + dir * 4;
20252                 
20253                 if(this.vIndex < 0){
20254                     this.vIndex = 0;
20255                 }
20256                 
20257                 if(this.vIndex > 11){
20258                     this.vIndex = 11;
20259                 }
20260                 
20261                 if(isNaN(this.vIndex)){
20262                     this.vIndex = 0;
20263                 }
20264                 
20265                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20266                 break;
20267                 
20268             case 13: // enter
20269                 
20270                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20271                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20272                 }
20273                 
20274                 this.hide();
20275                 e.preventDefault();
20276                 break;
20277             case 9: // tab
20278                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20279                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20280                 }
20281                 this.hide();
20282                 break;
20283             case 16: // shift
20284             case 17: // ctrl
20285             case 18: // alt
20286                 break;
20287             default :
20288                 this.hide();
20289                 
20290         }
20291     },
20292     
20293     remove: function() 
20294     {
20295         this.picker().remove();
20296     }
20297    
20298 });
20299
20300 Roo.apply(Roo.bootstrap.MonthField,  {
20301     
20302     content : {
20303         tag: 'tbody',
20304         cn: [
20305         {
20306             tag: 'tr',
20307             cn: [
20308             {
20309                 tag: 'td',
20310                 colspan: '7'
20311             }
20312             ]
20313         }
20314         ]
20315     },
20316     
20317     dates:{
20318         en: {
20319             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20320             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20321         }
20322     }
20323 });
20324
20325 Roo.apply(Roo.bootstrap.MonthField,  {
20326   
20327     template : {
20328         tag: 'div',
20329         cls: 'datepicker dropdown-menu roo-dynamic',
20330         cn: [
20331             {
20332                 tag: 'div',
20333                 cls: 'datepicker-months',
20334                 cn: [
20335                 {
20336                     tag: 'table',
20337                     cls: 'table-condensed',
20338                     cn:[
20339                         Roo.bootstrap.DateField.content
20340                     ]
20341                 }
20342                 ]
20343             }
20344         ]
20345     }
20346 });
20347
20348  
20349
20350  
20351  /*
20352  * - LGPL
20353  *
20354  * CheckBox
20355  * 
20356  */
20357
20358 /**
20359  * @class Roo.bootstrap.CheckBox
20360  * @extends Roo.bootstrap.Input
20361  * Bootstrap CheckBox class
20362  * 
20363  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20364  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20365  * @cfg {String} boxLabel The text that appears beside the checkbox
20366  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20367  * @cfg {Boolean} checked initnal the element
20368  * @cfg {Boolean} inline inline the element (default false)
20369  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20370  * @cfg {String} tooltip label tooltip
20371  * 
20372  * @constructor
20373  * Create a new CheckBox
20374  * @param {Object} config The config object
20375  */
20376
20377 Roo.bootstrap.CheckBox = function(config){
20378     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20379    
20380     this.addEvents({
20381         /**
20382         * @event check
20383         * Fires when the element is checked or unchecked.
20384         * @param {Roo.bootstrap.CheckBox} this This input
20385         * @param {Boolean} checked The new checked value
20386         */
20387        check : true,
20388        /**
20389         * @event click
20390         * Fires when the element is click.
20391         * @param {Roo.bootstrap.CheckBox} this This input
20392         */
20393        click : true
20394     });
20395     
20396 };
20397
20398 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20399   
20400     inputType: 'checkbox',
20401     inputValue: 1,
20402     valueOff: 0,
20403     boxLabel: false,
20404     checked: false,
20405     weight : false,
20406     inline: false,
20407     tooltip : '',
20408     
20409     getAutoCreate : function()
20410     {
20411         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20412         
20413         var id = Roo.id();
20414         
20415         var cfg = {};
20416         
20417         cfg.cls = 'form-group ' + this.inputType; //input-group
20418         
20419         if(this.inline){
20420             cfg.cls += ' ' + this.inputType + '-inline';
20421         }
20422         
20423         var input =  {
20424             tag: 'input',
20425             id : id,
20426             type : this.inputType,
20427             value : this.inputValue,
20428             cls : 'roo-' + this.inputType, //'form-box',
20429             placeholder : this.placeholder || ''
20430             
20431         };
20432         
20433         if(this.inputType != 'radio'){
20434             var hidden =  {
20435                 tag: 'input',
20436                 type : 'hidden',
20437                 cls : 'roo-hidden-value',
20438                 value : this.checked ? this.inputValue : this.valueOff
20439             };
20440         }
20441         
20442             
20443         if (this.weight) { // Validity check?
20444             cfg.cls += " " + this.inputType + "-" + this.weight;
20445         }
20446         
20447         if (this.disabled) {
20448             input.disabled=true;
20449         }
20450         
20451         if(this.checked){
20452             input.checked = this.checked;
20453         }
20454         
20455         if (this.name) {
20456             
20457             input.name = this.name;
20458             
20459             if(this.inputType != 'radio'){
20460                 hidden.name = this.name;
20461                 input.name = '_hidden_' + this.name;
20462             }
20463         }
20464         
20465         if (this.size) {
20466             input.cls += ' input-' + this.size;
20467         }
20468         
20469         var settings=this;
20470         
20471         ['xs','sm','md','lg'].map(function(size){
20472             if (settings[size]) {
20473                 cfg.cls += ' col-' + size + '-' + settings[size];
20474             }
20475         });
20476         
20477         var inputblock = input;
20478          
20479         if (this.before || this.after) {
20480             
20481             inputblock = {
20482                 cls : 'input-group',
20483                 cn :  [] 
20484             };
20485             
20486             if (this.before) {
20487                 inputblock.cn.push({
20488                     tag :'span',
20489                     cls : 'input-group-addon',
20490                     html : this.before
20491                 });
20492             }
20493             
20494             inputblock.cn.push(input);
20495             
20496             if(this.inputType != 'radio'){
20497                 inputblock.cn.push(hidden);
20498             }
20499             
20500             if (this.after) {
20501                 inputblock.cn.push({
20502                     tag :'span',
20503                     cls : 'input-group-addon',
20504                     html : this.after
20505                 });
20506             }
20507             
20508         }
20509         
20510         if (align ==='left' && this.fieldLabel.length) {
20511 //                Roo.log("left and has label");
20512             cfg.cn = [
20513                 {
20514                     tag: 'label',
20515                     'for' :  id,
20516                     cls : 'control-label',
20517                     html : this.fieldLabel
20518                 },
20519                 {
20520                     cls : "", 
20521                     cn: [
20522                         inputblock
20523                     ]
20524                 }
20525             ];
20526             
20527             if(this.labelWidth > 12){
20528                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20529             }
20530             
20531             if(this.labelWidth < 13 && this.labelmd == 0){
20532                 this.labelmd = this.labelWidth;
20533             }
20534             
20535             if(this.labellg > 0){
20536                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20537                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20538             }
20539             
20540             if(this.labelmd > 0){
20541                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20542                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20543             }
20544             
20545             if(this.labelsm > 0){
20546                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20547                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20548             }
20549             
20550             if(this.labelxs > 0){
20551                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20552                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20553             }
20554             
20555         } else if ( this.fieldLabel.length) {
20556 //                Roo.log(" label");
20557                 cfg.cn = [
20558                    
20559                     {
20560                         tag: this.boxLabel ? 'span' : 'label',
20561                         'for': id,
20562                         cls: 'control-label box-input-label',
20563                         //cls : 'input-group-addon',
20564                         html : this.fieldLabel
20565                     },
20566                     
20567                     inputblock
20568                     
20569                 ];
20570
20571         } else {
20572             
20573 //                Roo.log(" no label && no align");
20574                 cfg.cn = [  inputblock ] ;
20575                 
20576                 
20577         }
20578         
20579         if(this.boxLabel){
20580              var boxLabelCfg = {
20581                 tag: 'label',
20582                 //'for': id, // box label is handled by onclick - so no for...
20583                 cls: 'box-label',
20584                 html: this.boxLabel
20585             };
20586             
20587             if(this.tooltip){
20588                 boxLabelCfg.tooltip = this.tooltip;
20589             }
20590              
20591             cfg.cn.push(boxLabelCfg);
20592         }
20593         
20594         if(this.inputType != 'radio'){
20595             cfg.cn.push(hidden);
20596         }
20597         
20598         return cfg;
20599         
20600     },
20601     
20602     /**
20603      * return the real input element.
20604      */
20605     inputEl: function ()
20606     {
20607         return this.el.select('input.roo-' + this.inputType,true).first();
20608     },
20609     hiddenEl: function ()
20610     {
20611         return this.el.select('input.roo-hidden-value',true).first();
20612     },
20613     
20614     labelEl: function()
20615     {
20616         return this.el.select('label.control-label',true).first();
20617     },
20618     /* depricated... */
20619     
20620     label: function()
20621     {
20622         return this.labelEl();
20623     },
20624     
20625     boxLabelEl: function()
20626     {
20627         return this.el.select('label.box-label',true).first();
20628     },
20629     
20630     initEvents : function()
20631     {
20632 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20633         
20634         this.inputEl().on('click', this.onClick,  this);
20635         
20636         if (this.boxLabel) { 
20637             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20638         }
20639         
20640         this.startValue = this.getValue();
20641         
20642         if(this.groupId){
20643             Roo.bootstrap.CheckBox.register(this);
20644         }
20645     },
20646     
20647     onClick : function(e)
20648     {   
20649         if(this.fireEvent('click', this, e) !== false){
20650             this.setChecked(!this.checked);
20651         }
20652         
20653     },
20654     
20655     setChecked : function(state,suppressEvent)
20656     {
20657         this.startValue = this.getValue();
20658
20659         if(this.inputType == 'radio'){
20660             
20661             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20662                 e.dom.checked = false;
20663             });
20664             
20665             this.inputEl().dom.checked = true;
20666             
20667             this.inputEl().dom.value = this.inputValue;
20668             
20669             if(suppressEvent !== true){
20670                 this.fireEvent('check', this, true);
20671             }
20672             
20673             this.validate();
20674             
20675             return;
20676         }
20677         
20678         this.checked = state;
20679         
20680         this.inputEl().dom.checked = state;
20681         
20682         
20683         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20684         
20685         if(suppressEvent !== true){
20686             this.fireEvent('check', this, state);
20687         }
20688         
20689         this.validate();
20690     },
20691     
20692     getValue : function()
20693     {
20694         if(this.inputType == 'radio'){
20695             return this.getGroupValue();
20696         }
20697         
20698         return this.hiddenEl().dom.value;
20699         
20700     },
20701     
20702     getGroupValue : function()
20703     {
20704         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20705             return '';
20706         }
20707         
20708         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20709     },
20710     
20711     setValue : function(v,suppressEvent)
20712     {
20713         if(this.inputType == 'radio'){
20714             this.setGroupValue(v, suppressEvent);
20715             return;
20716         }
20717         
20718         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20719         
20720         this.validate();
20721     },
20722     
20723     setGroupValue : function(v, suppressEvent)
20724     {
20725         this.startValue = this.getValue();
20726         
20727         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20728             e.dom.checked = false;
20729             
20730             if(e.dom.value == v){
20731                 e.dom.checked = true;
20732             }
20733         });
20734         
20735         if(suppressEvent !== true){
20736             this.fireEvent('check', this, true);
20737         }
20738
20739         this.validate();
20740         
20741         return;
20742     },
20743     
20744     validate : function()
20745     {
20746         if(this.getVisibilityEl().hasClass('hidden')){
20747             return true;
20748         }
20749         
20750         if(
20751                 this.disabled || 
20752                 (this.inputType == 'radio' && this.validateRadio()) ||
20753                 (this.inputType == 'checkbox' && this.validateCheckbox())
20754         ){
20755             this.markValid();
20756             return true;
20757         }
20758         
20759         this.markInvalid();
20760         return false;
20761     },
20762     
20763     validateRadio : function()
20764     {
20765         if(this.getVisibilityEl().hasClass('hidden')){
20766             return true;
20767         }
20768         
20769         if(this.allowBlank){
20770             return true;
20771         }
20772         
20773         var valid = false;
20774         
20775         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20776             if(!e.dom.checked){
20777                 return;
20778             }
20779             
20780             valid = true;
20781             
20782             return false;
20783         });
20784         
20785         return valid;
20786     },
20787     
20788     validateCheckbox : function()
20789     {
20790         if(!this.groupId){
20791             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20792             //return (this.getValue() == this.inputValue) ? true : false;
20793         }
20794         
20795         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20796         
20797         if(!group){
20798             return false;
20799         }
20800         
20801         var r = false;
20802         
20803         for(var i in group){
20804             if(group[i].el.isVisible(true)){
20805                 r = false;
20806                 break;
20807             }
20808             
20809             r = true;
20810         }
20811         
20812         for(var i in group){
20813             if(r){
20814                 break;
20815             }
20816             
20817             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20818         }
20819         
20820         return r;
20821     },
20822     
20823     /**
20824      * Mark this field as valid
20825      */
20826     markValid : function()
20827     {
20828         var _this = this;
20829         
20830         this.fireEvent('valid', this);
20831         
20832         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20833         
20834         if(this.groupId){
20835             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20836         }
20837         
20838         if(label){
20839             label.markValid();
20840         }
20841
20842         if(this.inputType == 'radio'){
20843             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20844                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20845                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20846             });
20847             
20848             return;
20849         }
20850
20851         if(!this.groupId){
20852             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20853             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20854             return;
20855         }
20856         
20857         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20858         
20859         if(!group){
20860             return;
20861         }
20862         
20863         for(var i in group){
20864             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20865             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20866         }
20867     },
20868     
20869      /**
20870      * Mark this field as invalid
20871      * @param {String} msg The validation message
20872      */
20873     markInvalid : function(msg)
20874     {
20875         if(this.allowBlank){
20876             return;
20877         }
20878         
20879         var _this = this;
20880         
20881         this.fireEvent('invalid', this, msg);
20882         
20883         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20884         
20885         if(this.groupId){
20886             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20887         }
20888         
20889         if(label){
20890             label.markInvalid();
20891         }
20892             
20893         if(this.inputType == 'radio'){
20894             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20895                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20896                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20897             });
20898             
20899             return;
20900         }
20901         
20902         if(!this.groupId){
20903             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20904             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20905             return;
20906         }
20907         
20908         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20909         
20910         if(!group){
20911             return;
20912         }
20913         
20914         for(var i in group){
20915             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20916             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20917         }
20918         
20919     },
20920     
20921     clearInvalid : function()
20922     {
20923         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20924         
20925         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20926         
20927         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20928         
20929         if (label && label.iconEl) {
20930             label.iconEl.removeClass(label.validClass);
20931             label.iconEl.removeClass(label.invalidClass);
20932         }
20933     },
20934     
20935     disable : function()
20936     {
20937         if(this.inputType != 'radio'){
20938             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20939             return;
20940         }
20941         
20942         var _this = this;
20943         
20944         if(this.rendered){
20945             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20946                 _this.getActionEl().addClass(this.disabledClass);
20947                 e.dom.disabled = true;
20948             });
20949         }
20950         
20951         this.disabled = true;
20952         this.fireEvent("disable", this);
20953         return this;
20954     },
20955
20956     enable : function()
20957     {
20958         if(this.inputType != 'radio'){
20959             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20960             return;
20961         }
20962         
20963         var _this = this;
20964         
20965         if(this.rendered){
20966             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20967                 _this.getActionEl().removeClass(this.disabledClass);
20968                 e.dom.disabled = false;
20969             });
20970         }
20971         
20972         this.disabled = false;
20973         this.fireEvent("enable", this);
20974         return this;
20975     },
20976     
20977     setBoxLabel : function(v)
20978     {
20979         this.boxLabel = v;
20980         
20981         if(this.rendered){
20982             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20983         }
20984     }
20985
20986 });
20987
20988 Roo.apply(Roo.bootstrap.CheckBox, {
20989     
20990     groups: {},
20991     
20992      /**
20993     * register a CheckBox Group
20994     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20995     */
20996     register : function(checkbox)
20997     {
20998         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20999             this.groups[checkbox.groupId] = {};
21000         }
21001         
21002         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21003             return;
21004         }
21005         
21006         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21007         
21008     },
21009     /**
21010     * fetch a CheckBox Group based on the group ID
21011     * @param {string} the group ID
21012     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21013     */
21014     get: function(groupId) {
21015         if (typeof(this.groups[groupId]) == 'undefined') {
21016             return false;
21017         }
21018         
21019         return this.groups[groupId] ;
21020     }
21021     
21022     
21023 });
21024 /*
21025  * - LGPL
21026  *
21027  * RadioItem
21028  * 
21029  */
21030
21031 /**
21032  * @class Roo.bootstrap.Radio
21033  * @extends Roo.bootstrap.Component
21034  * Bootstrap Radio class
21035  * @cfg {String} boxLabel - the label associated
21036  * @cfg {String} value - the value of radio
21037  * 
21038  * @constructor
21039  * Create a new Radio
21040  * @param {Object} config The config object
21041  */
21042 Roo.bootstrap.Radio = function(config){
21043     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21044     
21045 };
21046
21047 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21048     
21049     boxLabel : '',
21050     
21051     value : '',
21052     
21053     getAutoCreate : function()
21054     {
21055         var cfg = {
21056             tag : 'div',
21057             cls : 'form-group radio',
21058             cn : [
21059                 {
21060                     tag : 'label',
21061                     cls : 'box-label',
21062                     html : this.boxLabel
21063                 }
21064             ]
21065         };
21066         
21067         return cfg;
21068     },
21069     
21070     initEvents : function() 
21071     {
21072         this.parent().register(this);
21073         
21074         this.el.on('click', this.onClick, this);
21075         
21076     },
21077     
21078     onClick : function(e)
21079     {
21080         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21081             this.setChecked(true);
21082         }
21083     },
21084     
21085     setChecked : function(state, suppressEvent)
21086     {
21087         this.parent().setValue(this.value, suppressEvent);
21088         
21089     },
21090     
21091     setBoxLabel : function(v)
21092     {
21093         this.boxLabel = v;
21094         
21095         if(this.rendered){
21096             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21097         }
21098     }
21099     
21100 });
21101  
21102
21103  /*
21104  * - LGPL
21105  *
21106  * Input
21107  * 
21108  */
21109
21110 /**
21111  * @class Roo.bootstrap.SecurePass
21112  * @extends Roo.bootstrap.Input
21113  * Bootstrap SecurePass class
21114  *
21115  * 
21116  * @constructor
21117  * Create a new SecurePass
21118  * @param {Object} config The config object
21119  */
21120  
21121 Roo.bootstrap.SecurePass = function (config) {
21122     // these go here, so the translation tool can replace them..
21123     this.errors = {
21124         PwdEmpty: "Please type a password, and then retype it to confirm.",
21125         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21126         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21127         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21128         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21129         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21130         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21131         TooWeak: "Your password is Too Weak."
21132     },
21133     this.meterLabel = "Password strength:";
21134     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21135     this.meterClass = [
21136         "roo-password-meter-tooweak", 
21137         "roo-password-meter-weak", 
21138         "roo-password-meter-medium", 
21139         "roo-password-meter-strong", 
21140         "roo-password-meter-grey"
21141     ];
21142     
21143     this.errors = {};
21144     
21145     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21146 }
21147
21148 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21149     /**
21150      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21151      * {
21152      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21153      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21154      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21155      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21156      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21157      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21158      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21159      * })
21160      */
21161     // private
21162     
21163     meterWidth: 300,
21164     errorMsg :'',    
21165     errors: false,
21166     imageRoot: '/',
21167     /**
21168      * @cfg {String/Object} Label for the strength meter (defaults to
21169      * 'Password strength:')
21170      */
21171     // private
21172     meterLabel: '',
21173     /**
21174      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21175      * ['Weak', 'Medium', 'Strong'])
21176      */
21177     // private    
21178     pwdStrengths: false,    
21179     // private
21180     strength: 0,
21181     // private
21182     _lastPwd: null,
21183     // private
21184     kCapitalLetter: 0,
21185     kSmallLetter: 1,
21186     kDigit: 2,
21187     kPunctuation: 3,
21188     
21189     insecure: false,
21190     // private
21191     initEvents: function ()
21192     {
21193         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21194
21195         if (this.el.is('input[type=password]') && Roo.isSafari) {
21196             this.el.on('keydown', this.SafariOnKeyDown, this);
21197         }
21198
21199         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21200     },
21201     // private
21202     onRender: function (ct, position)
21203     {
21204         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21205         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21206         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21207
21208         this.trigger.createChild({
21209                    cn: [
21210                     {
21211                     //id: 'PwdMeter',
21212                     tag: 'div',
21213                     cls: 'roo-password-meter-grey col-xs-12',
21214                     style: {
21215                         //width: 0,
21216                         //width: this.meterWidth + 'px'                                                
21217                         }
21218                     },
21219                     {                            
21220                          cls: 'roo-password-meter-text'                          
21221                     }
21222                 ]            
21223         });
21224
21225          
21226         if (this.hideTrigger) {
21227             this.trigger.setDisplayed(false);
21228         }
21229         this.setSize(this.width || '', this.height || '');
21230     },
21231     // private
21232     onDestroy: function ()
21233     {
21234         if (this.trigger) {
21235             this.trigger.removeAllListeners();
21236             this.trigger.remove();
21237         }
21238         if (this.wrap) {
21239             this.wrap.remove();
21240         }
21241         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21242     },
21243     // private
21244     checkStrength: function ()
21245     {
21246         var pwd = this.inputEl().getValue();
21247         if (pwd == this._lastPwd) {
21248             return;
21249         }
21250
21251         var strength;
21252         if (this.ClientSideStrongPassword(pwd)) {
21253             strength = 3;
21254         } else if (this.ClientSideMediumPassword(pwd)) {
21255             strength = 2;
21256         } else if (this.ClientSideWeakPassword(pwd)) {
21257             strength = 1;
21258         } else {
21259             strength = 0;
21260         }
21261         
21262         Roo.log('strength1: ' + strength);
21263         
21264         //var pm = this.trigger.child('div/div/div').dom;
21265         var pm = this.trigger.child('div/div');
21266         pm.removeClass(this.meterClass);
21267         pm.addClass(this.meterClass[strength]);
21268                 
21269         
21270         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21271                 
21272         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21273         
21274         this._lastPwd = pwd;
21275     },
21276     reset: function ()
21277     {
21278         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21279         
21280         this._lastPwd = '';
21281         
21282         var pm = this.trigger.child('div/div');
21283         pm.removeClass(this.meterClass);
21284         pm.addClass('roo-password-meter-grey');        
21285         
21286         
21287         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21288         
21289         pt.innerHTML = '';
21290         this.inputEl().dom.type='password';
21291     },
21292     // private
21293     validateValue: function (value)
21294     {
21295         
21296         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21297             return false;
21298         }
21299         if (value.length == 0) {
21300             if (this.allowBlank) {
21301                 this.clearInvalid();
21302                 return true;
21303             }
21304
21305             this.markInvalid(this.errors.PwdEmpty);
21306             this.errorMsg = this.errors.PwdEmpty;
21307             return false;
21308         }
21309         
21310         if(this.insecure){
21311             return true;
21312         }
21313         
21314         if ('[\x21-\x7e]*'.match(value)) {
21315             this.markInvalid(this.errors.PwdBadChar);
21316             this.errorMsg = this.errors.PwdBadChar;
21317             return false;
21318         }
21319         if (value.length < 6) {
21320             this.markInvalid(this.errors.PwdShort);
21321             this.errorMsg = this.errors.PwdShort;
21322             return false;
21323         }
21324         if (value.length > 16) {
21325             this.markInvalid(this.errors.PwdLong);
21326             this.errorMsg = this.errors.PwdLong;
21327             return false;
21328         }
21329         var strength;
21330         if (this.ClientSideStrongPassword(value)) {
21331             strength = 3;
21332         } else if (this.ClientSideMediumPassword(value)) {
21333             strength = 2;
21334         } else if (this.ClientSideWeakPassword(value)) {
21335             strength = 1;
21336         } else {
21337             strength = 0;
21338         }
21339
21340         
21341         if (strength < 2) {
21342             //this.markInvalid(this.errors.TooWeak);
21343             this.errorMsg = this.errors.TooWeak;
21344             //return false;
21345         }
21346         
21347         
21348         console.log('strength2: ' + strength);
21349         
21350         //var pm = this.trigger.child('div/div/div').dom;
21351         
21352         var pm = this.trigger.child('div/div');
21353         pm.removeClass(this.meterClass);
21354         pm.addClass(this.meterClass[strength]);
21355                 
21356         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21357                 
21358         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21359         
21360         this.errorMsg = ''; 
21361         return true;
21362     },
21363     // private
21364     CharacterSetChecks: function (type)
21365     {
21366         this.type = type;
21367         this.fResult = false;
21368     },
21369     // private
21370     isctype: function (character, type)
21371     {
21372         switch (type) {  
21373             case this.kCapitalLetter:
21374                 if (character >= 'A' && character <= 'Z') {
21375                     return true;
21376                 }
21377                 break;
21378             
21379             case this.kSmallLetter:
21380                 if (character >= 'a' && character <= 'z') {
21381                     return true;
21382                 }
21383                 break;
21384             
21385             case this.kDigit:
21386                 if (character >= '0' && character <= '9') {
21387                     return true;
21388                 }
21389                 break;
21390             
21391             case this.kPunctuation:
21392                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21393                     return true;
21394                 }
21395                 break;
21396             
21397             default:
21398                 return false;
21399         }
21400
21401     },
21402     // private
21403     IsLongEnough: function (pwd, size)
21404     {
21405         return !(pwd == null || isNaN(size) || pwd.length < size);
21406     },
21407     // private
21408     SpansEnoughCharacterSets: function (word, nb)
21409     {
21410         if (!this.IsLongEnough(word, nb))
21411         {
21412             return false;
21413         }
21414
21415         var characterSetChecks = new Array(
21416             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21417             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21418         );
21419         
21420         for (var index = 0; index < word.length; ++index) {
21421             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21422                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21423                     characterSetChecks[nCharSet].fResult = true;
21424                     break;
21425                 }
21426             }
21427         }
21428
21429         var nCharSets = 0;
21430         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21431             if (characterSetChecks[nCharSet].fResult) {
21432                 ++nCharSets;
21433             }
21434         }
21435
21436         if (nCharSets < nb) {
21437             return false;
21438         }
21439         return true;
21440     },
21441     // private
21442     ClientSideStrongPassword: function (pwd)
21443     {
21444         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21445     },
21446     // private
21447     ClientSideMediumPassword: function (pwd)
21448     {
21449         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21450     },
21451     // private
21452     ClientSideWeakPassword: function (pwd)
21453     {
21454         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21455     }
21456           
21457 })//<script type="text/javascript">
21458
21459 /*
21460  * Based  Ext JS Library 1.1.1
21461  * Copyright(c) 2006-2007, Ext JS, LLC.
21462  * LGPL
21463  *
21464  */
21465  
21466 /**
21467  * @class Roo.HtmlEditorCore
21468  * @extends Roo.Component
21469  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21470  *
21471  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21472  */
21473
21474 Roo.HtmlEditorCore = function(config){
21475     
21476     
21477     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21478     
21479     
21480     this.addEvents({
21481         /**
21482          * @event initialize
21483          * Fires when the editor is fully initialized (including the iframe)
21484          * @param {Roo.HtmlEditorCore} this
21485          */
21486         initialize: true,
21487         /**
21488          * @event activate
21489          * Fires when the editor is first receives the focus. Any insertion must wait
21490          * until after this event.
21491          * @param {Roo.HtmlEditorCore} this
21492          */
21493         activate: true,
21494          /**
21495          * @event beforesync
21496          * Fires before the textarea is updated with content from the editor iframe. Return false
21497          * to cancel the sync.
21498          * @param {Roo.HtmlEditorCore} this
21499          * @param {String} html
21500          */
21501         beforesync: true,
21502          /**
21503          * @event beforepush
21504          * Fires before the iframe editor is updated with content from the textarea. Return false
21505          * to cancel the push.
21506          * @param {Roo.HtmlEditorCore} this
21507          * @param {String} html
21508          */
21509         beforepush: true,
21510          /**
21511          * @event sync
21512          * Fires when the textarea is updated with content from the editor iframe.
21513          * @param {Roo.HtmlEditorCore} this
21514          * @param {String} html
21515          */
21516         sync: true,
21517          /**
21518          * @event push
21519          * Fires when the iframe editor is updated with content from the textarea.
21520          * @param {Roo.HtmlEditorCore} this
21521          * @param {String} html
21522          */
21523         push: true,
21524         
21525         /**
21526          * @event editorevent
21527          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21528          * @param {Roo.HtmlEditorCore} this
21529          */
21530         editorevent: true
21531         
21532     });
21533     
21534     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21535     
21536     // defaults : white / black...
21537     this.applyBlacklists();
21538     
21539     
21540     
21541 };
21542
21543
21544 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21545
21546
21547      /**
21548      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21549      */
21550     
21551     owner : false,
21552     
21553      /**
21554      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21555      *                        Roo.resizable.
21556      */
21557     resizable : false,
21558      /**
21559      * @cfg {Number} height (in pixels)
21560      */   
21561     height: 300,
21562    /**
21563      * @cfg {Number} width (in pixels)
21564      */   
21565     width: 500,
21566     
21567     /**
21568      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21569      * 
21570      */
21571     stylesheets: false,
21572     
21573     // id of frame..
21574     frameId: false,
21575     
21576     // private properties
21577     validationEvent : false,
21578     deferHeight: true,
21579     initialized : false,
21580     activated : false,
21581     sourceEditMode : false,
21582     onFocus : Roo.emptyFn,
21583     iframePad:3,
21584     hideMode:'offsets',
21585     
21586     clearUp: true,
21587     
21588     // blacklist + whitelisted elements..
21589     black: false,
21590     white: false,
21591      
21592     bodyCls : '',
21593
21594     /**
21595      * Protected method that will not generally be called directly. It
21596      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21597      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21598      */
21599     getDocMarkup : function(){
21600         // body styles..
21601         var st = '';
21602         
21603         // inherit styels from page...?? 
21604         if (this.stylesheets === false) {
21605             
21606             Roo.get(document.head).select('style').each(function(node) {
21607                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21608             });
21609             
21610             Roo.get(document.head).select('link').each(function(node) { 
21611                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21612             });
21613             
21614         } else if (!this.stylesheets.length) {
21615                 // simple..
21616                 st = '<style type="text/css">' +
21617                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21618                    '</style>';
21619         } else { 
21620             st = '<style type="text/css">' +
21621                     this.stylesheets +
21622                 '</style>';
21623         }
21624         
21625         st +=  '<style type="text/css">' +
21626             'IMG { cursor: pointer } ' +
21627         '</style>';
21628
21629         var cls = 'roo-htmleditor-body';
21630         
21631         if(this.bodyCls.length){
21632             cls += ' ' + this.bodyCls;
21633         }
21634         
21635         return '<html><head>' + st  +
21636             //<style type="text/css">' +
21637             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21638             //'</style>' +
21639             ' </head><body class="' +  cls + '"></body></html>';
21640     },
21641
21642     // private
21643     onRender : function(ct, position)
21644     {
21645         var _t = this;
21646         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21647         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21648         
21649         
21650         this.el.dom.style.border = '0 none';
21651         this.el.dom.setAttribute('tabIndex', -1);
21652         this.el.addClass('x-hidden hide');
21653         
21654         
21655         
21656         if(Roo.isIE){ // fix IE 1px bogus margin
21657             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21658         }
21659        
21660         
21661         this.frameId = Roo.id();
21662         
21663          
21664         
21665         var iframe = this.owner.wrap.createChild({
21666             tag: 'iframe',
21667             cls: 'form-control', // bootstrap..
21668             id: this.frameId,
21669             name: this.frameId,
21670             frameBorder : 'no',
21671             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21672         }, this.el
21673         );
21674         
21675         
21676         this.iframe = iframe.dom;
21677
21678          this.assignDocWin();
21679         
21680         this.doc.designMode = 'on';
21681        
21682         this.doc.open();
21683         this.doc.write(this.getDocMarkup());
21684         this.doc.close();
21685
21686         
21687         var task = { // must defer to wait for browser to be ready
21688             run : function(){
21689                 //console.log("run task?" + this.doc.readyState);
21690                 this.assignDocWin();
21691                 if(this.doc.body || this.doc.readyState == 'complete'){
21692                     try {
21693                         this.doc.designMode="on";
21694                     } catch (e) {
21695                         return;
21696                     }
21697                     Roo.TaskMgr.stop(task);
21698                     this.initEditor.defer(10, this);
21699                 }
21700             },
21701             interval : 10,
21702             duration: 10000,
21703             scope: this
21704         };
21705         Roo.TaskMgr.start(task);
21706
21707     },
21708
21709     // private
21710     onResize : function(w, h)
21711     {
21712          Roo.log('resize: ' +w + ',' + h );
21713         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21714         if(!this.iframe){
21715             return;
21716         }
21717         if(typeof w == 'number'){
21718             
21719             this.iframe.style.width = w + 'px';
21720         }
21721         if(typeof h == 'number'){
21722             
21723             this.iframe.style.height = h + 'px';
21724             if(this.doc){
21725                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21726             }
21727         }
21728         
21729     },
21730
21731     /**
21732      * Toggles the editor between standard and source edit mode.
21733      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21734      */
21735     toggleSourceEdit : function(sourceEditMode){
21736         
21737         this.sourceEditMode = sourceEditMode === true;
21738         
21739         if(this.sourceEditMode){
21740  
21741             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21742             
21743         }else{
21744             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21745             //this.iframe.className = '';
21746             this.deferFocus();
21747         }
21748         //this.setSize(this.owner.wrap.getSize());
21749         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21750     },
21751
21752     
21753   
21754
21755     /**
21756      * Protected method that will not generally be called directly. If you need/want
21757      * custom HTML cleanup, this is the method you should override.
21758      * @param {String} html The HTML to be cleaned
21759      * return {String} The cleaned HTML
21760      */
21761     cleanHtml : function(html){
21762         html = String(html);
21763         if(html.length > 5){
21764             if(Roo.isSafari){ // strip safari nonsense
21765                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21766             }
21767         }
21768         if(html == '&nbsp;'){
21769             html = '';
21770         }
21771         return html;
21772     },
21773
21774     /**
21775      * HTML Editor -> Textarea
21776      * Protected method that will not generally be called directly. Syncs the contents
21777      * of the editor iframe with the textarea.
21778      */
21779     syncValue : function(){
21780         if(this.initialized){
21781             var bd = (this.doc.body || this.doc.documentElement);
21782             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21783             var html = bd.innerHTML;
21784             if(Roo.isSafari){
21785                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21786                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21787                 if(m && m[1]){
21788                     html = '<div style="'+m[0]+'">' + html + '</div>';
21789                 }
21790             }
21791             html = this.cleanHtml(html);
21792             // fix up the special chars.. normaly like back quotes in word...
21793             // however we do not want to do this with chinese..
21794             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21795                 var cc = b.charCodeAt();
21796                 if (
21797                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21798                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21799                     (cc >= 0xf900 && cc < 0xfb00 )
21800                 ) {
21801                         return b;
21802                 }
21803                 return "&#"+cc+";" 
21804             });
21805             if(this.owner.fireEvent('beforesync', this, html) !== false){
21806                 this.el.dom.value = html;
21807                 this.owner.fireEvent('sync', this, html);
21808             }
21809         }
21810     },
21811
21812     /**
21813      * Protected method that will not generally be called directly. Pushes the value of the textarea
21814      * into the iframe editor.
21815      */
21816     pushValue : function(){
21817         if(this.initialized){
21818             var v = this.el.dom.value.trim();
21819             
21820 //            if(v.length < 1){
21821 //                v = '&#160;';
21822 //            }
21823             
21824             if(this.owner.fireEvent('beforepush', this, v) !== false){
21825                 var d = (this.doc.body || this.doc.documentElement);
21826                 d.innerHTML = v;
21827                 this.cleanUpPaste();
21828                 this.el.dom.value = d.innerHTML;
21829                 this.owner.fireEvent('push', this, v);
21830             }
21831         }
21832     },
21833
21834     // private
21835     deferFocus : function(){
21836         this.focus.defer(10, this);
21837     },
21838
21839     // doc'ed in Field
21840     focus : function(){
21841         if(this.win && !this.sourceEditMode){
21842             this.win.focus();
21843         }else{
21844             this.el.focus();
21845         }
21846     },
21847     
21848     assignDocWin: function()
21849     {
21850         var iframe = this.iframe;
21851         
21852          if(Roo.isIE){
21853             this.doc = iframe.contentWindow.document;
21854             this.win = iframe.contentWindow;
21855         } else {
21856 //            if (!Roo.get(this.frameId)) {
21857 //                return;
21858 //            }
21859 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21860 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21861             
21862             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21863                 return;
21864             }
21865             
21866             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21867             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21868         }
21869     },
21870     
21871     // private
21872     initEditor : function(){
21873         //console.log("INIT EDITOR");
21874         this.assignDocWin();
21875         
21876         
21877         
21878         this.doc.designMode="on";
21879         this.doc.open();
21880         this.doc.write(this.getDocMarkup());
21881         this.doc.close();
21882         
21883         var dbody = (this.doc.body || this.doc.documentElement);
21884         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21885         // this copies styles from the containing element into thsi one..
21886         // not sure why we need all of this..
21887         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21888         
21889         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21890         //ss['background-attachment'] = 'fixed'; // w3c
21891         dbody.bgProperties = 'fixed'; // ie
21892         //Roo.DomHelper.applyStyles(dbody, ss);
21893         Roo.EventManager.on(this.doc, {
21894             //'mousedown': this.onEditorEvent,
21895             'mouseup': this.onEditorEvent,
21896             'dblclick': this.onEditorEvent,
21897             'click': this.onEditorEvent,
21898             'keyup': this.onEditorEvent,
21899             buffer:100,
21900             scope: this
21901         });
21902         if(Roo.isGecko){
21903             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21904         }
21905         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21906             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21907         }
21908         this.initialized = true;
21909
21910         this.owner.fireEvent('initialize', this);
21911         this.pushValue();
21912     },
21913
21914     // private
21915     onDestroy : function(){
21916         
21917         
21918         
21919         if(this.rendered){
21920             
21921             //for (var i =0; i < this.toolbars.length;i++) {
21922             //    // fixme - ask toolbars for heights?
21923             //    this.toolbars[i].onDestroy();
21924            // }
21925             
21926             //this.wrap.dom.innerHTML = '';
21927             //this.wrap.remove();
21928         }
21929     },
21930
21931     // private
21932     onFirstFocus : function(){
21933         
21934         this.assignDocWin();
21935         
21936         
21937         this.activated = true;
21938          
21939     
21940         if(Roo.isGecko){ // prevent silly gecko errors
21941             this.win.focus();
21942             var s = this.win.getSelection();
21943             if(!s.focusNode || s.focusNode.nodeType != 3){
21944                 var r = s.getRangeAt(0);
21945                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21946                 r.collapse(true);
21947                 this.deferFocus();
21948             }
21949             try{
21950                 this.execCmd('useCSS', true);
21951                 this.execCmd('styleWithCSS', false);
21952             }catch(e){}
21953         }
21954         this.owner.fireEvent('activate', this);
21955     },
21956
21957     // private
21958     adjustFont: function(btn){
21959         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21960         //if(Roo.isSafari){ // safari
21961         //    adjust *= 2;
21962        // }
21963         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21964         if(Roo.isSafari){ // safari
21965             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21966             v =  (v < 10) ? 10 : v;
21967             v =  (v > 48) ? 48 : v;
21968             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21969             
21970         }
21971         
21972         
21973         v = Math.max(1, v+adjust);
21974         
21975         this.execCmd('FontSize', v  );
21976     },
21977
21978     onEditorEvent : function(e)
21979     {
21980         this.owner.fireEvent('editorevent', this, e);
21981       //  this.updateToolbar();
21982         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21983     },
21984
21985     insertTag : function(tg)
21986     {
21987         // could be a bit smarter... -> wrap the current selected tRoo..
21988         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21989             
21990             range = this.createRange(this.getSelection());
21991             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21992             wrappingNode.appendChild(range.extractContents());
21993             range.insertNode(wrappingNode);
21994
21995             return;
21996             
21997             
21998             
21999         }
22000         this.execCmd("formatblock",   tg);
22001         
22002     },
22003     
22004     insertText : function(txt)
22005     {
22006         
22007         
22008         var range = this.createRange();
22009         range.deleteContents();
22010                //alert(Sender.getAttribute('label'));
22011                
22012         range.insertNode(this.doc.createTextNode(txt));
22013     } ,
22014     
22015      
22016
22017     /**
22018      * Executes a Midas editor command on the editor document and performs necessary focus and
22019      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22020      * @param {String} cmd The Midas command
22021      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22022      */
22023     relayCmd : function(cmd, value){
22024         this.win.focus();
22025         this.execCmd(cmd, value);
22026         this.owner.fireEvent('editorevent', this);
22027         //this.updateToolbar();
22028         this.owner.deferFocus();
22029     },
22030
22031     /**
22032      * Executes a Midas editor command directly on the editor document.
22033      * For visual commands, you should use {@link #relayCmd} instead.
22034      * <b>This should only be called after the editor is initialized.</b>
22035      * @param {String} cmd The Midas command
22036      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22037      */
22038     execCmd : function(cmd, value){
22039         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22040         this.syncValue();
22041     },
22042  
22043  
22044    
22045     /**
22046      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22047      * to insert tRoo.
22048      * @param {String} text | dom node.. 
22049      */
22050     insertAtCursor : function(text)
22051     {
22052         
22053         if(!this.activated){
22054             return;
22055         }
22056         /*
22057         if(Roo.isIE){
22058             this.win.focus();
22059             var r = this.doc.selection.createRange();
22060             if(r){
22061                 r.collapse(true);
22062                 r.pasteHTML(text);
22063                 this.syncValue();
22064                 this.deferFocus();
22065             
22066             }
22067             return;
22068         }
22069         */
22070         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22071             this.win.focus();
22072             
22073             
22074             // from jquery ui (MIT licenced)
22075             var range, node;
22076             var win = this.win;
22077             
22078             if (win.getSelection && win.getSelection().getRangeAt) {
22079                 range = win.getSelection().getRangeAt(0);
22080                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22081                 range.insertNode(node);
22082             } else if (win.document.selection && win.document.selection.createRange) {
22083                 // no firefox support
22084                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22085                 win.document.selection.createRange().pasteHTML(txt);
22086             } else {
22087                 // no firefox support
22088                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22089                 this.execCmd('InsertHTML', txt);
22090             } 
22091             
22092             this.syncValue();
22093             
22094             this.deferFocus();
22095         }
22096     },
22097  // private
22098     mozKeyPress : function(e){
22099         if(e.ctrlKey){
22100             var c = e.getCharCode(), cmd;
22101           
22102             if(c > 0){
22103                 c = String.fromCharCode(c).toLowerCase();
22104                 switch(c){
22105                     case 'b':
22106                         cmd = 'bold';
22107                         break;
22108                     case 'i':
22109                         cmd = 'italic';
22110                         break;
22111                     
22112                     case 'u':
22113                         cmd = 'underline';
22114                         break;
22115                     
22116                     case 'v':
22117                         this.cleanUpPaste.defer(100, this);
22118                         return;
22119                         
22120                 }
22121                 if(cmd){
22122                     this.win.focus();
22123                     this.execCmd(cmd);
22124                     this.deferFocus();
22125                     e.preventDefault();
22126                 }
22127                 
22128             }
22129         }
22130     },
22131
22132     // private
22133     fixKeys : function(){ // load time branching for fastest keydown performance
22134         if(Roo.isIE){
22135             return function(e){
22136                 var k = e.getKey(), r;
22137                 if(k == e.TAB){
22138                     e.stopEvent();
22139                     r = this.doc.selection.createRange();
22140                     if(r){
22141                         r.collapse(true);
22142                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22143                         this.deferFocus();
22144                     }
22145                     return;
22146                 }
22147                 
22148                 if(k == e.ENTER){
22149                     r = this.doc.selection.createRange();
22150                     if(r){
22151                         var target = r.parentElement();
22152                         if(!target || target.tagName.toLowerCase() != 'li'){
22153                             e.stopEvent();
22154                             r.pasteHTML('<br />');
22155                             r.collapse(false);
22156                             r.select();
22157                         }
22158                     }
22159                 }
22160                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22161                     this.cleanUpPaste.defer(100, this);
22162                     return;
22163                 }
22164                 
22165                 
22166             };
22167         }else if(Roo.isOpera){
22168             return function(e){
22169                 var k = e.getKey();
22170                 if(k == e.TAB){
22171                     e.stopEvent();
22172                     this.win.focus();
22173                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22174                     this.deferFocus();
22175                 }
22176                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22177                     this.cleanUpPaste.defer(100, this);
22178                     return;
22179                 }
22180                 
22181             };
22182         }else if(Roo.isSafari){
22183             return function(e){
22184                 var k = e.getKey();
22185                 
22186                 if(k == e.TAB){
22187                     e.stopEvent();
22188                     this.execCmd('InsertText','\t');
22189                     this.deferFocus();
22190                     return;
22191                 }
22192                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22193                     this.cleanUpPaste.defer(100, this);
22194                     return;
22195                 }
22196                 
22197              };
22198         }
22199     }(),
22200     
22201     getAllAncestors: function()
22202     {
22203         var p = this.getSelectedNode();
22204         var a = [];
22205         if (!p) {
22206             a.push(p); // push blank onto stack..
22207             p = this.getParentElement();
22208         }
22209         
22210         
22211         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22212             a.push(p);
22213             p = p.parentNode;
22214         }
22215         a.push(this.doc.body);
22216         return a;
22217     },
22218     lastSel : false,
22219     lastSelNode : false,
22220     
22221     
22222     getSelection : function() 
22223     {
22224         this.assignDocWin();
22225         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22226     },
22227     
22228     getSelectedNode: function() 
22229     {
22230         // this may only work on Gecko!!!
22231         
22232         // should we cache this!!!!
22233         
22234         
22235         
22236          
22237         var range = this.createRange(this.getSelection()).cloneRange();
22238         
22239         if (Roo.isIE) {
22240             var parent = range.parentElement();
22241             while (true) {
22242                 var testRange = range.duplicate();
22243                 testRange.moveToElementText(parent);
22244                 if (testRange.inRange(range)) {
22245                     break;
22246                 }
22247                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22248                     break;
22249                 }
22250                 parent = parent.parentElement;
22251             }
22252             return parent;
22253         }
22254         
22255         // is ancestor a text element.
22256         var ac =  range.commonAncestorContainer;
22257         if (ac.nodeType == 3) {
22258             ac = ac.parentNode;
22259         }
22260         
22261         var ar = ac.childNodes;
22262          
22263         var nodes = [];
22264         var other_nodes = [];
22265         var has_other_nodes = false;
22266         for (var i=0;i<ar.length;i++) {
22267             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22268                 continue;
22269             }
22270             // fullly contained node.
22271             
22272             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22273                 nodes.push(ar[i]);
22274                 continue;
22275             }
22276             
22277             // probably selected..
22278             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22279                 other_nodes.push(ar[i]);
22280                 continue;
22281             }
22282             // outer..
22283             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22284                 continue;
22285             }
22286             
22287             
22288             has_other_nodes = true;
22289         }
22290         if (!nodes.length && other_nodes.length) {
22291             nodes= other_nodes;
22292         }
22293         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22294             return false;
22295         }
22296         
22297         return nodes[0];
22298     },
22299     createRange: function(sel)
22300     {
22301         // this has strange effects when using with 
22302         // top toolbar - not sure if it's a great idea.
22303         //this.editor.contentWindow.focus();
22304         if (typeof sel != "undefined") {
22305             try {
22306                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22307             } catch(e) {
22308                 return this.doc.createRange();
22309             }
22310         } else {
22311             return this.doc.createRange();
22312         }
22313     },
22314     getParentElement: function()
22315     {
22316         
22317         this.assignDocWin();
22318         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22319         
22320         var range = this.createRange(sel);
22321          
22322         try {
22323             var p = range.commonAncestorContainer;
22324             while (p.nodeType == 3) { // text node
22325                 p = p.parentNode;
22326             }
22327             return p;
22328         } catch (e) {
22329             return null;
22330         }
22331     
22332     },
22333     /***
22334      *
22335      * Range intersection.. the hard stuff...
22336      *  '-1' = before
22337      *  '0' = hits..
22338      *  '1' = after.
22339      *         [ -- selected range --- ]
22340      *   [fail]                        [fail]
22341      *
22342      *    basically..
22343      *      if end is before start or  hits it. fail.
22344      *      if start is after end or hits it fail.
22345      *
22346      *   if either hits (but other is outside. - then it's not 
22347      *   
22348      *    
22349      **/
22350     
22351     
22352     // @see http://www.thismuchiknow.co.uk/?p=64.
22353     rangeIntersectsNode : function(range, node)
22354     {
22355         var nodeRange = node.ownerDocument.createRange();
22356         try {
22357             nodeRange.selectNode(node);
22358         } catch (e) {
22359             nodeRange.selectNodeContents(node);
22360         }
22361     
22362         var rangeStartRange = range.cloneRange();
22363         rangeStartRange.collapse(true);
22364     
22365         var rangeEndRange = range.cloneRange();
22366         rangeEndRange.collapse(false);
22367     
22368         var nodeStartRange = nodeRange.cloneRange();
22369         nodeStartRange.collapse(true);
22370     
22371         var nodeEndRange = nodeRange.cloneRange();
22372         nodeEndRange.collapse(false);
22373     
22374         return rangeStartRange.compareBoundaryPoints(
22375                  Range.START_TO_START, nodeEndRange) == -1 &&
22376                rangeEndRange.compareBoundaryPoints(
22377                  Range.START_TO_START, nodeStartRange) == 1;
22378         
22379          
22380     },
22381     rangeCompareNode : function(range, node)
22382     {
22383         var nodeRange = node.ownerDocument.createRange();
22384         try {
22385             nodeRange.selectNode(node);
22386         } catch (e) {
22387             nodeRange.selectNodeContents(node);
22388         }
22389         
22390         
22391         range.collapse(true);
22392     
22393         nodeRange.collapse(true);
22394      
22395         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22396         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22397          
22398         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22399         
22400         var nodeIsBefore   =  ss == 1;
22401         var nodeIsAfter    = ee == -1;
22402         
22403         if (nodeIsBefore && nodeIsAfter) {
22404             return 0; // outer
22405         }
22406         if (!nodeIsBefore && nodeIsAfter) {
22407             return 1; //right trailed.
22408         }
22409         
22410         if (nodeIsBefore && !nodeIsAfter) {
22411             return 2;  // left trailed.
22412         }
22413         // fully contined.
22414         return 3;
22415     },
22416
22417     // private? - in a new class?
22418     cleanUpPaste :  function()
22419     {
22420         // cleans up the whole document..
22421         Roo.log('cleanuppaste');
22422         
22423         this.cleanUpChildren(this.doc.body);
22424         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22425         if (clean != this.doc.body.innerHTML) {
22426             this.doc.body.innerHTML = clean;
22427         }
22428         
22429     },
22430     
22431     cleanWordChars : function(input) {// change the chars to hex code
22432         var he = Roo.HtmlEditorCore;
22433         
22434         var output = input;
22435         Roo.each(he.swapCodes, function(sw) { 
22436             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22437             
22438             output = output.replace(swapper, sw[1]);
22439         });
22440         
22441         return output;
22442     },
22443     
22444     
22445     cleanUpChildren : function (n)
22446     {
22447         if (!n.childNodes.length) {
22448             return;
22449         }
22450         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22451            this.cleanUpChild(n.childNodes[i]);
22452         }
22453     },
22454     
22455     
22456         
22457     
22458     cleanUpChild : function (node)
22459     {
22460         var ed = this;
22461         //console.log(node);
22462         if (node.nodeName == "#text") {
22463             // clean up silly Windows -- stuff?
22464             return; 
22465         }
22466         if (node.nodeName == "#comment") {
22467             node.parentNode.removeChild(node);
22468             // clean up silly Windows -- stuff?
22469             return; 
22470         }
22471         var lcname = node.tagName.toLowerCase();
22472         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22473         // whitelist of tags..
22474         
22475         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22476             // remove node.
22477             node.parentNode.removeChild(node);
22478             return;
22479             
22480         }
22481         
22482         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22483         
22484         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22485         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22486         
22487         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22488         //    remove_keep_children = true;
22489         //}
22490         
22491         if (remove_keep_children) {
22492             this.cleanUpChildren(node);
22493             // inserts everything just before this node...
22494             while (node.childNodes.length) {
22495                 var cn = node.childNodes[0];
22496                 node.removeChild(cn);
22497                 node.parentNode.insertBefore(cn, node);
22498             }
22499             node.parentNode.removeChild(node);
22500             return;
22501         }
22502         
22503         if (!node.attributes || !node.attributes.length) {
22504             this.cleanUpChildren(node);
22505             return;
22506         }
22507         
22508         function cleanAttr(n,v)
22509         {
22510             
22511             if (v.match(/^\./) || v.match(/^\//)) {
22512                 return;
22513             }
22514             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22515                 return;
22516             }
22517             if (v.match(/^#/)) {
22518                 return;
22519             }
22520 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22521             node.removeAttribute(n);
22522             
22523         }
22524         
22525         var cwhite = this.cwhite;
22526         var cblack = this.cblack;
22527             
22528         function cleanStyle(n,v)
22529         {
22530             if (v.match(/expression/)) { //XSS?? should we even bother..
22531                 node.removeAttribute(n);
22532                 return;
22533             }
22534             
22535             var parts = v.split(/;/);
22536             var clean = [];
22537             
22538             Roo.each(parts, function(p) {
22539                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22540                 if (!p.length) {
22541                     return true;
22542                 }
22543                 var l = p.split(':').shift().replace(/\s+/g,'');
22544                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22545                 
22546                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22547 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22548                     //node.removeAttribute(n);
22549                     return true;
22550                 }
22551                 //Roo.log()
22552                 // only allow 'c whitelisted system attributes'
22553                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22554 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22555                     //node.removeAttribute(n);
22556                     return true;
22557                 }
22558                 
22559                 
22560                  
22561                 
22562                 clean.push(p);
22563                 return true;
22564             });
22565             if (clean.length) { 
22566                 node.setAttribute(n, clean.join(';'));
22567             } else {
22568                 node.removeAttribute(n);
22569             }
22570             
22571         }
22572         
22573         
22574         for (var i = node.attributes.length-1; i > -1 ; i--) {
22575             var a = node.attributes[i];
22576             //console.log(a);
22577             
22578             if (a.name.toLowerCase().substr(0,2)=='on')  {
22579                 node.removeAttribute(a.name);
22580                 continue;
22581             }
22582             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22583                 node.removeAttribute(a.name);
22584                 continue;
22585             }
22586             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22587                 cleanAttr(a.name,a.value); // fixme..
22588                 continue;
22589             }
22590             if (a.name == 'style') {
22591                 cleanStyle(a.name,a.value);
22592                 continue;
22593             }
22594             /// clean up MS crap..
22595             // tecnically this should be a list of valid class'es..
22596             
22597             
22598             if (a.name == 'class') {
22599                 if (a.value.match(/^Mso/)) {
22600                     node.className = '';
22601                 }
22602                 
22603                 if (a.value.match(/^body$/)) {
22604                     node.className = '';
22605                 }
22606                 continue;
22607             }
22608             
22609             // style cleanup!?
22610             // class cleanup?
22611             
22612         }
22613         
22614         
22615         this.cleanUpChildren(node);
22616         
22617         
22618     },
22619     
22620     /**
22621      * Clean up MS wordisms...
22622      */
22623     cleanWord : function(node)
22624     {
22625         
22626         
22627         if (!node) {
22628             this.cleanWord(this.doc.body);
22629             return;
22630         }
22631         if (node.nodeName == "#text") {
22632             // clean up silly Windows -- stuff?
22633             return; 
22634         }
22635         if (node.nodeName == "#comment") {
22636             node.parentNode.removeChild(node);
22637             // clean up silly Windows -- stuff?
22638             return; 
22639         }
22640         
22641         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22642             node.parentNode.removeChild(node);
22643             return;
22644         }
22645         
22646         // remove - but keep children..
22647         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22648             while (node.childNodes.length) {
22649                 var cn = node.childNodes[0];
22650                 node.removeChild(cn);
22651                 node.parentNode.insertBefore(cn, node);
22652             }
22653             node.parentNode.removeChild(node);
22654             this.iterateChildren(node, this.cleanWord);
22655             return;
22656         }
22657         // clean styles
22658         if (node.className.length) {
22659             
22660             var cn = node.className.split(/\W+/);
22661             var cna = [];
22662             Roo.each(cn, function(cls) {
22663                 if (cls.match(/Mso[a-zA-Z]+/)) {
22664                     return;
22665                 }
22666                 cna.push(cls);
22667             });
22668             node.className = cna.length ? cna.join(' ') : '';
22669             if (!cna.length) {
22670                 node.removeAttribute("class");
22671             }
22672         }
22673         
22674         if (node.hasAttribute("lang")) {
22675             node.removeAttribute("lang");
22676         }
22677         
22678         if (node.hasAttribute("style")) {
22679             
22680             var styles = node.getAttribute("style").split(";");
22681             var nstyle = [];
22682             Roo.each(styles, function(s) {
22683                 if (!s.match(/:/)) {
22684                     return;
22685                 }
22686                 var kv = s.split(":");
22687                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22688                     return;
22689                 }
22690                 // what ever is left... we allow.
22691                 nstyle.push(s);
22692             });
22693             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22694             if (!nstyle.length) {
22695                 node.removeAttribute('style');
22696             }
22697         }
22698         this.iterateChildren(node, this.cleanWord);
22699         
22700         
22701         
22702     },
22703     /**
22704      * iterateChildren of a Node, calling fn each time, using this as the scole..
22705      * @param {DomNode} node node to iterate children of.
22706      * @param {Function} fn method of this class to call on each item.
22707      */
22708     iterateChildren : function(node, fn)
22709     {
22710         if (!node.childNodes.length) {
22711                 return;
22712         }
22713         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22714            fn.call(this, node.childNodes[i])
22715         }
22716     },
22717     
22718     
22719     /**
22720      * cleanTableWidths.
22721      *
22722      * Quite often pasting from word etc.. results in tables with column and widths.
22723      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22724      *
22725      */
22726     cleanTableWidths : function(node)
22727     {
22728          
22729          
22730         if (!node) {
22731             this.cleanTableWidths(this.doc.body);
22732             return;
22733         }
22734         
22735         // ignore list...
22736         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22737             return; 
22738         }
22739         Roo.log(node.tagName);
22740         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22741             this.iterateChildren(node, this.cleanTableWidths);
22742             return;
22743         }
22744         if (node.hasAttribute('width')) {
22745             node.removeAttribute('width');
22746         }
22747         
22748          
22749         if (node.hasAttribute("style")) {
22750             // pretty basic...
22751             
22752             var styles = node.getAttribute("style").split(";");
22753             var nstyle = [];
22754             Roo.each(styles, function(s) {
22755                 if (!s.match(/:/)) {
22756                     return;
22757                 }
22758                 var kv = s.split(":");
22759                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22760                     return;
22761                 }
22762                 // what ever is left... we allow.
22763                 nstyle.push(s);
22764             });
22765             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22766             if (!nstyle.length) {
22767                 node.removeAttribute('style');
22768             }
22769         }
22770         
22771         this.iterateChildren(node, this.cleanTableWidths);
22772         
22773         
22774     },
22775     
22776     
22777     
22778     
22779     domToHTML : function(currentElement, depth, nopadtext) {
22780         
22781         depth = depth || 0;
22782         nopadtext = nopadtext || false;
22783     
22784         if (!currentElement) {
22785             return this.domToHTML(this.doc.body);
22786         }
22787         
22788         //Roo.log(currentElement);
22789         var j;
22790         var allText = false;
22791         var nodeName = currentElement.nodeName;
22792         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22793         
22794         if  (nodeName == '#text') {
22795             
22796             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22797         }
22798         
22799         
22800         var ret = '';
22801         if (nodeName != 'BODY') {
22802              
22803             var i = 0;
22804             // Prints the node tagName, such as <A>, <IMG>, etc
22805             if (tagName) {
22806                 var attr = [];
22807                 for(i = 0; i < currentElement.attributes.length;i++) {
22808                     // quoting?
22809                     var aname = currentElement.attributes.item(i).name;
22810                     if (!currentElement.attributes.item(i).value.length) {
22811                         continue;
22812                     }
22813                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22814                 }
22815                 
22816                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22817             } 
22818             else {
22819                 
22820                 // eack
22821             }
22822         } else {
22823             tagName = false;
22824         }
22825         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22826             return ret;
22827         }
22828         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22829             nopadtext = true;
22830         }
22831         
22832         
22833         // Traverse the tree
22834         i = 0;
22835         var currentElementChild = currentElement.childNodes.item(i);
22836         var allText = true;
22837         var innerHTML  = '';
22838         lastnode = '';
22839         while (currentElementChild) {
22840             // Formatting code (indent the tree so it looks nice on the screen)
22841             var nopad = nopadtext;
22842             if (lastnode == 'SPAN') {
22843                 nopad  = true;
22844             }
22845             // text
22846             if  (currentElementChild.nodeName == '#text') {
22847                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22848                 toadd = nopadtext ? toadd : toadd.trim();
22849                 if (!nopad && toadd.length > 80) {
22850                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22851                 }
22852                 innerHTML  += toadd;
22853                 
22854                 i++;
22855                 currentElementChild = currentElement.childNodes.item(i);
22856                 lastNode = '';
22857                 continue;
22858             }
22859             allText = false;
22860             
22861             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22862                 
22863             // Recursively traverse the tree structure of the child node
22864             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22865             lastnode = currentElementChild.nodeName;
22866             i++;
22867             currentElementChild=currentElement.childNodes.item(i);
22868         }
22869         
22870         ret += innerHTML;
22871         
22872         if (!allText) {
22873                 // The remaining code is mostly for formatting the tree
22874             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22875         }
22876         
22877         
22878         if (tagName) {
22879             ret+= "</"+tagName+">";
22880         }
22881         return ret;
22882         
22883     },
22884         
22885     applyBlacklists : function()
22886     {
22887         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22888         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22889         
22890         this.white = [];
22891         this.black = [];
22892         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22893             if (b.indexOf(tag) > -1) {
22894                 return;
22895             }
22896             this.white.push(tag);
22897             
22898         }, this);
22899         
22900         Roo.each(w, function(tag) {
22901             if (b.indexOf(tag) > -1) {
22902                 return;
22903             }
22904             if (this.white.indexOf(tag) > -1) {
22905                 return;
22906             }
22907             this.white.push(tag);
22908             
22909         }, this);
22910         
22911         
22912         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22913             if (w.indexOf(tag) > -1) {
22914                 return;
22915             }
22916             this.black.push(tag);
22917             
22918         }, this);
22919         
22920         Roo.each(b, function(tag) {
22921             if (w.indexOf(tag) > -1) {
22922                 return;
22923             }
22924             if (this.black.indexOf(tag) > -1) {
22925                 return;
22926             }
22927             this.black.push(tag);
22928             
22929         }, this);
22930         
22931         
22932         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22933         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22934         
22935         this.cwhite = [];
22936         this.cblack = [];
22937         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22938             if (b.indexOf(tag) > -1) {
22939                 return;
22940             }
22941             this.cwhite.push(tag);
22942             
22943         }, this);
22944         
22945         Roo.each(w, function(tag) {
22946             if (b.indexOf(tag) > -1) {
22947                 return;
22948             }
22949             if (this.cwhite.indexOf(tag) > -1) {
22950                 return;
22951             }
22952             this.cwhite.push(tag);
22953             
22954         }, this);
22955         
22956         
22957         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22958             if (w.indexOf(tag) > -1) {
22959                 return;
22960             }
22961             this.cblack.push(tag);
22962             
22963         }, this);
22964         
22965         Roo.each(b, function(tag) {
22966             if (w.indexOf(tag) > -1) {
22967                 return;
22968             }
22969             if (this.cblack.indexOf(tag) > -1) {
22970                 return;
22971             }
22972             this.cblack.push(tag);
22973             
22974         }, this);
22975     },
22976     
22977     setStylesheets : function(stylesheets)
22978     {
22979         if(typeof(stylesheets) == 'string'){
22980             Roo.get(this.iframe.contentDocument.head).createChild({
22981                 tag : 'link',
22982                 rel : 'stylesheet',
22983                 type : 'text/css',
22984                 href : stylesheets
22985             });
22986             
22987             return;
22988         }
22989         var _this = this;
22990      
22991         Roo.each(stylesheets, function(s) {
22992             if(!s.length){
22993                 return;
22994             }
22995             
22996             Roo.get(_this.iframe.contentDocument.head).createChild({
22997                 tag : 'link',
22998                 rel : 'stylesheet',
22999                 type : 'text/css',
23000                 href : s
23001             });
23002         });
23003
23004         
23005     },
23006     
23007     removeStylesheets : function()
23008     {
23009         var _this = this;
23010         
23011         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23012             s.remove();
23013         });
23014     },
23015     
23016     setStyle : function(style)
23017     {
23018         Roo.get(this.iframe.contentDocument.head).createChild({
23019             tag : 'style',
23020             type : 'text/css',
23021             html : style
23022         });
23023
23024         return;
23025     }
23026     
23027     // hide stuff that is not compatible
23028     /**
23029      * @event blur
23030      * @hide
23031      */
23032     /**
23033      * @event change
23034      * @hide
23035      */
23036     /**
23037      * @event focus
23038      * @hide
23039      */
23040     /**
23041      * @event specialkey
23042      * @hide
23043      */
23044     /**
23045      * @cfg {String} fieldClass @hide
23046      */
23047     /**
23048      * @cfg {String} focusClass @hide
23049      */
23050     /**
23051      * @cfg {String} autoCreate @hide
23052      */
23053     /**
23054      * @cfg {String} inputType @hide
23055      */
23056     /**
23057      * @cfg {String} invalidClass @hide
23058      */
23059     /**
23060      * @cfg {String} invalidText @hide
23061      */
23062     /**
23063      * @cfg {String} msgFx @hide
23064      */
23065     /**
23066      * @cfg {String} validateOnBlur @hide
23067      */
23068 });
23069
23070 Roo.HtmlEditorCore.white = [
23071         'area', 'br', 'img', 'input', 'hr', 'wbr',
23072         
23073        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23074        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23075        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23076        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23077        'table',   'ul',         'xmp', 
23078        
23079        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23080       'thead',   'tr', 
23081      
23082       'dir', 'menu', 'ol', 'ul', 'dl',
23083        
23084       'embed',  'object'
23085 ];
23086
23087
23088 Roo.HtmlEditorCore.black = [
23089     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23090         'applet', // 
23091         'base',   'basefont', 'bgsound', 'blink',  'body', 
23092         'frame',  'frameset', 'head',    'html',   'ilayer', 
23093         'iframe', 'layer',  'link',     'meta',    'object',   
23094         'script', 'style' ,'title',  'xml' // clean later..
23095 ];
23096 Roo.HtmlEditorCore.clean = [
23097     'script', 'style', 'title', 'xml'
23098 ];
23099 Roo.HtmlEditorCore.remove = [
23100     'font'
23101 ];
23102 // attributes..
23103
23104 Roo.HtmlEditorCore.ablack = [
23105     'on'
23106 ];
23107     
23108 Roo.HtmlEditorCore.aclean = [ 
23109     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23110 ];
23111
23112 // protocols..
23113 Roo.HtmlEditorCore.pwhite= [
23114         'http',  'https',  'mailto'
23115 ];
23116
23117 // white listed style attributes.
23118 Roo.HtmlEditorCore.cwhite= [
23119       //  'text-align', /// default is to allow most things..
23120       
23121          
23122 //        'font-size'//??
23123 ];
23124
23125 // black listed style attributes.
23126 Roo.HtmlEditorCore.cblack= [
23127       //  'font-size' -- this can be set by the project 
23128 ];
23129
23130
23131 Roo.HtmlEditorCore.swapCodes   =[ 
23132     [    8211, "--" ], 
23133     [    8212, "--" ], 
23134     [    8216,  "'" ],  
23135     [    8217, "'" ],  
23136     [    8220, '"' ],  
23137     [    8221, '"' ],  
23138     [    8226, "*" ],  
23139     [    8230, "..." ]
23140 ]; 
23141
23142     /*
23143  * - LGPL
23144  *
23145  * HtmlEditor
23146  * 
23147  */
23148
23149 /**
23150  * @class Roo.bootstrap.HtmlEditor
23151  * @extends Roo.bootstrap.TextArea
23152  * Bootstrap HtmlEditor class
23153
23154  * @constructor
23155  * Create a new HtmlEditor
23156  * @param {Object} config The config object
23157  */
23158
23159 Roo.bootstrap.HtmlEditor = function(config){
23160     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23161     if (!this.toolbars) {
23162         this.toolbars = [];
23163     }
23164     
23165     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23166     this.addEvents({
23167             /**
23168              * @event initialize
23169              * Fires when the editor is fully initialized (including the iframe)
23170              * @param {HtmlEditor} this
23171              */
23172             initialize: true,
23173             /**
23174              * @event activate
23175              * Fires when the editor is first receives the focus. Any insertion must wait
23176              * until after this event.
23177              * @param {HtmlEditor} this
23178              */
23179             activate: true,
23180              /**
23181              * @event beforesync
23182              * Fires before the textarea is updated with content from the editor iframe. Return false
23183              * to cancel the sync.
23184              * @param {HtmlEditor} this
23185              * @param {String} html
23186              */
23187             beforesync: true,
23188              /**
23189              * @event beforepush
23190              * Fires before the iframe editor is updated with content from the textarea. Return false
23191              * to cancel the push.
23192              * @param {HtmlEditor} this
23193              * @param {String} html
23194              */
23195             beforepush: true,
23196              /**
23197              * @event sync
23198              * Fires when the textarea is updated with content from the editor iframe.
23199              * @param {HtmlEditor} this
23200              * @param {String} html
23201              */
23202             sync: true,
23203              /**
23204              * @event push
23205              * Fires when the iframe editor is updated with content from the textarea.
23206              * @param {HtmlEditor} this
23207              * @param {String} html
23208              */
23209             push: true,
23210              /**
23211              * @event editmodechange
23212              * Fires when the editor switches edit modes
23213              * @param {HtmlEditor} this
23214              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23215              */
23216             editmodechange: true,
23217             /**
23218              * @event editorevent
23219              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23220              * @param {HtmlEditor} this
23221              */
23222             editorevent: true,
23223             /**
23224              * @event firstfocus
23225              * Fires when on first focus - needed by toolbars..
23226              * @param {HtmlEditor} this
23227              */
23228             firstfocus: true,
23229             /**
23230              * @event autosave
23231              * Auto save the htmlEditor value as a file into Events
23232              * @param {HtmlEditor} this
23233              */
23234             autosave: true,
23235             /**
23236              * @event savedpreview
23237              * preview the saved version of htmlEditor
23238              * @param {HtmlEditor} this
23239              */
23240             savedpreview: true
23241         });
23242 };
23243
23244
23245 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23246     
23247     
23248       /**
23249      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23250      */
23251     toolbars : false,
23252     
23253      /**
23254     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23255     */
23256     btns : [],
23257    
23258      /**
23259      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23260      *                        Roo.resizable.
23261      */
23262     resizable : false,
23263      /**
23264      * @cfg {Number} height (in pixels)
23265      */   
23266     height: 300,
23267    /**
23268      * @cfg {Number} width (in pixels)
23269      */   
23270     width: false,
23271     
23272     /**
23273      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23274      * 
23275      */
23276     stylesheets: false,
23277     
23278     // id of frame..
23279     frameId: false,
23280     
23281     // private properties
23282     validationEvent : false,
23283     deferHeight: true,
23284     initialized : false,
23285     activated : false,
23286     
23287     onFocus : Roo.emptyFn,
23288     iframePad:3,
23289     hideMode:'offsets',
23290     
23291     tbContainer : false,
23292     
23293     bodyCls : '',
23294     
23295     toolbarContainer :function() {
23296         return this.wrap.select('.x-html-editor-tb',true).first();
23297     },
23298
23299     /**
23300      * Protected method that will not generally be called directly. It
23301      * is called when the editor creates its toolbar. Override this method if you need to
23302      * add custom toolbar buttons.
23303      * @param {HtmlEditor} editor
23304      */
23305     createToolbar : function(){
23306         Roo.log('renewing');
23307         Roo.log("create toolbars");
23308         
23309         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23310         this.toolbars[0].render(this.toolbarContainer());
23311         
23312         return;
23313         
23314 //        if (!editor.toolbars || !editor.toolbars.length) {
23315 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23316 //        }
23317 //        
23318 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23319 //            editor.toolbars[i] = Roo.factory(
23320 //                    typeof(editor.toolbars[i]) == 'string' ?
23321 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23322 //                Roo.bootstrap.HtmlEditor);
23323 //            editor.toolbars[i].init(editor);
23324 //        }
23325     },
23326
23327      
23328     // private
23329     onRender : function(ct, position)
23330     {
23331        // Roo.log("Call onRender: " + this.xtype);
23332         var _t = this;
23333         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23334       
23335         this.wrap = this.inputEl().wrap({
23336             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23337         });
23338         
23339         this.editorcore.onRender(ct, position);
23340          
23341         if (this.resizable) {
23342             this.resizeEl = new Roo.Resizable(this.wrap, {
23343                 pinned : true,
23344                 wrap: true,
23345                 dynamic : true,
23346                 minHeight : this.height,
23347                 height: this.height,
23348                 handles : this.resizable,
23349                 width: this.width,
23350                 listeners : {
23351                     resize : function(r, w, h) {
23352                         _t.onResize(w,h); // -something
23353                     }
23354                 }
23355             });
23356             
23357         }
23358         this.createToolbar(this);
23359        
23360         
23361         if(!this.width && this.resizable){
23362             this.setSize(this.wrap.getSize());
23363         }
23364         if (this.resizeEl) {
23365             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23366             // should trigger onReize..
23367         }
23368         
23369     },
23370
23371     // private
23372     onResize : function(w, h)
23373     {
23374         Roo.log('resize: ' +w + ',' + h );
23375         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23376         var ew = false;
23377         var eh = false;
23378         
23379         if(this.inputEl() ){
23380             if(typeof w == 'number'){
23381                 var aw = w - this.wrap.getFrameWidth('lr');
23382                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23383                 ew = aw;
23384             }
23385             if(typeof h == 'number'){
23386                  var tbh = -11;  // fixme it needs to tool bar size!
23387                 for (var i =0; i < this.toolbars.length;i++) {
23388                     // fixme - ask toolbars for heights?
23389                     tbh += this.toolbars[i].el.getHeight();
23390                     //if (this.toolbars[i].footer) {
23391                     //    tbh += this.toolbars[i].footer.el.getHeight();
23392                     //}
23393                 }
23394               
23395                 
23396                 
23397                 
23398                 
23399                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23400                 ah -= 5; // knock a few pixes off for look..
23401                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23402                 var eh = ah;
23403             }
23404         }
23405         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23406         this.editorcore.onResize(ew,eh);
23407         
23408     },
23409
23410     /**
23411      * Toggles the editor between standard and source edit mode.
23412      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23413      */
23414     toggleSourceEdit : function(sourceEditMode)
23415     {
23416         this.editorcore.toggleSourceEdit(sourceEditMode);
23417         
23418         if(this.editorcore.sourceEditMode){
23419             Roo.log('editor - showing textarea');
23420             
23421 //            Roo.log('in');
23422 //            Roo.log(this.syncValue());
23423             this.syncValue();
23424             this.inputEl().removeClass(['hide', 'x-hidden']);
23425             this.inputEl().dom.removeAttribute('tabIndex');
23426             this.inputEl().focus();
23427         }else{
23428             Roo.log('editor - hiding textarea');
23429 //            Roo.log('out')
23430 //            Roo.log(this.pushValue()); 
23431             this.pushValue();
23432             
23433             this.inputEl().addClass(['hide', 'x-hidden']);
23434             this.inputEl().dom.setAttribute('tabIndex', -1);
23435             //this.deferFocus();
23436         }
23437          
23438         if(this.resizable){
23439             this.setSize(this.wrap.getSize());
23440         }
23441         
23442         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23443     },
23444  
23445     // private (for BoxComponent)
23446     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23447
23448     // private (for BoxComponent)
23449     getResizeEl : function(){
23450         return this.wrap;
23451     },
23452
23453     // private (for BoxComponent)
23454     getPositionEl : function(){
23455         return this.wrap;
23456     },
23457
23458     // private
23459     initEvents : function(){
23460         this.originalValue = this.getValue();
23461     },
23462
23463 //    /**
23464 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23465 //     * @method
23466 //     */
23467 //    markInvalid : Roo.emptyFn,
23468 //    /**
23469 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23470 //     * @method
23471 //     */
23472 //    clearInvalid : Roo.emptyFn,
23473
23474     setValue : function(v){
23475         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23476         this.editorcore.pushValue();
23477     },
23478
23479      
23480     // private
23481     deferFocus : function(){
23482         this.focus.defer(10, this);
23483     },
23484
23485     // doc'ed in Field
23486     focus : function(){
23487         this.editorcore.focus();
23488         
23489     },
23490       
23491
23492     // private
23493     onDestroy : function(){
23494         
23495         
23496         
23497         if(this.rendered){
23498             
23499             for (var i =0; i < this.toolbars.length;i++) {
23500                 // fixme - ask toolbars for heights?
23501                 this.toolbars[i].onDestroy();
23502             }
23503             
23504             this.wrap.dom.innerHTML = '';
23505             this.wrap.remove();
23506         }
23507     },
23508
23509     // private
23510     onFirstFocus : function(){
23511         //Roo.log("onFirstFocus");
23512         this.editorcore.onFirstFocus();
23513          for (var i =0; i < this.toolbars.length;i++) {
23514             this.toolbars[i].onFirstFocus();
23515         }
23516         
23517     },
23518     
23519     // private
23520     syncValue : function()
23521     {   
23522         this.editorcore.syncValue();
23523     },
23524     
23525     pushValue : function()
23526     {   
23527         this.editorcore.pushValue();
23528     }
23529      
23530     
23531     // hide stuff that is not compatible
23532     /**
23533      * @event blur
23534      * @hide
23535      */
23536     /**
23537      * @event change
23538      * @hide
23539      */
23540     /**
23541      * @event focus
23542      * @hide
23543      */
23544     /**
23545      * @event specialkey
23546      * @hide
23547      */
23548     /**
23549      * @cfg {String} fieldClass @hide
23550      */
23551     /**
23552      * @cfg {String} focusClass @hide
23553      */
23554     /**
23555      * @cfg {String} autoCreate @hide
23556      */
23557     /**
23558      * @cfg {String} inputType @hide
23559      */
23560     /**
23561      * @cfg {String} invalidClass @hide
23562      */
23563     /**
23564      * @cfg {String} invalidText @hide
23565      */
23566     /**
23567      * @cfg {String} msgFx @hide
23568      */
23569     /**
23570      * @cfg {String} validateOnBlur @hide
23571      */
23572 });
23573  
23574     
23575    
23576    
23577    
23578       
23579 Roo.namespace('Roo.bootstrap.htmleditor');
23580 /**
23581  * @class Roo.bootstrap.HtmlEditorToolbar1
23582  * Basic Toolbar
23583  * 
23584  * Usage:
23585  *
23586  new Roo.bootstrap.HtmlEditor({
23587     ....
23588     toolbars : [
23589         new Roo.bootstrap.HtmlEditorToolbar1({
23590             disable : { fonts: 1 , format: 1, ..., ... , ...],
23591             btns : [ .... ]
23592         })
23593     }
23594      
23595  * 
23596  * @cfg {Object} disable List of elements to disable..
23597  * @cfg {Array} btns List of additional buttons.
23598  * 
23599  * 
23600  * NEEDS Extra CSS? 
23601  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23602  */
23603  
23604 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23605 {
23606     
23607     Roo.apply(this, config);
23608     
23609     // default disabled, based on 'good practice'..
23610     this.disable = this.disable || {};
23611     Roo.applyIf(this.disable, {
23612         fontSize : true,
23613         colors : true,
23614         specialElements : true
23615     });
23616     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23617     
23618     this.editor = config.editor;
23619     this.editorcore = config.editor.editorcore;
23620     
23621     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23622     
23623     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23624     // dont call parent... till later.
23625 }
23626 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23627      
23628     bar : true,
23629     
23630     editor : false,
23631     editorcore : false,
23632     
23633     
23634     formats : [
23635         "p" ,  
23636         "h1","h2","h3","h4","h5","h6", 
23637         "pre", "code", 
23638         "abbr", "acronym", "address", "cite", "samp", "var",
23639         'div','span'
23640     ],
23641     
23642     onRender : function(ct, position)
23643     {
23644        // Roo.log("Call onRender: " + this.xtype);
23645         
23646        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23647        Roo.log(this.el);
23648        this.el.dom.style.marginBottom = '0';
23649        var _this = this;
23650        var editorcore = this.editorcore;
23651        var editor= this.editor;
23652        
23653        var children = [];
23654        var btn = function(id,cmd , toggle, handler, html){
23655        
23656             var  event = toggle ? 'toggle' : 'click';
23657        
23658             var a = {
23659                 size : 'sm',
23660                 xtype: 'Button',
23661                 xns: Roo.bootstrap,
23662                 glyphicon : id,
23663                 cmd : id || cmd,
23664                 enableToggle:toggle !== false,
23665                 html : html || '',
23666                 pressed : toggle ? false : null,
23667                 listeners : {}
23668             };
23669             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23670                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23671             };
23672             children.push(a);
23673             return a;
23674        }
23675        
23676     //    var cb_box = function...
23677         
23678         var style = {
23679                 xtype: 'Button',
23680                 size : 'sm',
23681                 xns: Roo.bootstrap,
23682                 glyphicon : 'font',
23683                 //html : 'submit'
23684                 menu : {
23685                     xtype: 'Menu',
23686                     xns: Roo.bootstrap,
23687                     items:  []
23688                 }
23689         };
23690         Roo.each(this.formats, function(f) {
23691             style.menu.items.push({
23692                 xtype :'MenuItem',
23693                 xns: Roo.bootstrap,
23694                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23695                 tagname : f,
23696                 listeners : {
23697                     click : function()
23698                     {
23699                         editorcore.insertTag(this.tagname);
23700                         editor.focus();
23701                     }
23702                 }
23703                 
23704             });
23705         });
23706         children.push(style);   
23707         
23708         btn('bold',false,true);
23709         btn('italic',false,true);
23710         btn('align-left', 'justifyleft',true);
23711         btn('align-center', 'justifycenter',true);
23712         btn('align-right' , 'justifyright',true);
23713         btn('link', false, false, function(btn) {
23714             //Roo.log("create link?");
23715             var url = prompt(this.createLinkText, this.defaultLinkValue);
23716             if(url && url != 'http:/'+'/'){
23717                 this.editorcore.relayCmd('createlink', url);
23718             }
23719         }),
23720         btn('list','insertunorderedlist',true);
23721         btn('pencil', false,true, function(btn){
23722                 Roo.log(this);
23723                 this.toggleSourceEdit(btn.pressed);
23724         });
23725         
23726         if (this.editor.btns.length > 0) {
23727             for (var i = 0; i<this.editor.btns.length; i++) {
23728                 children.push(this.editor.btns[i]);
23729             }
23730         }
23731         
23732         /*
23733         var cog = {
23734                 xtype: 'Button',
23735                 size : 'sm',
23736                 xns: Roo.bootstrap,
23737                 glyphicon : 'cog',
23738                 //html : 'submit'
23739                 menu : {
23740                     xtype: 'Menu',
23741                     xns: Roo.bootstrap,
23742                     items:  []
23743                 }
23744         };
23745         
23746         cog.menu.items.push({
23747             xtype :'MenuItem',
23748             xns: Roo.bootstrap,
23749             html : Clean styles,
23750             tagname : f,
23751             listeners : {
23752                 click : function()
23753                 {
23754                     editorcore.insertTag(this.tagname);
23755                     editor.focus();
23756                 }
23757             }
23758             
23759         });
23760        */
23761         
23762          
23763        this.xtype = 'NavSimplebar';
23764         
23765         for(var i=0;i< children.length;i++) {
23766             
23767             this.buttons.add(this.addxtypeChild(children[i]));
23768             
23769         }
23770         
23771         editor.on('editorevent', this.updateToolbar, this);
23772     },
23773     onBtnClick : function(id)
23774     {
23775        this.editorcore.relayCmd(id);
23776        this.editorcore.focus();
23777     },
23778     
23779     /**
23780      * Protected method that will not generally be called directly. It triggers
23781      * a toolbar update by reading the markup state of the current selection in the editor.
23782      */
23783     updateToolbar: function(){
23784
23785         if(!this.editorcore.activated){
23786             this.editor.onFirstFocus(); // is this neeed?
23787             return;
23788         }
23789
23790         var btns = this.buttons; 
23791         var doc = this.editorcore.doc;
23792         btns.get('bold').setActive(doc.queryCommandState('bold'));
23793         btns.get('italic').setActive(doc.queryCommandState('italic'));
23794         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23795         
23796         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23797         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23798         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23799         
23800         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23801         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23802          /*
23803         
23804         var ans = this.editorcore.getAllAncestors();
23805         if (this.formatCombo) {
23806             
23807             
23808             var store = this.formatCombo.store;
23809             this.formatCombo.setValue("");
23810             for (var i =0; i < ans.length;i++) {
23811                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23812                     // select it..
23813                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23814                     break;
23815                 }
23816             }
23817         }
23818         
23819         
23820         
23821         // hides menus... - so this cant be on a menu...
23822         Roo.bootstrap.MenuMgr.hideAll();
23823         */
23824         Roo.bootstrap.MenuMgr.hideAll();
23825         //this.editorsyncValue();
23826     },
23827     onFirstFocus: function() {
23828         this.buttons.each(function(item){
23829            item.enable();
23830         });
23831     },
23832     toggleSourceEdit : function(sourceEditMode){
23833         
23834           
23835         if(sourceEditMode){
23836             Roo.log("disabling buttons");
23837            this.buttons.each( function(item){
23838                 if(item.cmd != 'pencil'){
23839                     item.disable();
23840                 }
23841             });
23842           
23843         }else{
23844             Roo.log("enabling buttons");
23845             if(this.editorcore.initialized){
23846                 this.buttons.each( function(item){
23847                     item.enable();
23848                 });
23849             }
23850             
23851         }
23852         Roo.log("calling toggole on editor");
23853         // tell the editor that it's been pressed..
23854         this.editor.toggleSourceEdit(sourceEditMode);
23855        
23856     }
23857 });
23858
23859
23860
23861
23862
23863 /**
23864  * @class Roo.bootstrap.Table.AbstractSelectionModel
23865  * @extends Roo.util.Observable
23866  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23867  * implemented by descendant classes.  This class should not be directly instantiated.
23868  * @constructor
23869  */
23870 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23871     this.locked = false;
23872     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23873 };
23874
23875
23876 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23877     /** @ignore Called by the grid automatically. Do not call directly. */
23878     init : function(grid){
23879         this.grid = grid;
23880         this.initEvents();
23881     },
23882
23883     /**
23884      * Locks the selections.
23885      */
23886     lock : function(){
23887         this.locked = true;
23888     },
23889
23890     /**
23891      * Unlocks the selections.
23892      */
23893     unlock : function(){
23894         this.locked = false;
23895     },
23896
23897     /**
23898      * Returns true if the selections are locked.
23899      * @return {Boolean}
23900      */
23901     isLocked : function(){
23902         return this.locked;
23903     }
23904 });
23905 /**
23906  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23907  * @class Roo.bootstrap.Table.RowSelectionModel
23908  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23909  * It supports multiple selections and keyboard selection/navigation. 
23910  * @constructor
23911  * @param {Object} config
23912  */
23913
23914 Roo.bootstrap.Table.RowSelectionModel = function(config){
23915     Roo.apply(this, config);
23916     this.selections = new Roo.util.MixedCollection(false, function(o){
23917         return o.id;
23918     });
23919
23920     this.last = false;
23921     this.lastActive = false;
23922
23923     this.addEvents({
23924         /**
23925              * @event selectionchange
23926              * Fires when the selection changes
23927              * @param {SelectionModel} this
23928              */
23929             "selectionchange" : true,
23930         /**
23931              * @event afterselectionchange
23932              * Fires after the selection changes (eg. by key press or clicking)
23933              * @param {SelectionModel} this
23934              */
23935             "afterselectionchange" : true,
23936         /**
23937              * @event beforerowselect
23938              * Fires when a row is selected being selected, return false to cancel.
23939              * @param {SelectionModel} this
23940              * @param {Number} rowIndex The selected index
23941              * @param {Boolean} keepExisting False if other selections will be cleared
23942              */
23943             "beforerowselect" : true,
23944         /**
23945              * @event rowselect
23946              * Fires when a row is selected.
23947              * @param {SelectionModel} this
23948              * @param {Number} rowIndex The selected index
23949              * @param {Roo.data.Record} r The record
23950              */
23951             "rowselect" : true,
23952         /**
23953              * @event rowdeselect
23954              * Fires when a row is deselected.
23955              * @param {SelectionModel} this
23956              * @param {Number} rowIndex The selected index
23957              */
23958         "rowdeselect" : true
23959     });
23960     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23961     this.locked = false;
23962  };
23963
23964 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23965     /**
23966      * @cfg {Boolean} singleSelect
23967      * True to allow selection of only one row at a time (defaults to false)
23968      */
23969     singleSelect : false,
23970
23971     // private
23972     initEvents : function()
23973     {
23974
23975         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23976         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23977         //}else{ // allow click to work like normal
23978          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23979         //}
23980         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23981         this.grid.on("rowclick", this.handleMouseDown, this);
23982         
23983         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23984             "up" : function(e){
23985                 if(!e.shiftKey){
23986                     this.selectPrevious(e.shiftKey);
23987                 }else if(this.last !== false && this.lastActive !== false){
23988                     var last = this.last;
23989                     this.selectRange(this.last,  this.lastActive-1);
23990                     this.grid.getView().focusRow(this.lastActive);
23991                     if(last !== false){
23992                         this.last = last;
23993                     }
23994                 }else{
23995                     this.selectFirstRow();
23996                 }
23997                 this.fireEvent("afterselectionchange", this);
23998             },
23999             "down" : function(e){
24000                 if(!e.shiftKey){
24001                     this.selectNext(e.shiftKey);
24002                 }else if(this.last !== false && this.lastActive !== false){
24003                     var last = this.last;
24004                     this.selectRange(this.last,  this.lastActive+1);
24005                     this.grid.getView().focusRow(this.lastActive);
24006                     if(last !== false){
24007                         this.last = last;
24008                     }
24009                 }else{
24010                     this.selectFirstRow();
24011                 }
24012                 this.fireEvent("afterselectionchange", this);
24013             },
24014             scope: this
24015         });
24016         this.grid.store.on('load', function(){
24017             this.selections.clear();
24018         },this);
24019         /*
24020         var view = this.grid.view;
24021         view.on("refresh", this.onRefresh, this);
24022         view.on("rowupdated", this.onRowUpdated, this);
24023         view.on("rowremoved", this.onRemove, this);
24024         */
24025     },
24026
24027     // private
24028     onRefresh : function()
24029     {
24030         var ds = this.grid.store, i, v = this.grid.view;
24031         var s = this.selections;
24032         s.each(function(r){
24033             if((i = ds.indexOfId(r.id)) != -1){
24034                 v.onRowSelect(i);
24035             }else{
24036                 s.remove(r);
24037             }
24038         });
24039     },
24040
24041     // private
24042     onRemove : function(v, index, r){
24043         this.selections.remove(r);
24044     },
24045
24046     // private
24047     onRowUpdated : function(v, index, r){
24048         if(this.isSelected(r)){
24049             v.onRowSelect(index);
24050         }
24051     },
24052
24053     /**
24054      * Select records.
24055      * @param {Array} records The records to select
24056      * @param {Boolean} keepExisting (optional) True to keep existing selections
24057      */
24058     selectRecords : function(records, keepExisting)
24059     {
24060         if(!keepExisting){
24061             this.clearSelections();
24062         }
24063             var ds = this.grid.store;
24064         for(var i = 0, len = records.length; i < len; i++){
24065             this.selectRow(ds.indexOf(records[i]), true);
24066         }
24067     },
24068
24069     /**
24070      * Gets the number of selected rows.
24071      * @return {Number}
24072      */
24073     getCount : function(){
24074         return this.selections.length;
24075     },
24076
24077     /**
24078      * Selects the first row in the grid.
24079      */
24080     selectFirstRow : function(){
24081         this.selectRow(0);
24082     },
24083
24084     /**
24085      * Select the last row.
24086      * @param {Boolean} keepExisting (optional) True to keep existing selections
24087      */
24088     selectLastRow : function(keepExisting){
24089         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24090         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24091     },
24092
24093     /**
24094      * Selects the row immediately following the last selected row.
24095      * @param {Boolean} keepExisting (optional) True to keep existing selections
24096      */
24097     selectNext : function(keepExisting)
24098     {
24099             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24100             this.selectRow(this.last+1, keepExisting);
24101             this.grid.getView().focusRow(this.last);
24102         }
24103     },
24104
24105     /**
24106      * Selects the row that precedes the last selected row.
24107      * @param {Boolean} keepExisting (optional) True to keep existing selections
24108      */
24109     selectPrevious : function(keepExisting){
24110         if(this.last){
24111             this.selectRow(this.last-1, keepExisting);
24112             this.grid.getView().focusRow(this.last);
24113         }
24114     },
24115
24116     /**
24117      * Returns the selected records
24118      * @return {Array} Array of selected records
24119      */
24120     getSelections : function(){
24121         return [].concat(this.selections.items);
24122     },
24123
24124     /**
24125      * Returns the first selected record.
24126      * @return {Record}
24127      */
24128     getSelected : function(){
24129         return this.selections.itemAt(0);
24130     },
24131
24132
24133     /**
24134      * Clears all selections.
24135      */
24136     clearSelections : function(fast)
24137     {
24138         if(this.locked) {
24139             return;
24140         }
24141         if(fast !== true){
24142                 var ds = this.grid.store;
24143             var s = this.selections;
24144             s.each(function(r){
24145                 this.deselectRow(ds.indexOfId(r.id));
24146             }, this);
24147             s.clear();
24148         }else{
24149             this.selections.clear();
24150         }
24151         this.last = false;
24152     },
24153
24154
24155     /**
24156      * Selects all rows.
24157      */
24158     selectAll : function(){
24159         if(this.locked) {
24160             return;
24161         }
24162         this.selections.clear();
24163         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24164             this.selectRow(i, true);
24165         }
24166     },
24167
24168     /**
24169      * Returns True if there is a selection.
24170      * @return {Boolean}
24171      */
24172     hasSelection : function(){
24173         return this.selections.length > 0;
24174     },
24175
24176     /**
24177      * Returns True if the specified row is selected.
24178      * @param {Number/Record} record The record or index of the record to check
24179      * @return {Boolean}
24180      */
24181     isSelected : function(index){
24182             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24183         return (r && this.selections.key(r.id) ? true : false);
24184     },
24185
24186     /**
24187      * Returns True if the specified record id is selected.
24188      * @param {String} id The id of record to check
24189      * @return {Boolean}
24190      */
24191     isIdSelected : function(id){
24192         return (this.selections.key(id) ? true : false);
24193     },
24194
24195
24196     // private
24197     handleMouseDBClick : function(e, t){
24198         
24199     },
24200     // private
24201     handleMouseDown : function(e, t)
24202     {
24203             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24204         if(this.isLocked() || rowIndex < 0 ){
24205             return;
24206         };
24207         if(e.shiftKey && this.last !== false){
24208             var last = this.last;
24209             this.selectRange(last, rowIndex, e.ctrlKey);
24210             this.last = last; // reset the last
24211             t.focus();
24212     
24213         }else{
24214             var isSelected = this.isSelected(rowIndex);
24215             //Roo.log("select row:" + rowIndex);
24216             if(isSelected){
24217                 this.deselectRow(rowIndex);
24218             } else {
24219                         this.selectRow(rowIndex, true);
24220             }
24221     
24222             /*
24223                 if(e.button !== 0 && isSelected){
24224                 alert('rowIndex 2: ' + rowIndex);
24225                     view.focusRow(rowIndex);
24226                 }else if(e.ctrlKey && isSelected){
24227                     this.deselectRow(rowIndex);
24228                 }else if(!isSelected){
24229                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24230                     view.focusRow(rowIndex);
24231                 }
24232             */
24233         }
24234         this.fireEvent("afterselectionchange", this);
24235     },
24236     // private
24237     handleDragableRowClick :  function(grid, rowIndex, e) 
24238     {
24239         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24240             this.selectRow(rowIndex, false);
24241             grid.view.focusRow(rowIndex);
24242              this.fireEvent("afterselectionchange", this);
24243         }
24244     },
24245     
24246     /**
24247      * Selects multiple rows.
24248      * @param {Array} rows Array of the indexes of the row to select
24249      * @param {Boolean} keepExisting (optional) True to keep existing selections
24250      */
24251     selectRows : function(rows, keepExisting){
24252         if(!keepExisting){
24253             this.clearSelections();
24254         }
24255         for(var i = 0, len = rows.length; i < len; i++){
24256             this.selectRow(rows[i], true);
24257         }
24258     },
24259
24260     /**
24261      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24262      * @param {Number} startRow The index of the first row in the range
24263      * @param {Number} endRow The index of the last row in the range
24264      * @param {Boolean} keepExisting (optional) True to retain existing selections
24265      */
24266     selectRange : function(startRow, endRow, keepExisting){
24267         if(this.locked) {
24268             return;
24269         }
24270         if(!keepExisting){
24271             this.clearSelections();
24272         }
24273         if(startRow <= endRow){
24274             for(var i = startRow; i <= endRow; i++){
24275                 this.selectRow(i, true);
24276             }
24277         }else{
24278             for(var i = startRow; i >= endRow; i--){
24279                 this.selectRow(i, true);
24280             }
24281         }
24282     },
24283
24284     /**
24285      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24286      * @param {Number} startRow The index of the first row in the range
24287      * @param {Number} endRow The index of the last row in the range
24288      */
24289     deselectRange : function(startRow, endRow, preventViewNotify){
24290         if(this.locked) {
24291             return;
24292         }
24293         for(var i = startRow; i <= endRow; i++){
24294             this.deselectRow(i, preventViewNotify);
24295         }
24296     },
24297
24298     /**
24299      * Selects a row.
24300      * @param {Number} row The index of the row to select
24301      * @param {Boolean} keepExisting (optional) True to keep existing selections
24302      */
24303     selectRow : function(index, keepExisting, preventViewNotify)
24304     {
24305             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24306             return;
24307         }
24308         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24309             if(!keepExisting || this.singleSelect){
24310                 this.clearSelections();
24311             }
24312             
24313             var r = this.grid.store.getAt(index);
24314             //console.log('selectRow - record id :' + r.id);
24315             
24316             this.selections.add(r);
24317             this.last = this.lastActive = index;
24318             if(!preventViewNotify){
24319                 var proxy = new Roo.Element(
24320                                 this.grid.getRowDom(index)
24321                 );
24322                 proxy.addClass('bg-info info');
24323             }
24324             this.fireEvent("rowselect", this, index, r);
24325             this.fireEvent("selectionchange", this);
24326         }
24327     },
24328
24329     /**
24330      * Deselects a row.
24331      * @param {Number} row The index of the row to deselect
24332      */
24333     deselectRow : function(index, preventViewNotify)
24334     {
24335         if(this.locked) {
24336             return;
24337         }
24338         if(this.last == index){
24339             this.last = false;
24340         }
24341         if(this.lastActive == index){
24342             this.lastActive = false;
24343         }
24344         
24345         var r = this.grid.store.getAt(index);
24346         if (!r) {
24347             return;
24348         }
24349         
24350         this.selections.remove(r);
24351         //.console.log('deselectRow - record id :' + r.id);
24352         if(!preventViewNotify){
24353         
24354             var proxy = new Roo.Element(
24355                 this.grid.getRowDom(index)
24356             );
24357             proxy.removeClass('bg-info info');
24358         }
24359         this.fireEvent("rowdeselect", this, index);
24360         this.fireEvent("selectionchange", this);
24361     },
24362
24363     // private
24364     restoreLast : function(){
24365         if(this._last){
24366             this.last = this._last;
24367         }
24368     },
24369
24370     // private
24371     acceptsNav : function(row, col, cm){
24372         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24373     },
24374
24375     // private
24376     onEditorKey : function(field, e){
24377         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24378         if(k == e.TAB){
24379             e.stopEvent();
24380             ed.completeEdit();
24381             if(e.shiftKey){
24382                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24383             }else{
24384                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24385             }
24386         }else if(k == e.ENTER && !e.ctrlKey){
24387             e.stopEvent();
24388             ed.completeEdit();
24389             if(e.shiftKey){
24390                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24391             }else{
24392                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24393             }
24394         }else if(k == e.ESC){
24395             ed.cancelEdit();
24396         }
24397         if(newCell){
24398             g.startEditing(newCell[0], newCell[1]);
24399         }
24400     }
24401 });
24402 /*
24403  * Based on:
24404  * Ext JS Library 1.1.1
24405  * Copyright(c) 2006-2007, Ext JS, LLC.
24406  *
24407  * Originally Released Under LGPL - original licence link has changed is not relivant.
24408  *
24409  * Fork - LGPL
24410  * <script type="text/javascript">
24411  */
24412  
24413 /**
24414  * @class Roo.bootstrap.PagingToolbar
24415  * @extends Roo.bootstrap.NavSimplebar
24416  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24417  * @constructor
24418  * Create a new PagingToolbar
24419  * @param {Object} config The config object
24420  * @param {Roo.data.Store} store
24421  */
24422 Roo.bootstrap.PagingToolbar = function(config)
24423 {
24424     // old args format still supported... - xtype is prefered..
24425         // created from xtype...
24426     
24427     this.ds = config.dataSource;
24428     
24429     if (config.store && !this.ds) {
24430         this.store= Roo.factory(config.store, Roo.data);
24431         this.ds = this.store;
24432         this.ds.xmodule = this.xmodule || false;
24433     }
24434     
24435     this.toolbarItems = [];
24436     if (config.items) {
24437         this.toolbarItems = config.items;
24438     }
24439     
24440     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24441     
24442     this.cursor = 0;
24443     
24444     if (this.ds) { 
24445         this.bind(this.ds);
24446     }
24447     
24448     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24449     
24450 };
24451
24452 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24453     /**
24454      * @cfg {Roo.data.Store} dataSource
24455      * The underlying data store providing the paged data
24456      */
24457     /**
24458      * @cfg {String/HTMLElement/Element} container
24459      * container The id or element that will contain the toolbar
24460      */
24461     /**
24462      * @cfg {Boolean} displayInfo
24463      * True to display the displayMsg (defaults to false)
24464      */
24465     /**
24466      * @cfg {Number} pageSize
24467      * The number of records to display per page (defaults to 20)
24468      */
24469     pageSize: 20,
24470     /**
24471      * @cfg {String} displayMsg
24472      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24473      */
24474     displayMsg : 'Displaying {0} - {1} of {2}',
24475     /**
24476      * @cfg {String} emptyMsg
24477      * The message to display when no records are found (defaults to "No data to display")
24478      */
24479     emptyMsg : 'No data to display',
24480     /**
24481      * Customizable piece of the default paging text (defaults to "Page")
24482      * @type String
24483      */
24484     beforePageText : "Page",
24485     /**
24486      * Customizable piece of the default paging text (defaults to "of %0")
24487      * @type String
24488      */
24489     afterPageText : "of {0}",
24490     /**
24491      * Customizable piece of the default paging text (defaults to "First Page")
24492      * @type String
24493      */
24494     firstText : "First Page",
24495     /**
24496      * Customizable piece of the default paging text (defaults to "Previous Page")
24497      * @type String
24498      */
24499     prevText : "Previous Page",
24500     /**
24501      * Customizable piece of the default paging text (defaults to "Next Page")
24502      * @type String
24503      */
24504     nextText : "Next Page",
24505     /**
24506      * Customizable piece of the default paging text (defaults to "Last Page")
24507      * @type String
24508      */
24509     lastText : "Last Page",
24510     /**
24511      * Customizable piece of the default paging text (defaults to "Refresh")
24512      * @type String
24513      */
24514     refreshText : "Refresh",
24515
24516     buttons : false,
24517     // private
24518     onRender : function(ct, position) 
24519     {
24520         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24521         this.navgroup.parentId = this.id;
24522         this.navgroup.onRender(this.el, null);
24523         // add the buttons to the navgroup
24524         
24525         if(this.displayInfo){
24526             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24527             this.displayEl = this.el.select('.x-paging-info', true).first();
24528 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24529 //            this.displayEl = navel.el.select('span',true).first();
24530         }
24531         
24532         var _this = this;
24533         
24534         if(this.buttons){
24535             Roo.each(_this.buttons, function(e){ // this might need to use render????
24536                Roo.factory(e).render(_this.el);
24537             });
24538         }
24539             
24540         Roo.each(_this.toolbarItems, function(e) {
24541             _this.navgroup.addItem(e);
24542         });
24543         
24544         
24545         this.first = this.navgroup.addItem({
24546             tooltip: this.firstText,
24547             cls: "prev",
24548             icon : 'fa fa-backward',
24549             disabled: true,
24550             preventDefault: true,
24551             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24552         });
24553         
24554         this.prev =  this.navgroup.addItem({
24555             tooltip: this.prevText,
24556             cls: "prev",
24557             icon : 'fa fa-step-backward',
24558             disabled: true,
24559             preventDefault: true,
24560             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24561         });
24562     //this.addSeparator();
24563         
24564         
24565         var field = this.navgroup.addItem( {
24566             tagtype : 'span',
24567             cls : 'x-paging-position',
24568             
24569             html : this.beforePageText  +
24570                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24571                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24572          } ); //?? escaped?
24573         
24574         this.field = field.el.select('input', true).first();
24575         this.field.on("keydown", this.onPagingKeydown, this);
24576         this.field.on("focus", function(){this.dom.select();});
24577     
24578     
24579         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24580         //this.field.setHeight(18);
24581         //this.addSeparator();
24582         this.next = this.navgroup.addItem({
24583             tooltip: this.nextText,
24584             cls: "next",
24585             html : ' <i class="fa fa-step-forward">',
24586             disabled: true,
24587             preventDefault: true,
24588             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24589         });
24590         this.last = this.navgroup.addItem({
24591             tooltip: this.lastText,
24592             icon : 'fa fa-forward',
24593             cls: "next",
24594             disabled: true,
24595             preventDefault: true,
24596             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24597         });
24598     //this.addSeparator();
24599         this.loading = this.navgroup.addItem({
24600             tooltip: this.refreshText,
24601             icon: 'fa fa-refresh',
24602             preventDefault: true,
24603             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24604         });
24605         
24606     },
24607
24608     // private
24609     updateInfo : function(){
24610         if(this.displayEl){
24611             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24612             var msg = count == 0 ?
24613                 this.emptyMsg :
24614                 String.format(
24615                     this.displayMsg,
24616                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24617                 );
24618             this.displayEl.update(msg);
24619         }
24620     },
24621
24622     // private
24623     onLoad : function(ds, r, o)
24624     {
24625         this.cursor = o.params.start ? o.params.start : 0;
24626         
24627         var d = this.getPageData(),
24628             ap = d.activePage,
24629             ps = d.pages;
24630         
24631         
24632         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24633         this.field.dom.value = ap;
24634         this.first.setDisabled(ap == 1);
24635         this.prev.setDisabled(ap == 1);
24636         this.next.setDisabled(ap == ps);
24637         this.last.setDisabled(ap == ps);
24638         this.loading.enable();
24639         this.updateInfo();
24640     },
24641
24642     // private
24643     getPageData : function(){
24644         var total = this.ds.getTotalCount();
24645         return {
24646             total : total,
24647             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24648             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24649         };
24650     },
24651
24652     // private
24653     onLoadError : function(){
24654         this.loading.enable();
24655     },
24656
24657     // private
24658     onPagingKeydown : function(e){
24659         var k = e.getKey();
24660         var d = this.getPageData();
24661         if(k == e.RETURN){
24662             var v = this.field.dom.value, pageNum;
24663             if(!v || isNaN(pageNum = parseInt(v, 10))){
24664                 this.field.dom.value = d.activePage;
24665                 return;
24666             }
24667             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24668             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24669             e.stopEvent();
24670         }
24671         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))
24672         {
24673           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24674           this.field.dom.value = pageNum;
24675           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24676           e.stopEvent();
24677         }
24678         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24679         {
24680           var v = this.field.dom.value, pageNum; 
24681           var increment = (e.shiftKey) ? 10 : 1;
24682           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24683                 increment *= -1;
24684           }
24685           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24686             this.field.dom.value = d.activePage;
24687             return;
24688           }
24689           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24690           {
24691             this.field.dom.value = parseInt(v, 10) + increment;
24692             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24693             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24694           }
24695           e.stopEvent();
24696         }
24697     },
24698
24699     // private
24700     beforeLoad : function(){
24701         if(this.loading){
24702             this.loading.disable();
24703         }
24704     },
24705
24706     // private
24707     onClick : function(which){
24708         
24709         var ds = this.ds;
24710         if (!ds) {
24711             return;
24712         }
24713         
24714         switch(which){
24715             case "first":
24716                 ds.load({params:{start: 0, limit: this.pageSize}});
24717             break;
24718             case "prev":
24719                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24720             break;
24721             case "next":
24722                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24723             break;
24724             case "last":
24725                 var total = ds.getTotalCount();
24726                 var extra = total % this.pageSize;
24727                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24728                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24729             break;
24730             case "refresh":
24731                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24732             break;
24733         }
24734     },
24735
24736     /**
24737      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24738      * @param {Roo.data.Store} store The data store to unbind
24739      */
24740     unbind : function(ds){
24741         ds.un("beforeload", this.beforeLoad, this);
24742         ds.un("load", this.onLoad, this);
24743         ds.un("loadexception", this.onLoadError, this);
24744         ds.un("remove", this.updateInfo, this);
24745         ds.un("add", this.updateInfo, this);
24746         this.ds = undefined;
24747     },
24748
24749     /**
24750      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24751      * @param {Roo.data.Store} store The data store to bind
24752      */
24753     bind : function(ds){
24754         ds.on("beforeload", this.beforeLoad, this);
24755         ds.on("load", this.onLoad, this);
24756         ds.on("loadexception", this.onLoadError, this);
24757         ds.on("remove", this.updateInfo, this);
24758         ds.on("add", this.updateInfo, this);
24759         this.ds = ds;
24760     }
24761 });/*
24762  * - LGPL
24763  *
24764  * element
24765  * 
24766  */
24767
24768 /**
24769  * @class Roo.bootstrap.MessageBar
24770  * @extends Roo.bootstrap.Component
24771  * Bootstrap MessageBar class
24772  * @cfg {String} html contents of the MessageBar
24773  * @cfg {String} weight (info | success | warning | danger) default info
24774  * @cfg {String} beforeClass insert the bar before the given class
24775  * @cfg {Boolean} closable (true | false) default false
24776  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24777  * 
24778  * @constructor
24779  * Create a new Element
24780  * @param {Object} config The config object
24781  */
24782
24783 Roo.bootstrap.MessageBar = function(config){
24784     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24785 };
24786
24787 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24788     
24789     html: '',
24790     weight: 'info',
24791     closable: false,
24792     fixed: false,
24793     beforeClass: 'bootstrap-sticky-wrap',
24794     
24795     getAutoCreate : function(){
24796         
24797         var cfg = {
24798             tag: 'div',
24799             cls: 'alert alert-dismissable alert-' + this.weight,
24800             cn: [
24801                 {
24802                     tag: 'span',
24803                     cls: 'message',
24804                     html: this.html || ''
24805                 }
24806             ]
24807         };
24808         
24809         if(this.fixed){
24810             cfg.cls += ' alert-messages-fixed';
24811         }
24812         
24813         if(this.closable){
24814             cfg.cn.push({
24815                 tag: 'button',
24816                 cls: 'close',
24817                 html: 'x'
24818             });
24819         }
24820         
24821         return cfg;
24822     },
24823     
24824     onRender : function(ct, position)
24825     {
24826         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24827         
24828         if(!this.el){
24829             var cfg = Roo.apply({},  this.getAutoCreate());
24830             cfg.id = Roo.id();
24831             
24832             if (this.cls) {
24833                 cfg.cls += ' ' + this.cls;
24834             }
24835             if (this.style) {
24836                 cfg.style = this.style;
24837             }
24838             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24839             
24840             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24841         }
24842         
24843         this.el.select('>button.close').on('click', this.hide, this);
24844         
24845     },
24846     
24847     show : function()
24848     {
24849         if (!this.rendered) {
24850             this.render();
24851         }
24852         
24853         this.el.show();
24854         
24855         this.fireEvent('show', this);
24856         
24857     },
24858     
24859     hide : function()
24860     {
24861         if (!this.rendered) {
24862             this.render();
24863         }
24864         
24865         this.el.hide();
24866         
24867         this.fireEvent('hide', this);
24868     },
24869     
24870     update : function()
24871     {
24872 //        var e = this.el.dom.firstChild;
24873 //        
24874 //        if(this.closable){
24875 //            e = e.nextSibling;
24876 //        }
24877 //        
24878 //        e.data = this.html || '';
24879
24880         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24881     }
24882    
24883 });
24884
24885  
24886
24887      /*
24888  * - LGPL
24889  *
24890  * Graph
24891  * 
24892  */
24893
24894
24895 /**
24896  * @class Roo.bootstrap.Graph
24897  * @extends Roo.bootstrap.Component
24898  * Bootstrap Graph class
24899 > Prameters
24900  -sm {number} sm 4
24901  -md {number} md 5
24902  @cfg {String} graphtype  bar | vbar | pie
24903  @cfg {number} g_x coodinator | centre x (pie)
24904  @cfg {number} g_y coodinator | centre y (pie)
24905  @cfg {number} g_r radius (pie)
24906  @cfg {number} g_height height of the chart (respected by all elements in the set)
24907  @cfg {number} g_width width of the chart (respected by all elements in the set)
24908  @cfg {Object} title The title of the chart
24909     
24910  -{Array}  values
24911  -opts (object) options for the chart 
24912      o {
24913      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24914      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24915      o vgutter (number)
24916      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.
24917      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24918      o to
24919      o stretch (boolean)
24920      o }
24921  -opts (object) options for the pie
24922      o{
24923      o cut
24924      o startAngle (number)
24925      o endAngle (number)
24926      } 
24927  *
24928  * @constructor
24929  * Create a new Input
24930  * @param {Object} config The config object
24931  */
24932
24933 Roo.bootstrap.Graph = function(config){
24934     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24935     
24936     this.addEvents({
24937         // img events
24938         /**
24939          * @event click
24940          * The img click event for the img.
24941          * @param {Roo.EventObject} e
24942          */
24943         "click" : true
24944     });
24945 };
24946
24947 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24948     
24949     sm: 4,
24950     md: 5,
24951     graphtype: 'bar',
24952     g_height: 250,
24953     g_width: 400,
24954     g_x: 50,
24955     g_y: 50,
24956     g_r: 30,
24957     opts:{
24958         //g_colors: this.colors,
24959         g_type: 'soft',
24960         g_gutter: '20%'
24961
24962     },
24963     title : false,
24964
24965     getAutoCreate : function(){
24966         
24967         var cfg = {
24968             tag: 'div',
24969             html : null
24970         };
24971         
24972         
24973         return  cfg;
24974     },
24975
24976     onRender : function(ct,position){
24977         
24978         
24979         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24980         
24981         if (typeof(Raphael) == 'undefined') {
24982             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24983             return;
24984         }
24985         
24986         this.raphael = Raphael(this.el.dom);
24987         
24988                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24989                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24990                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24991                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24992                 /*
24993                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24994                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24995                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24996                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24997                 
24998                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24999                 r.barchart(330, 10, 300, 220, data1);
25000                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25001                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25002                 */
25003                 
25004                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25005                 // r.barchart(30, 30, 560, 250,  xdata, {
25006                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25007                 //     axis : "0 0 1 1",
25008                 //     axisxlabels :  xdata
25009                 //     //yvalues : cols,
25010                    
25011                 // });
25012 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25013 //        
25014 //        this.load(null,xdata,{
25015 //                axis : "0 0 1 1",
25016 //                axisxlabels :  xdata
25017 //                });
25018
25019     },
25020
25021     load : function(graphtype,xdata,opts)
25022     {
25023         this.raphael.clear();
25024         if(!graphtype) {
25025             graphtype = this.graphtype;
25026         }
25027         if(!opts){
25028             opts = this.opts;
25029         }
25030         var r = this.raphael,
25031             fin = function () {
25032                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25033             },
25034             fout = function () {
25035                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25036             },
25037             pfin = function() {
25038                 this.sector.stop();
25039                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25040
25041                 if (this.label) {
25042                     this.label[0].stop();
25043                     this.label[0].attr({ r: 7.5 });
25044                     this.label[1].attr({ "font-weight": 800 });
25045                 }
25046             },
25047             pfout = function() {
25048                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25049
25050                 if (this.label) {
25051                     this.label[0].animate({ r: 5 }, 500, "bounce");
25052                     this.label[1].attr({ "font-weight": 400 });
25053                 }
25054             };
25055
25056         switch(graphtype){
25057             case 'bar':
25058                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25059                 break;
25060             case 'hbar':
25061                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25062                 break;
25063             case 'pie':
25064 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25065 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25066 //            
25067                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25068                 
25069                 break;
25070
25071         }
25072         
25073         if(this.title){
25074             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25075         }
25076         
25077     },
25078     
25079     setTitle: function(o)
25080     {
25081         this.title = o;
25082     },
25083     
25084     initEvents: function() {
25085         
25086         if(!this.href){
25087             this.el.on('click', this.onClick, this);
25088         }
25089     },
25090     
25091     onClick : function(e)
25092     {
25093         Roo.log('img onclick');
25094         this.fireEvent('click', this, e);
25095     }
25096    
25097 });
25098
25099  
25100 /*
25101  * - LGPL
25102  *
25103  * numberBox
25104  * 
25105  */
25106 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25107
25108 /**
25109  * @class Roo.bootstrap.dash.NumberBox
25110  * @extends Roo.bootstrap.Component
25111  * Bootstrap NumberBox class
25112  * @cfg {String} headline Box headline
25113  * @cfg {String} content Box content
25114  * @cfg {String} icon Box icon
25115  * @cfg {String} footer Footer text
25116  * @cfg {String} fhref Footer href
25117  * 
25118  * @constructor
25119  * Create a new NumberBox
25120  * @param {Object} config The config object
25121  */
25122
25123
25124 Roo.bootstrap.dash.NumberBox = function(config){
25125     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25126     
25127 };
25128
25129 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25130     
25131     headline : '',
25132     content : '',
25133     icon : '',
25134     footer : '',
25135     fhref : '',
25136     ficon : '',
25137     
25138     getAutoCreate : function(){
25139         
25140         var cfg = {
25141             tag : 'div',
25142             cls : 'small-box ',
25143             cn : [
25144                 {
25145                     tag : 'div',
25146                     cls : 'inner',
25147                     cn :[
25148                         {
25149                             tag : 'h3',
25150                             cls : 'roo-headline',
25151                             html : this.headline
25152                         },
25153                         {
25154                             tag : 'p',
25155                             cls : 'roo-content',
25156                             html : this.content
25157                         }
25158                     ]
25159                 }
25160             ]
25161         };
25162         
25163         if(this.icon){
25164             cfg.cn.push({
25165                 tag : 'div',
25166                 cls : 'icon',
25167                 cn :[
25168                     {
25169                         tag : 'i',
25170                         cls : 'ion ' + this.icon
25171                     }
25172                 ]
25173             });
25174         }
25175         
25176         if(this.footer){
25177             var footer = {
25178                 tag : 'a',
25179                 cls : 'small-box-footer',
25180                 href : this.fhref || '#',
25181                 html : this.footer
25182             };
25183             
25184             cfg.cn.push(footer);
25185             
25186         }
25187         
25188         return  cfg;
25189     },
25190
25191     onRender : function(ct,position){
25192         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25193
25194
25195        
25196                 
25197     },
25198
25199     setHeadline: function (value)
25200     {
25201         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25202     },
25203     
25204     setFooter: function (value, href)
25205     {
25206         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25207         
25208         if(href){
25209             this.el.select('a.small-box-footer',true).first().attr('href', href);
25210         }
25211         
25212     },
25213
25214     setContent: function (value)
25215     {
25216         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25217     },
25218
25219     initEvents: function() 
25220     {   
25221         
25222     }
25223     
25224 });
25225
25226  
25227 /*
25228  * - LGPL
25229  *
25230  * TabBox
25231  * 
25232  */
25233 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25234
25235 /**
25236  * @class Roo.bootstrap.dash.TabBox
25237  * @extends Roo.bootstrap.Component
25238  * Bootstrap TabBox class
25239  * @cfg {String} title Title of the TabBox
25240  * @cfg {String} icon Icon of the TabBox
25241  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25242  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25243  * 
25244  * @constructor
25245  * Create a new TabBox
25246  * @param {Object} config The config object
25247  */
25248
25249
25250 Roo.bootstrap.dash.TabBox = function(config){
25251     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25252     this.addEvents({
25253         // raw events
25254         /**
25255          * @event addpane
25256          * When a pane is added
25257          * @param {Roo.bootstrap.dash.TabPane} pane
25258          */
25259         "addpane" : true,
25260         /**
25261          * @event activatepane
25262          * When a pane is activated
25263          * @param {Roo.bootstrap.dash.TabPane} pane
25264          */
25265         "activatepane" : true
25266         
25267          
25268     });
25269     
25270     this.panes = [];
25271 };
25272
25273 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25274
25275     title : '',
25276     icon : false,
25277     showtabs : true,
25278     tabScrollable : false,
25279     
25280     getChildContainer : function()
25281     {
25282         return this.el.select('.tab-content', true).first();
25283     },
25284     
25285     getAutoCreate : function(){
25286         
25287         var header = {
25288             tag: 'li',
25289             cls: 'pull-left header',
25290             html: this.title,
25291             cn : []
25292         };
25293         
25294         if(this.icon){
25295             header.cn.push({
25296                 tag: 'i',
25297                 cls: 'fa ' + this.icon
25298             });
25299         }
25300         
25301         var h = {
25302             tag: 'ul',
25303             cls: 'nav nav-tabs pull-right',
25304             cn: [
25305                 header
25306             ]
25307         };
25308         
25309         if(this.tabScrollable){
25310             h = {
25311                 tag: 'div',
25312                 cls: 'tab-header',
25313                 cn: [
25314                     {
25315                         tag: 'ul',
25316                         cls: 'nav nav-tabs pull-right',
25317                         cn: [
25318                             header
25319                         ]
25320                     }
25321                 ]
25322             };
25323         }
25324         
25325         var cfg = {
25326             tag: 'div',
25327             cls: 'nav-tabs-custom',
25328             cn: [
25329                 h,
25330                 {
25331                     tag: 'div',
25332                     cls: 'tab-content no-padding',
25333                     cn: []
25334                 }
25335             ]
25336         };
25337
25338         return  cfg;
25339     },
25340     initEvents : function()
25341     {
25342         //Roo.log('add add pane handler');
25343         this.on('addpane', this.onAddPane, this);
25344     },
25345      /**
25346      * Updates the box title
25347      * @param {String} html to set the title to.
25348      */
25349     setTitle : function(value)
25350     {
25351         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25352     },
25353     onAddPane : function(pane)
25354     {
25355         this.panes.push(pane);
25356         //Roo.log('addpane');
25357         //Roo.log(pane);
25358         // tabs are rendere left to right..
25359         if(!this.showtabs){
25360             return;
25361         }
25362         
25363         var ctr = this.el.select('.nav-tabs', true).first();
25364          
25365          
25366         var existing = ctr.select('.nav-tab',true);
25367         var qty = existing.getCount();;
25368         
25369         
25370         var tab = ctr.createChild({
25371             tag : 'li',
25372             cls : 'nav-tab' + (qty ? '' : ' active'),
25373             cn : [
25374                 {
25375                     tag : 'a',
25376                     href:'#',
25377                     html : pane.title
25378                 }
25379             ]
25380         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25381         pane.tab = tab;
25382         
25383         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25384         if (!qty) {
25385             pane.el.addClass('active');
25386         }
25387         
25388                 
25389     },
25390     onTabClick : function(ev,un,ob,pane)
25391     {
25392         //Roo.log('tab - prev default');
25393         ev.preventDefault();
25394         
25395         
25396         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25397         pane.tab.addClass('active');
25398         //Roo.log(pane.title);
25399         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25400         // technically we should have a deactivate event.. but maybe add later.
25401         // and it should not de-activate the selected tab...
25402         this.fireEvent('activatepane', pane);
25403         pane.el.addClass('active');
25404         pane.fireEvent('activate');
25405         
25406         
25407     },
25408     
25409     getActivePane : function()
25410     {
25411         var r = false;
25412         Roo.each(this.panes, function(p) {
25413             if(p.el.hasClass('active')){
25414                 r = p;
25415                 return false;
25416             }
25417             
25418             return;
25419         });
25420         
25421         return r;
25422     }
25423     
25424     
25425 });
25426
25427  
25428 /*
25429  * - LGPL
25430  *
25431  * Tab pane
25432  * 
25433  */
25434 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25435 /**
25436  * @class Roo.bootstrap.TabPane
25437  * @extends Roo.bootstrap.Component
25438  * Bootstrap TabPane class
25439  * @cfg {Boolean} active (false | true) Default false
25440  * @cfg {String} title title of panel
25441
25442  * 
25443  * @constructor
25444  * Create a new TabPane
25445  * @param {Object} config The config object
25446  */
25447
25448 Roo.bootstrap.dash.TabPane = function(config){
25449     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25450     
25451     this.addEvents({
25452         // raw events
25453         /**
25454          * @event activate
25455          * When a pane is activated
25456          * @param {Roo.bootstrap.dash.TabPane} pane
25457          */
25458         "activate" : true
25459          
25460     });
25461 };
25462
25463 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25464     
25465     active : false,
25466     title : '',
25467     
25468     // the tabBox that this is attached to.
25469     tab : false,
25470      
25471     getAutoCreate : function() 
25472     {
25473         var cfg = {
25474             tag: 'div',
25475             cls: 'tab-pane'
25476         };
25477         
25478         if(this.active){
25479             cfg.cls += ' active';
25480         }
25481         
25482         return cfg;
25483     },
25484     initEvents  : function()
25485     {
25486         //Roo.log('trigger add pane handler');
25487         this.parent().fireEvent('addpane', this)
25488     },
25489     
25490      /**
25491      * Updates the tab title 
25492      * @param {String} html to set the title to.
25493      */
25494     setTitle: function(str)
25495     {
25496         if (!this.tab) {
25497             return;
25498         }
25499         this.title = str;
25500         this.tab.select('a', true).first().dom.innerHTML = str;
25501         
25502     }
25503     
25504     
25505     
25506 });
25507
25508  
25509
25510
25511  /*
25512  * - LGPL
25513  *
25514  * menu
25515  * 
25516  */
25517 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25518
25519 /**
25520  * @class Roo.bootstrap.menu.Menu
25521  * @extends Roo.bootstrap.Component
25522  * Bootstrap Menu class - container for Menu
25523  * @cfg {String} html Text of the menu
25524  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25525  * @cfg {String} icon Font awesome icon
25526  * @cfg {String} pos Menu align to (top | bottom) default bottom
25527  * 
25528  * 
25529  * @constructor
25530  * Create a new Menu
25531  * @param {Object} config The config object
25532  */
25533
25534
25535 Roo.bootstrap.menu.Menu = function(config){
25536     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25537     
25538     this.addEvents({
25539         /**
25540          * @event beforeshow
25541          * Fires before this menu is displayed
25542          * @param {Roo.bootstrap.menu.Menu} this
25543          */
25544         beforeshow : true,
25545         /**
25546          * @event beforehide
25547          * Fires before this menu is hidden
25548          * @param {Roo.bootstrap.menu.Menu} this
25549          */
25550         beforehide : true,
25551         /**
25552          * @event show
25553          * Fires after this menu is displayed
25554          * @param {Roo.bootstrap.menu.Menu} this
25555          */
25556         show : true,
25557         /**
25558          * @event hide
25559          * Fires after this menu is hidden
25560          * @param {Roo.bootstrap.menu.Menu} this
25561          */
25562         hide : true,
25563         /**
25564          * @event click
25565          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25566          * @param {Roo.bootstrap.menu.Menu} this
25567          * @param {Roo.EventObject} e
25568          */
25569         click : true
25570     });
25571     
25572 };
25573
25574 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25575     
25576     submenu : false,
25577     html : '',
25578     weight : 'default',
25579     icon : false,
25580     pos : 'bottom',
25581     
25582     
25583     getChildContainer : function() {
25584         if(this.isSubMenu){
25585             return this.el;
25586         }
25587         
25588         return this.el.select('ul.dropdown-menu', true).first();  
25589     },
25590     
25591     getAutoCreate : function()
25592     {
25593         var text = [
25594             {
25595                 tag : 'span',
25596                 cls : 'roo-menu-text',
25597                 html : this.html
25598             }
25599         ];
25600         
25601         if(this.icon){
25602             text.unshift({
25603                 tag : 'i',
25604                 cls : 'fa ' + this.icon
25605             })
25606         }
25607         
25608         
25609         var cfg = {
25610             tag : 'div',
25611             cls : 'btn-group',
25612             cn : [
25613                 {
25614                     tag : 'button',
25615                     cls : 'dropdown-button btn btn-' + this.weight,
25616                     cn : text
25617                 },
25618                 {
25619                     tag : 'button',
25620                     cls : 'dropdown-toggle btn btn-' + this.weight,
25621                     cn : [
25622                         {
25623                             tag : 'span',
25624                             cls : 'caret'
25625                         }
25626                     ]
25627                 },
25628                 {
25629                     tag : 'ul',
25630                     cls : 'dropdown-menu'
25631                 }
25632             ]
25633             
25634         };
25635         
25636         if(this.pos == 'top'){
25637             cfg.cls += ' dropup';
25638         }
25639         
25640         if(this.isSubMenu){
25641             cfg = {
25642                 tag : 'ul',
25643                 cls : 'dropdown-menu'
25644             }
25645         }
25646         
25647         return cfg;
25648     },
25649     
25650     onRender : function(ct, position)
25651     {
25652         this.isSubMenu = ct.hasClass('dropdown-submenu');
25653         
25654         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25655     },
25656     
25657     initEvents : function() 
25658     {
25659         if(this.isSubMenu){
25660             return;
25661         }
25662         
25663         this.hidden = true;
25664         
25665         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25666         this.triggerEl.on('click', this.onTriggerPress, this);
25667         
25668         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25669         this.buttonEl.on('click', this.onClick, this);
25670         
25671     },
25672     
25673     list : function()
25674     {
25675         if(this.isSubMenu){
25676             return this.el;
25677         }
25678         
25679         return this.el.select('ul.dropdown-menu', true).first();
25680     },
25681     
25682     onClick : function(e)
25683     {
25684         this.fireEvent("click", this, e);
25685     },
25686     
25687     onTriggerPress  : function(e)
25688     {   
25689         if (this.isVisible()) {
25690             this.hide();
25691         } else {
25692             this.show();
25693         }
25694     },
25695     
25696     isVisible : function(){
25697         return !this.hidden;
25698     },
25699     
25700     show : function()
25701     {
25702         this.fireEvent("beforeshow", this);
25703         
25704         this.hidden = false;
25705         this.el.addClass('open');
25706         
25707         Roo.get(document).on("mouseup", this.onMouseUp, this);
25708         
25709         this.fireEvent("show", this);
25710         
25711         
25712     },
25713     
25714     hide : function()
25715     {
25716         this.fireEvent("beforehide", this);
25717         
25718         this.hidden = true;
25719         this.el.removeClass('open');
25720         
25721         Roo.get(document).un("mouseup", this.onMouseUp);
25722         
25723         this.fireEvent("hide", this);
25724     },
25725     
25726     onMouseUp : function()
25727     {
25728         this.hide();
25729     }
25730     
25731 });
25732
25733  
25734  /*
25735  * - LGPL
25736  *
25737  * menu item
25738  * 
25739  */
25740 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25741
25742 /**
25743  * @class Roo.bootstrap.menu.Item
25744  * @extends Roo.bootstrap.Component
25745  * Bootstrap MenuItem class
25746  * @cfg {Boolean} submenu (true | false) default false
25747  * @cfg {String} html text of the item
25748  * @cfg {String} href the link
25749  * @cfg {Boolean} disable (true | false) default false
25750  * @cfg {Boolean} preventDefault (true | false) default true
25751  * @cfg {String} icon Font awesome icon
25752  * @cfg {String} pos Submenu align to (left | right) default right 
25753  * 
25754  * 
25755  * @constructor
25756  * Create a new Item
25757  * @param {Object} config The config object
25758  */
25759
25760
25761 Roo.bootstrap.menu.Item = function(config){
25762     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25763     this.addEvents({
25764         /**
25765          * @event mouseover
25766          * Fires when the mouse is hovering over this menu
25767          * @param {Roo.bootstrap.menu.Item} this
25768          * @param {Roo.EventObject} e
25769          */
25770         mouseover : true,
25771         /**
25772          * @event mouseout
25773          * Fires when the mouse exits this menu
25774          * @param {Roo.bootstrap.menu.Item} this
25775          * @param {Roo.EventObject} e
25776          */
25777         mouseout : true,
25778         // raw events
25779         /**
25780          * @event click
25781          * The raw click event for the entire grid.
25782          * @param {Roo.EventObject} e
25783          */
25784         click : true
25785     });
25786 };
25787
25788 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25789     
25790     submenu : false,
25791     href : '',
25792     html : '',
25793     preventDefault: true,
25794     disable : false,
25795     icon : false,
25796     pos : 'right',
25797     
25798     getAutoCreate : function()
25799     {
25800         var text = [
25801             {
25802                 tag : 'span',
25803                 cls : 'roo-menu-item-text',
25804                 html : this.html
25805             }
25806         ];
25807         
25808         if(this.icon){
25809             text.unshift({
25810                 tag : 'i',
25811                 cls : 'fa ' + this.icon
25812             })
25813         }
25814         
25815         var cfg = {
25816             tag : 'li',
25817             cn : [
25818                 {
25819                     tag : 'a',
25820                     href : this.href || '#',
25821                     cn : text
25822                 }
25823             ]
25824         };
25825         
25826         if(this.disable){
25827             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25828         }
25829         
25830         if(this.submenu){
25831             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25832             
25833             if(this.pos == 'left'){
25834                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25835             }
25836         }
25837         
25838         return cfg;
25839     },
25840     
25841     initEvents : function() 
25842     {
25843         this.el.on('mouseover', this.onMouseOver, this);
25844         this.el.on('mouseout', this.onMouseOut, this);
25845         
25846         this.el.select('a', true).first().on('click', this.onClick, this);
25847         
25848     },
25849     
25850     onClick : function(e)
25851     {
25852         if(this.preventDefault){
25853             e.preventDefault();
25854         }
25855         
25856         this.fireEvent("click", this, e);
25857     },
25858     
25859     onMouseOver : function(e)
25860     {
25861         if(this.submenu && this.pos == 'left'){
25862             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25863         }
25864         
25865         this.fireEvent("mouseover", this, e);
25866     },
25867     
25868     onMouseOut : function(e)
25869     {
25870         this.fireEvent("mouseout", this, e);
25871     }
25872 });
25873
25874  
25875
25876  /*
25877  * - LGPL
25878  *
25879  * menu separator
25880  * 
25881  */
25882 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25883
25884 /**
25885  * @class Roo.bootstrap.menu.Separator
25886  * @extends Roo.bootstrap.Component
25887  * Bootstrap Separator class
25888  * 
25889  * @constructor
25890  * Create a new Separator
25891  * @param {Object} config The config object
25892  */
25893
25894
25895 Roo.bootstrap.menu.Separator = function(config){
25896     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25897 };
25898
25899 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25900     
25901     getAutoCreate : function(){
25902         var cfg = {
25903             tag : 'li',
25904             cls: 'divider'
25905         };
25906         
25907         return cfg;
25908     }
25909    
25910 });
25911
25912  
25913
25914  /*
25915  * - LGPL
25916  *
25917  * Tooltip
25918  * 
25919  */
25920
25921 /**
25922  * @class Roo.bootstrap.Tooltip
25923  * Bootstrap Tooltip class
25924  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25925  * to determine which dom element triggers the tooltip.
25926  * 
25927  * It needs to add support for additional attributes like tooltip-position
25928  * 
25929  * @constructor
25930  * Create a new Toolti
25931  * @param {Object} config The config object
25932  */
25933
25934 Roo.bootstrap.Tooltip = function(config){
25935     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25936     
25937     this.alignment = Roo.bootstrap.Tooltip.alignment;
25938     
25939     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25940         this.alignment = config.alignment;
25941     }
25942     
25943 };
25944
25945 Roo.apply(Roo.bootstrap.Tooltip, {
25946     /**
25947      * @function init initialize tooltip monitoring.
25948      * @static
25949      */
25950     currentEl : false,
25951     currentTip : false,
25952     currentRegion : false,
25953     
25954     //  init : delay?
25955     
25956     init : function()
25957     {
25958         Roo.get(document).on('mouseover', this.enter ,this);
25959         Roo.get(document).on('mouseout', this.leave, this);
25960          
25961         
25962         this.currentTip = new Roo.bootstrap.Tooltip();
25963     },
25964     
25965     enter : function(ev)
25966     {
25967         var dom = ev.getTarget();
25968         
25969         //Roo.log(['enter',dom]);
25970         var el = Roo.fly(dom);
25971         if (this.currentEl) {
25972             //Roo.log(dom);
25973             //Roo.log(this.currentEl);
25974             //Roo.log(this.currentEl.contains(dom));
25975             if (this.currentEl == el) {
25976                 return;
25977             }
25978             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25979                 return;
25980             }
25981
25982         }
25983         
25984         if (this.currentTip.el) {
25985             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25986         }    
25987         //Roo.log(ev);
25988         
25989         if(!el || el.dom == document){
25990             return;
25991         }
25992         
25993         var bindEl = el;
25994         
25995         // you can not look for children, as if el is the body.. then everythign is the child..
25996         if (!el.attr('tooltip')) { //
25997             if (!el.select("[tooltip]").elements.length) {
25998                 return;
25999             }
26000             // is the mouse over this child...?
26001             bindEl = el.select("[tooltip]").first();
26002             var xy = ev.getXY();
26003             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26004                 //Roo.log("not in region.");
26005                 return;
26006             }
26007             //Roo.log("child element over..");
26008             
26009         }
26010         this.currentEl = bindEl;
26011         this.currentTip.bind(bindEl);
26012         this.currentRegion = Roo.lib.Region.getRegion(dom);
26013         this.currentTip.enter();
26014         
26015     },
26016     leave : function(ev)
26017     {
26018         var dom = ev.getTarget();
26019         //Roo.log(['leave',dom]);
26020         if (!this.currentEl) {
26021             return;
26022         }
26023         
26024         
26025         if (dom != this.currentEl.dom) {
26026             return;
26027         }
26028         var xy = ev.getXY();
26029         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26030             return;
26031         }
26032         // only activate leave if mouse cursor is outside... bounding box..
26033         
26034         
26035         
26036         
26037         if (this.currentTip) {
26038             this.currentTip.leave();
26039         }
26040         //Roo.log('clear currentEl');
26041         this.currentEl = false;
26042         
26043         
26044     },
26045     alignment : {
26046         'left' : ['r-l', [-2,0], 'right'],
26047         'right' : ['l-r', [2,0], 'left'],
26048         'bottom' : ['t-b', [0,2], 'top'],
26049         'top' : [ 'b-t', [0,-2], 'bottom']
26050     }
26051     
26052 });
26053
26054
26055 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26056     
26057     
26058     bindEl : false,
26059     
26060     delay : null, // can be { show : 300 , hide: 500}
26061     
26062     timeout : null,
26063     
26064     hoverState : null, //???
26065     
26066     placement : 'bottom', 
26067     
26068     alignment : false,
26069     
26070     getAutoCreate : function(){
26071     
26072         var cfg = {
26073            cls : 'tooltip',
26074            role : 'tooltip',
26075            cn : [
26076                 {
26077                     cls : 'tooltip-arrow'
26078                 },
26079                 {
26080                     cls : 'tooltip-inner'
26081                 }
26082            ]
26083         };
26084         
26085         return cfg;
26086     },
26087     bind : function(el)
26088     {
26089         this.bindEl = el;
26090     },
26091       
26092     
26093     enter : function () {
26094        
26095         if (this.timeout != null) {
26096             clearTimeout(this.timeout);
26097         }
26098         
26099         this.hoverState = 'in';
26100          //Roo.log("enter - show");
26101         if (!this.delay || !this.delay.show) {
26102             this.show();
26103             return;
26104         }
26105         var _t = this;
26106         this.timeout = setTimeout(function () {
26107             if (_t.hoverState == 'in') {
26108                 _t.show();
26109             }
26110         }, this.delay.show);
26111     },
26112     leave : function()
26113     {
26114         clearTimeout(this.timeout);
26115     
26116         this.hoverState = 'out';
26117          if (!this.delay || !this.delay.hide) {
26118             this.hide();
26119             return;
26120         }
26121        
26122         var _t = this;
26123         this.timeout = setTimeout(function () {
26124             //Roo.log("leave - timeout");
26125             
26126             if (_t.hoverState == 'out') {
26127                 _t.hide();
26128                 Roo.bootstrap.Tooltip.currentEl = false;
26129             }
26130         }, delay);
26131     },
26132     
26133     show : function (msg)
26134     {
26135         if (!this.el) {
26136             this.render(document.body);
26137         }
26138         // set content.
26139         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26140         
26141         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26142         
26143         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26144         
26145         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26146         
26147         var placement = typeof this.placement == 'function' ?
26148             this.placement.call(this, this.el, on_el) :
26149             this.placement;
26150             
26151         var autoToken = /\s?auto?\s?/i;
26152         var autoPlace = autoToken.test(placement);
26153         if (autoPlace) {
26154             placement = placement.replace(autoToken, '') || 'top';
26155         }
26156         
26157         //this.el.detach()
26158         //this.el.setXY([0,0]);
26159         this.el.show();
26160         //this.el.dom.style.display='block';
26161         
26162         //this.el.appendTo(on_el);
26163         
26164         var p = this.getPosition();
26165         var box = this.el.getBox();
26166         
26167         if (autoPlace) {
26168             // fixme..
26169         }
26170         
26171         var align = this.alignment[placement];
26172         
26173         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26174         
26175         if(placement == 'top' || placement == 'bottom'){
26176             if(xy[0] < 0){
26177                 placement = 'right';
26178             }
26179             
26180             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26181                 placement = 'left';
26182             }
26183             
26184             var scroll = Roo.select('body', true).first().getScroll();
26185             
26186             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26187                 placement = 'top';
26188             }
26189             
26190         }
26191         
26192         this.el.alignTo(this.bindEl, align[0],align[1]);
26193         //var arrow = this.el.select('.arrow',true).first();
26194         //arrow.set(align[2], 
26195         
26196         this.el.addClass(placement);
26197         
26198         this.el.addClass('in fade');
26199         
26200         this.hoverState = null;
26201         
26202         if (this.el.hasClass('fade')) {
26203             // fade it?
26204         }
26205         
26206     },
26207     hide : function()
26208     {
26209          
26210         if (!this.el) {
26211             return;
26212         }
26213         //this.el.setXY([0,0]);
26214         this.el.removeClass('in');
26215         //this.el.hide();
26216         
26217     }
26218     
26219 });
26220  
26221
26222  /*
26223  * - LGPL
26224  *
26225  * Location Picker
26226  * 
26227  */
26228
26229 /**
26230  * @class Roo.bootstrap.LocationPicker
26231  * @extends Roo.bootstrap.Component
26232  * Bootstrap LocationPicker class
26233  * @cfg {Number} latitude Position when init default 0
26234  * @cfg {Number} longitude Position when init default 0
26235  * @cfg {Number} zoom default 15
26236  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26237  * @cfg {Boolean} mapTypeControl default false
26238  * @cfg {Boolean} disableDoubleClickZoom default false
26239  * @cfg {Boolean} scrollwheel default true
26240  * @cfg {Boolean} streetViewControl default false
26241  * @cfg {Number} radius default 0
26242  * @cfg {String} locationName
26243  * @cfg {Boolean} draggable default true
26244  * @cfg {Boolean} enableAutocomplete default false
26245  * @cfg {Boolean} enableReverseGeocode default true
26246  * @cfg {String} markerTitle
26247  * 
26248  * @constructor
26249  * Create a new LocationPicker
26250  * @param {Object} config The config object
26251  */
26252
26253
26254 Roo.bootstrap.LocationPicker = function(config){
26255     
26256     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26257     
26258     this.addEvents({
26259         /**
26260          * @event initial
26261          * Fires when the picker initialized.
26262          * @param {Roo.bootstrap.LocationPicker} this
26263          * @param {Google Location} location
26264          */
26265         initial : true,
26266         /**
26267          * @event positionchanged
26268          * Fires when the picker position changed.
26269          * @param {Roo.bootstrap.LocationPicker} this
26270          * @param {Google Location} location
26271          */
26272         positionchanged : true,
26273         /**
26274          * @event resize
26275          * Fires when the map resize.
26276          * @param {Roo.bootstrap.LocationPicker} this
26277          */
26278         resize : true,
26279         /**
26280          * @event show
26281          * Fires when the map show.
26282          * @param {Roo.bootstrap.LocationPicker} this
26283          */
26284         show : true,
26285         /**
26286          * @event hide
26287          * Fires when the map hide.
26288          * @param {Roo.bootstrap.LocationPicker} this
26289          */
26290         hide : true,
26291         /**
26292          * @event mapClick
26293          * Fires when click the map.
26294          * @param {Roo.bootstrap.LocationPicker} this
26295          * @param {Map event} e
26296          */
26297         mapClick : true,
26298         /**
26299          * @event mapRightClick
26300          * Fires when right click the map.
26301          * @param {Roo.bootstrap.LocationPicker} this
26302          * @param {Map event} e
26303          */
26304         mapRightClick : true,
26305         /**
26306          * @event markerClick
26307          * Fires when click the marker.
26308          * @param {Roo.bootstrap.LocationPicker} this
26309          * @param {Map event} e
26310          */
26311         markerClick : true,
26312         /**
26313          * @event markerRightClick
26314          * Fires when right click the marker.
26315          * @param {Roo.bootstrap.LocationPicker} this
26316          * @param {Map event} e
26317          */
26318         markerRightClick : true,
26319         /**
26320          * @event OverlayViewDraw
26321          * Fires when OverlayView Draw
26322          * @param {Roo.bootstrap.LocationPicker} this
26323          */
26324         OverlayViewDraw : true,
26325         /**
26326          * @event OverlayViewOnAdd
26327          * Fires when OverlayView Draw
26328          * @param {Roo.bootstrap.LocationPicker} this
26329          */
26330         OverlayViewOnAdd : true,
26331         /**
26332          * @event OverlayViewOnRemove
26333          * Fires when OverlayView Draw
26334          * @param {Roo.bootstrap.LocationPicker} this
26335          */
26336         OverlayViewOnRemove : true,
26337         /**
26338          * @event OverlayViewShow
26339          * Fires when OverlayView Draw
26340          * @param {Roo.bootstrap.LocationPicker} this
26341          * @param {Pixel} cpx
26342          */
26343         OverlayViewShow : true,
26344         /**
26345          * @event OverlayViewHide
26346          * Fires when OverlayView Draw
26347          * @param {Roo.bootstrap.LocationPicker} this
26348          */
26349         OverlayViewHide : true,
26350         /**
26351          * @event loadexception
26352          * Fires when load google lib failed.
26353          * @param {Roo.bootstrap.LocationPicker} this
26354          */
26355         loadexception : true
26356     });
26357         
26358 };
26359
26360 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26361     
26362     gMapContext: false,
26363     
26364     latitude: 0,
26365     longitude: 0,
26366     zoom: 15,
26367     mapTypeId: false,
26368     mapTypeControl: false,
26369     disableDoubleClickZoom: false,
26370     scrollwheel: true,
26371     streetViewControl: false,
26372     radius: 0,
26373     locationName: '',
26374     draggable: true,
26375     enableAutocomplete: false,
26376     enableReverseGeocode: true,
26377     markerTitle: '',
26378     
26379     getAutoCreate: function()
26380     {
26381
26382         var cfg = {
26383             tag: 'div',
26384             cls: 'roo-location-picker'
26385         };
26386         
26387         return cfg
26388     },
26389     
26390     initEvents: function(ct, position)
26391     {       
26392         if(!this.el.getWidth() || this.isApplied()){
26393             return;
26394         }
26395         
26396         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26397         
26398         this.initial();
26399     },
26400     
26401     initial: function()
26402     {
26403         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26404             this.fireEvent('loadexception', this);
26405             return;
26406         }
26407         
26408         if(!this.mapTypeId){
26409             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26410         }
26411         
26412         this.gMapContext = this.GMapContext();
26413         
26414         this.initOverlayView();
26415         
26416         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26417         
26418         var _this = this;
26419                 
26420         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26421             _this.setPosition(_this.gMapContext.marker.position);
26422         });
26423         
26424         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26425             _this.fireEvent('mapClick', this, event);
26426             
26427         });
26428
26429         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26430             _this.fireEvent('mapRightClick', this, event);
26431             
26432         });
26433         
26434         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26435             _this.fireEvent('markerClick', this, event);
26436             
26437         });
26438
26439         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26440             _this.fireEvent('markerRightClick', this, event);
26441             
26442         });
26443         
26444         this.setPosition(this.gMapContext.location);
26445         
26446         this.fireEvent('initial', this, this.gMapContext.location);
26447     },
26448     
26449     initOverlayView: function()
26450     {
26451         var _this = this;
26452         
26453         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26454             
26455             draw: function()
26456             {
26457                 _this.fireEvent('OverlayViewDraw', _this);
26458             },
26459             
26460             onAdd: function()
26461             {
26462                 _this.fireEvent('OverlayViewOnAdd', _this);
26463             },
26464             
26465             onRemove: function()
26466             {
26467                 _this.fireEvent('OverlayViewOnRemove', _this);
26468             },
26469             
26470             show: function(cpx)
26471             {
26472                 _this.fireEvent('OverlayViewShow', _this, cpx);
26473             },
26474             
26475             hide: function()
26476             {
26477                 _this.fireEvent('OverlayViewHide', _this);
26478             }
26479             
26480         });
26481     },
26482     
26483     fromLatLngToContainerPixel: function(event)
26484     {
26485         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26486     },
26487     
26488     isApplied: function() 
26489     {
26490         return this.getGmapContext() == false ? false : true;
26491     },
26492     
26493     getGmapContext: function() 
26494     {
26495         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26496     },
26497     
26498     GMapContext: function() 
26499     {
26500         var position = new google.maps.LatLng(this.latitude, this.longitude);
26501         
26502         var _map = new google.maps.Map(this.el.dom, {
26503             center: position,
26504             zoom: this.zoom,
26505             mapTypeId: this.mapTypeId,
26506             mapTypeControl: this.mapTypeControl,
26507             disableDoubleClickZoom: this.disableDoubleClickZoom,
26508             scrollwheel: this.scrollwheel,
26509             streetViewControl: this.streetViewControl,
26510             locationName: this.locationName,
26511             draggable: this.draggable,
26512             enableAutocomplete: this.enableAutocomplete,
26513             enableReverseGeocode: this.enableReverseGeocode
26514         });
26515         
26516         var _marker = new google.maps.Marker({
26517             position: position,
26518             map: _map,
26519             title: this.markerTitle,
26520             draggable: this.draggable
26521         });
26522         
26523         return {
26524             map: _map,
26525             marker: _marker,
26526             circle: null,
26527             location: position,
26528             radius: this.radius,
26529             locationName: this.locationName,
26530             addressComponents: {
26531                 formatted_address: null,
26532                 addressLine1: null,
26533                 addressLine2: null,
26534                 streetName: null,
26535                 streetNumber: null,
26536                 city: null,
26537                 district: null,
26538                 state: null,
26539                 stateOrProvince: null
26540             },
26541             settings: this,
26542             domContainer: this.el.dom,
26543             geodecoder: new google.maps.Geocoder()
26544         };
26545     },
26546     
26547     drawCircle: function(center, radius, options) 
26548     {
26549         if (this.gMapContext.circle != null) {
26550             this.gMapContext.circle.setMap(null);
26551         }
26552         if (radius > 0) {
26553             radius *= 1;
26554             options = Roo.apply({}, options, {
26555                 strokeColor: "#0000FF",
26556                 strokeOpacity: .35,
26557                 strokeWeight: 2,
26558                 fillColor: "#0000FF",
26559                 fillOpacity: .2
26560             });
26561             
26562             options.map = this.gMapContext.map;
26563             options.radius = radius;
26564             options.center = center;
26565             this.gMapContext.circle = new google.maps.Circle(options);
26566             return this.gMapContext.circle;
26567         }
26568         
26569         return null;
26570     },
26571     
26572     setPosition: function(location) 
26573     {
26574         this.gMapContext.location = location;
26575         this.gMapContext.marker.setPosition(location);
26576         this.gMapContext.map.panTo(location);
26577         this.drawCircle(location, this.gMapContext.radius, {});
26578         
26579         var _this = this;
26580         
26581         if (this.gMapContext.settings.enableReverseGeocode) {
26582             this.gMapContext.geodecoder.geocode({
26583                 latLng: this.gMapContext.location
26584             }, function(results, status) {
26585                 
26586                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26587                     _this.gMapContext.locationName = results[0].formatted_address;
26588                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26589                     
26590                     _this.fireEvent('positionchanged', this, location);
26591                 }
26592             });
26593             
26594             return;
26595         }
26596         
26597         this.fireEvent('positionchanged', this, location);
26598     },
26599     
26600     resize: function()
26601     {
26602         google.maps.event.trigger(this.gMapContext.map, "resize");
26603         
26604         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26605         
26606         this.fireEvent('resize', this);
26607     },
26608     
26609     setPositionByLatLng: function(latitude, longitude)
26610     {
26611         this.setPosition(new google.maps.LatLng(latitude, longitude));
26612     },
26613     
26614     getCurrentPosition: function() 
26615     {
26616         return {
26617             latitude: this.gMapContext.location.lat(),
26618             longitude: this.gMapContext.location.lng()
26619         };
26620     },
26621     
26622     getAddressName: function() 
26623     {
26624         return this.gMapContext.locationName;
26625     },
26626     
26627     getAddressComponents: function() 
26628     {
26629         return this.gMapContext.addressComponents;
26630     },
26631     
26632     address_component_from_google_geocode: function(address_components) 
26633     {
26634         var result = {};
26635         
26636         for (var i = 0; i < address_components.length; i++) {
26637             var component = address_components[i];
26638             if (component.types.indexOf("postal_code") >= 0) {
26639                 result.postalCode = component.short_name;
26640             } else if (component.types.indexOf("street_number") >= 0) {
26641                 result.streetNumber = component.short_name;
26642             } else if (component.types.indexOf("route") >= 0) {
26643                 result.streetName = component.short_name;
26644             } else if (component.types.indexOf("neighborhood") >= 0) {
26645                 result.city = component.short_name;
26646             } else if (component.types.indexOf("locality") >= 0) {
26647                 result.city = component.short_name;
26648             } else if (component.types.indexOf("sublocality") >= 0) {
26649                 result.district = component.short_name;
26650             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26651                 result.stateOrProvince = component.short_name;
26652             } else if (component.types.indexOf("country") >= 0) {
26653                 result.country = component.short_name;
26654             }
26655         }
26656         
26657         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26658         result.addressLine2 = "";
26659         return result;
26660     },
26661     
26662     setZoomLevel: function(zoom)
26663     {
26664         this.gMapContext.map.setZoom(zoom);
26665     },
26666     
26667     show: function()
26668     {
26669         if(!this.el){
26670             return;
26671         }
26672         
26673         this.el.show();
26674         
26675         this.resize();
26676         
26677         this.fireEvent('show', this);
26678     },
26679     
26680     hide: function()
26681     {
26682         if(!this.el){
26683             return;
26684         }
26685         
26686         this.el.hide();
26687         
26688         this.fireEvent('hide', this);
26689     }
26690     
26691 });
26692
26693 Roo.apply(Roo.bootstrap.LocationPicker, {
26694     
26695     OverlayView : function(map, options)
26696     {
26697         options = options || {};
26698         
26699         this.setMap(map);
26700     }
26701     
26702     
26703 });/*
26704  * - LGPL
26705  *
26706  * Alert
26707  * 
26708  */
26709
26710 /**
26711  * @class Roo.bootstrap.Alert
26712  * @extends Roo.bootstrap.Component
26713  * Bootstrap Alert class
26714  * @cfg {String} title The title of alert
26715  * @cfg {String} html The content of alert
26716  * @cfg {String} weight (  success | info | warning | danger )
26717  * @cfg {String} faicon font-awesomeicon
26718  * 
26719  * @constructor
26720  * Create a new alert
26721  * @param {Object} config The config object
26722  */
26723
26724
26725 Roo.bootstrap.Alert = function(config){
26726     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26727     
26728 };
26729
26730 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26731     
26732     title: '',
26733     html: '',
26734     weight: false,
26735     faicon: false,
26736     
26737     getAutoCreate : function()
26738     {
26739         
26740         var cfg = {
26741             tag : 'div',
26742             cls : 'alert',
26743             cn : [
26744                 {
26745                     tag : 'i',
26746                     cls : 'roo-alert-icon'
26747                     
26748                 },
26749                 {
26750                     tag : 'b',
26751                     cls : 'roo-alert-title',
26752                     html : this.title
26753                 },
26754                 {
26755                     tag : 'span',
26756                     cls : 'roo-alert-text',
26757                     html : this.html
26758                 }
26759             ]
26760         };
26761         
26762         if(this.faicon){
26763             cfg.cn[0].cls += ' fa ' + this.faicon;
26764         }
26765         
26766         if(this.weight){
26767             cfg.cls += ' alert-' + this.weight;
26768         }
26769         
26770         return cfg;
26771     },
26772     
26773     initEvents: function() 
26774     {
26775         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26776     },
26777     
26778     setTitle : function(str)
26779     {
26780         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26781     },
26782     
26783     setText : function(str)
26784     {
26785         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26786     },
26787     
26788     setWeight : function(weight)
26789     {
26790         if(this.weight){
26791             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26792         }
26793         
26794         this.weight = weight;
26795         
26796         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26797     },
26798     
26799     setIcon : function(icon)
26800     {
26801         if(this.faicon){
26802             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26803         }
26804         
26805         this.faicon = icon;
26806         
26807         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26808     },
26809     
26810     hide: function() 
26811     {
26812         this.el.hide();   
26813     },
26814     
26815     show: function() 
26816     {  
26817         this.el.show();   
26818     }
26819     
26820 });
26821
26822  
26823 /*
26824 * Licence: LGPL
26825 */
26826
26827 /**
26828  * @class Roo.bootstrap.UploadCropbox
26829  * @extends Roo.bootstrap.Component
26830  * Bootstrap UploadCropbox class
26831  * @cfg {String} emptyText show when image has been loaded
26832  * @cfg {String} rotateNotify show when image too small to rotate
26833  * @cfg {Number} errorTimeout default 3000
26834  * @cfg {Number} minWidth default 300
26835  * @cfg {Number} minHeight default 300
26836  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26837  * @cfg {Boolean} isDocument (true|false) default false
26838  * @cfg {String} url action url
26839  * @cfg {String} paramName default 'imageUpload'
26840  * @cfg {String} method default POST
26841  * @cfg {Boolean} loadMask (true|false) default true
26842  * @cfg {Boolean} loadingText default 'Loading...'
26843  * 
26844  * @constructor
26845  * Create a new UploadCropbox
26846  * @param {Object} config The config object
26847  */
26848
26849 Roo.bootstrap.UploadCropbox = function(config){
26850     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26851     
26852     this.addEvents({
26853         /**
26854          * @event beforeselectfile
26855          * Fire before select file
26856          * @param {Roo.bootstrap.UploadCropbox} this
26857          */
26858         "beforeselectfile" : true,
26859         /**
26860          * @event initial
26861          * Fire after initEvent
26862          * @param {Roo.bootstrap.UploadCropbox} this
26863          */
26864         "initial" : true,
26865         /**
26866          * @event crop
26867          * Fire after initEvent
26868          * @param {Roo.bootstrap.UploadCropbox} this
26869          * @param {String} data
26870          */
26871         "crop" : true,
26872         /**
26873          * @event prepare
26874          * Fire when preparing the file data
26875          * @param {Roo.bootstrap.UploadCropbox} this
26876          * @param {Object} file
26877          */
26878         "prepare" : true,
26879         /**
26880          * @event exception
26881          * Fire when get exception
26882          * @param {Roo.bootstrap.UploadCropbox} this
26883          * @param {XMLHttpRequest} xhr
26884          */
26885         "exception" : true,
26886         /**
26887          * @event beforeloadcanvas
26888          * Fire before load the canvas
26889          * @param {Roo.bootstrap.UploadCropbox} this
26890          * @param {String} src
26891          */
26892         "beforeloadcanvas" : true,
26893         /**
26894          * @event trash
26895          * Fire when trash image
26896          * @param {Roo.bootstrap.UploadCropbox} this
26897          */
26898         "trash" : true,
26899         /**
26900          * @event download
26901          * Fire when download the image
26902          * @param {Roo.bootstrap.UploadCropbox} this
26903          */
26904         "download" : true,
26905         /**
26906          * @event footerbuttonclick
26907          * Fire when footerbuttonclick
26908          * @param {Roo.bootstrap.UploadCropbox} this
26909          * @param {String} type
26910          */
26911         "footerbuttonclick" : true,
26912         /**
26913          * @event resize
26914          * Fire when resize
26915          * @param {Roo.bootstrap.UploadCropbox} this
26916          */
26917         "resize" : true,
26918         /**
26919          * @event rotate
26920          * Fire when rotate the image
26921          * @param {Roo.bootstrap.UploadCropbox} this
26922          * @param {String} pos
26923          */
26924         "rotate" : true,
26925         /**
26926          * @event inspect
26927          * Fire when inspect the file
26928          * @param {Roo.bootstrap.UploadCropbox} this
26929          * @param {Object} file
26930          */
26931         "inspect" : true,
26932         /**
26933          * @event upload
26934          * Fire when xhr upload the file
26935          * @param {Roo.bootstrap.UploadCropbox} this
26936          * @param {Object} data
26937          */
26938         "upload" : true,
26939         /**
26940          * @event arrange
26941          * Fire when arrange the file data
26942          * @param {Roo.bootstrap.UploadCropbox} this
26943          * @param {Object} formData
26944          */
26945         "arrange" : true
26946     });
26947     
26948     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26949 };
26950
26951 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26952     
26953     emptyText : 'Click to upload image',
26954     rotateNotify : 'Image is too small to rotate',
26955     errorTimeout : 3000,
26956     scale : 0,
26957     baseScale : 1,
26958     rotate : 0,
26959     dragable : false,
26960     pinching : false,
26961     mouseX : 0,
26962     mouseY : 0,
26963     cropData : false,
26964     minWidth : 300,
26965     minHeight : 300,
26966     file : false,
26967     exif : {},
26968     baseRotate : 1,
26969     cropType : 'image/jpeg',
26970     buttons : false,
26971     canvasLoaded : false,
26972     isDocument : false,
26973     method : 'POST',
26974     paramName : 'imageUpload',
26975     loadMask : true,
26976     loadingText : 'Loading...',
26977     maskEl : false,
26978     
26979     getAutoCreate : function()
26980     {
26981         var cfg = {
26982             tag : 'div',
26983             cls : 'roo-upload-cropbox',
26984             cn : [
26985                 {
26986                     tag : 'input',
26987                     cls : 'roo-upload-cropbox-selector',
26988                     type : 'file'
26989                 },
26990                 {
26991                     tag : 'div',
26992                     cls : 'roo-upload-cropbox-body',
26993                     style : 'cursor:pointer',
26994                     cn : [
26995                         {
26996                             tag : 'div',
26997                             cls : 'roo-upload-cropbox-preview'
26998                         },
26999                         {
27000                             tag : 'div',
27001                             cls : 'roo-upload-cropbox-thumb'
27002                         },
27003                         {
27004                             tag : 'div',
27005                             cls : 'roo-upload-cropbox-empty-notify',
27006                             html : this.emptyText
27007                         },
27008                         {
27009                             tag : 'div',
27010                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27011                             html : this.rotateNotify
27012                         }
27013                     ]
27014                 },
27015                 {
27016                     tag : 'div',
27017                     cls : 'roo-upload-cropbox-footer',
27018                     cn : {
27019                         tag : 'div',
27020                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27021                         cn : []
27022                     }
27023                 }
27024             ]
27025         };
27026         
27027         return cfg;
27028     },
27029     
27030     onRender : function(ct, position)
27031     {
27032         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27033         
27034         if (this.buttons.length) {
27035             
27036             Roo.each(this.buttons, function(bb) {
27037                 
27038                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27039                 
27040                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27041                 
27042             }, this);
27043         }
27044         
27045         if(this.loadMask){
27046             this.maskEl = this.el;
27047         }
27048     },
27049     
27050     initEvents : function()
27051     {
27052         this.urlAPI = (window.createObjectURL && window) || 
27053                                 (window.URL && URL.revokeObjectURL && URL) || 
27054                                 (window.webkitURL && webkitURL);
27055                         
27056         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27057         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27058         
27059         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27060         this.selectorEl.hide();
27061         
27062         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27063         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27064         
27065         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27066         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27067         this.thumbEl.hide();
27068         
27069         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27070         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27071         
27072         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27073         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27074         this.errorEl.hide();
27075         
27076         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27077         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27078         this.footerEl.hide();
27079         
27080         this.setThumbBoxSize();
27081         
27082         this.bind();
27083         
27084         this.resize();
27085         
27086         this.fireEvent('initial', this);
27087     },
27088
27089     bind : function()
27090     {
27091         var _this = this;
27092         
27093         window.addEventListener("resize", function() { _this.resize(); } );
27094         
27095         this.bodyEl.on('click', this.beforeSelectFile, this);
27096         
27097         if(Roo.isTouch){
27098             this.bodyEl.on('touchstart', this.onTouchStart, this);
27099             this.bodyEl.on('touchmove', this.onTouchMove, this);
27100             this.bodyEl.on('touchend', this.onTouchEnd, this);
27101         }
27102         
27103         if(!Roo.isTouch){
27104             this.bodyEl.on('mousedown', this.onMouseDown, this);
27105             this.bodyEl.on('mousemove', this.onMouseMove, this);
27106             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27107             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27108             Roo.get(document).on('mouseup', this.onMouseUp, this);
27109         }
27110         
27111         this.selectorEl.on('change', this.onFileSelected, this);
27112     },
27113     
27114     reset : function()
27115     {    
27116         this.scale = 0;
27117         this.baseScale = 1;
27118         this.rotate = 0;
27119         this.baseRotate = 1;
27120         this.dragable = false;
27121         this.pinching = false;
27122         this.mouseX = 0;
27123         this.mouseY = 0;
27124         this.cropData = false;
27125         this.notifyEl.dom.innerHTML = this.emptyText;
27126         
27127         this.selectorEl.dom.value = '';
27128         
27129     },
27130     
27131     resize : function()
27132     {
27133         if(this.fireEvent('resize', this) != false){
27134             this.setThumbBoxPosition();
27135             this.setCanvasPosition();
27136         }
27137     },
27138     
27139     onFooterButtonClick : function(e, el, o, type)
27140     {
27141         switch (type) {
27142             case 'rotate-left' :
27143                 this.onRotateLeft(e);
27144                 break;
27145             case 'rotate-right' :
27146                 this.onRotateRight(e);
27147                 break;
27148             case 'picture' :
27149                 this.beforeSelectFile(e);
27150                 break;
27151             case 'trash' :
27152                 this.trash(e);
27153                 break;
27154             case 'crop' :
27155                 this.crop(e);
27156                 break;
27157             case 'download' :
27158                 this.download(e);
27159                 break;
27160             default :
27161                 break;
27162         }
27163         
27164         this.fireEvent('footerbuttonclick', this, type);
27165     },
27166     
27167     beforeSelectFile : function(e)
27168     {
27169         e.preventDefault();
27170         
27171         if(this.fireEvent('beforeselectfile', this) != false){
27172             this.selectorEl.dom.click();
27173         }
27174     },
27175     
27176     onFileSelected : function(e)
27177     {
27178         e.preventDefault();
27179         
27180         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27181             return;
27182         }
27183         
27184         var file = this.selectorEl.dom.files[0];
27185         
27186         if(this.fireEvent('inspect', this, file) != false){
27187             this.prepare(file);
27188         }
27189         
27190     },
27191     
27192     trash : function(e)
27193     {
27194         this.fireEvent('trash', this);
27195     },
27196     
27197     download : function(e)
27198     {
27199         this.fireEvent('download', this);
27200     },
27201     
27202     loadCanvas : function(src)
27203     {   
27204         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27205             
27206             this.reset();
27207             
27208             this.imageEl = document.createElement('img');
27209             
27210             var _this = this;
27211             
27212             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27213             
27214             this.imageEl.src = src;
27215         }
27216     },
27217     
27218     onLoadCanvas : function()
27219     {   
27220         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27221         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27222         
27223         this.bodyEl.un('click', this.beforeSelectFile, this);
27224         
27225         this.notifyEl.hide();
27226         this.thumbEl.show();
27227         this.footerEl.show();
27228         
27229         this.baseRotateLevel();
27230         
27231         if(this.isDocument){
27232             this.setThumbBoxSize();
27233         }
27234         
27235         this.setThumbBoxPosition();
27236         
27237         this.baseScaleLevel();
27238         
27239         this.draw();
27240         
27241         this.resize();
27242         
27243         this.canvasLoaded = true;
27244         
27245         if(this.loadMask){
27246             this.maskEl.unmask();
27247         }
27248         
27249     },
27250     
27251     setCanvasPosition : function()
27252     {   
27253         if(!this.canvasEl){
27254             return;
27255         }
27256         
27257         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27258         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27259         
27260         this.previewEl.setLeft(pw);
27261         this.previewEl.setTop(ph);
27262         
27263     },
27264     
27265     onMouseDown : function(e)
27266     {   
27267         e.stopEvent();
27268         
27269         this.dragable = true;
27270         this.pinching = false;
27271         
27272         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27273             this.dragable = false;
27274             return;
27275         }
27276         
27277         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27278         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27279         
27280     },
27281     
27282     onMouseMove : function(e)
27283     {   
27284         e.stopEvent();
27285         
27286         if(!this.canvasLoaded){
27287             return;
27288         }
27289         
27290         if (!this.dragable){
27291             return;
27292         }
27293         
27294         var minX = Math.ceil(this.thumbEl.getLeft(true));
27295         var minY = Math.ceil(this.thumbEl.getTop(true));
27296         
27297         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27298         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27299         
27300         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27301         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27302         
27303         x = x - this.mouseX;
27304         y = y - this.mouseY;
27305         
27306         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27307         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27308         
27309         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27310         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27311         
27312         this.previewEl.setLeft(bgX);
27313         this.previewEl.setTop(bgY);
27314         
27315         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27316         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27317     },
27318     
27319     onMouseUp : function(e)
27320     {   
27321         e.stopEvent();
27322         
27323         this.dragable = false;
27324     },
27325     
27326     onMouseWheel : function(e)
27327     {   
27328         e.stopEvent();
27329         
27330         this.startScale = this.scale;
27331         
27332         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27333         
27334         if(!this.zoomable()){
27335             this.scale = this.startScale;
27336             return;
27337         }
27338         
27339         this.draw();
27340         
27341         return;
27342     },
27343     
27344     zoomable : function()
27345     {
27346         var minScale = this.thumbEl.getWidth() / this.minWidth;
27347         
27348         if(this.minWidth < this.minHeight){
27349             minScale = this.thumbEl.getHeight() / this.minHeight;
27350         }
27351         
27352         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27353         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27354         
27355         if(
27356                 this.isDocument &&
27357                 (this.rotate == 0 || this.rotate == 180) && 
27358                 (
27359                     width > this.imageEl.OriginWidth || 
27360                     height > this.imageEl.OriginHeight ||
27361                     (width < this.minWidth && height < this.minHeight)
27362                 )
27363         ){
27364             return false;
27365         }
27366         
27367         if(
27368                 this.isDocument &&
27369                 (this.rotate == 90 || this.rotate == 270) && 
27370                 (
27371                     width > this.imageEl.OriginWidth || 
27372                     height > this.imageEl.OriginHeight ||
27373                     (width < this.minHeight && height < this.minWidth)
27374                 )
27375         ){
27376             return false;
27377         }
27378         
27379         if(
27380                 !this.isDocument &&
27381                 (this.rotate == 0 || this.rotate == 180) && 
27382                 (
27383                     width < this.minWidth || 
27384                     width > this.imageEl.OriginWidth || 
27385                     height < this.minHeight || 
27386                     height > this.imageEl.OriginHeight
27387                 )
27388         ){
27389             return false;
27390         }
27391         
27392         if(
27393                 !this.isDocument &&
27394                 (this.rotate == 90 || this.rotate == 270) && 
27395                 (
27396                     width < this.minHeight || 
27397                     width > this.imageEl.OriginWidth || 
27398                     height < this.minWidth || 
27399                     height > this.imageEl.OriginHeight
27400                 )
27401         ){
27402             return false;
27403         }
27404         
27405         return true;
27406         
27407     },
27408     
27409     onRotateLeft : function(e)
27410     {   
27411         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27412             
27413             var minScale = this.thumbEl.getWidth() / this.minWidth;
27414             
27415             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27416             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27417             
27418             this.startScale = this.scale;
27419             
27420             while (this.getScaleLevel() < minScale){
27421             
27422                 this.scale = this.scale + 1;
27423                 
27424                 if(!this.zoomable()){
27425                     break;
27426                 }
27427                 
27428                 if(
27429                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27430                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27431                 ){
27432                     continue;
27433                 }
27434                 
27435                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27436
27437                 this.draw();
27438                 
27439                 return;
27440             }
27441             
27442             this.scale = this.startScale;
27443             
27444             this.onRotateFail();
27445             
27446             return false;
27447         }
27448         
27449         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27450
27451         if(this.isDocument){
27452             this.setThumbBoxSize();
27453             this.setThumbBoxPosition();
27454             this.setCanvasPosition();
27455         }
27456         
27457         this.draw();
27458         
27459         this.fireEvent('rotate', this, 'left');
27460         
27461     },
27462     
27463     onRotateRight : function(e)
27464     {
27465         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27466             
27467             var minScale = this.thumbEl.getWidth() / this.minWidth;
27468         
27469             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27470             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27471             
27472             this.startScale = this.scale;
27473             
27474             while (this.getScaleLevel() < minScale){
27475             
27476                 this.scale = this.scale + 1;
27477                 
27478                 if(!this.zoomable()){
27479                     break;
27480                 }
27481                 
27482                 if(
27483                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27484                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27485                 ){
27486                     continue;
27487                 }
27488                 
27489                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27490
27491                 this.draw();
27492                 
27493                 return;
27494             }
27495             
27496             this.scale = this.startScale;
27497             
27498             this.onRotateFail();
27499             
27500             return false;
27501         }
27502         
27503         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27504
27505         if(this.isDocument){
27506             this.setThumbBoxSize();
27507             this.setThumbBoxPosition();
27508             this.setCanvasPosition();
27509         }
27510         
27511         this.draw();
27512         
27513         this.fireEvent('rotate', this, 'right');
27514     },
27515     
27516     onRotateFail : function()
27517     {
27518         this.errorEl.show(true);
27519         
27520         var _this = this;
27521         
27522         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27523     },
27524     
27525     draw : function()
27526     {
27527         this.previewEl.dom.innerHTML = '';
27528         
27529         var canvasEl = document.createElement("canvas");
27530         
27531         var contextEl = canvasEl.getContext("2d");
27532         
27533         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27534         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27535         var center = this.imageEl.OriginWidth / 2;
27536         
27537         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27538             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27539             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27540             center = this.imageEl.OriginHeight / 2;
27541         }
27542         
27543         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27544         
27545         contextEl.translate(center, center);
27546         contextEl.rotate(this.rotate * Math.PI / 180);
27547
27548         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27549         
27550         this.canvasEl = document.createElement("canvas");
27551         
27552         this.contextEl = this.canvasEl.getContext("2d");
27553         
27554         switch (this.rotate) {
27555             case 0 :
27556                 
27557                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27558                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27559                 
27560                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27561                 
27562                 break;
27563             case 90 : 
27564                 
27565                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27566                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27567                 
27568                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27569                     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);
27570                     break;
27571                 }
27572                 
27573                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27574                 
27575                 break;
27576             case 180 :
27577                 
27578                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27579                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27580                 
27581                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27582                     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);
27583                     break;
27584                 }
27585                 
27586                 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);
27587                 
27588                 break;
27589             case 270 :
27590                 
27591                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27592                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27593         
27594                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27595                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27596                     break;
27597                 }
27598                 
27599                 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);
27600                 
27601                 break;
27602             default : 
27603                 break;
27604         }
27605         
27606         this.previewEl.appendChild(this.canvasEl);
27607         
27608         this.setCanvasPosition();
27609     },
27610     
27611     crop : function()
27612     {
27613         if(!this.canvasLoaded){
27614             return;
27615         }
27616         
27617         var imageCanvas = document.createElement("canvas");
27618         
27619         var imageContext = imageCanvas.getContext("2d");
27620         
27621         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27622         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27623         
27624         var center = imageCanvas.width / 2;
27625         
27626         imageContext.translate(center, center);
27627         
27628         imageContext.rotate(this.rotate * Math.PI / 180);
27629         
27630         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27631         
27632         var canvas = document.createElement("canvas");
27633         
27634         var context = canvas.getContext("2d");
27635                 
27636         canvas.width = this.minWidth;
27637         canvas.height = this.minHeight;
27638
27639         switch (this.rotate) {
27640             case 0 :
27641                 
27642                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27643                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27644                 
27645                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27646                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27647                 
27648                 var targetWidth = this.minWidth - 2 * x;
27649                 var targetHeight = this.minHeight - 2 * y;
27650                 
27651                 var scale = 1;
27652                 
27653                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27654                     scale = targetWidth / width;
27655                 }
27656                 
27657                 if(x > 0 && y == 0){
27658                     scale = targetHeight / height;
27659                 }
27660                 
27661                 if(x > 0 && y > 0){
27662                     scale = targetWidth / width;
27663                     
27664                     if(width < height){
27665                         scale = targetHeight / height;
27666                     }
27667                 }
27668                 
27669                 context.scale(scale, scale);
27670                 
27671                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27672                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27673
27674                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27675                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27676
27677                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27678                 
27679                 break;
27680             case 90 : 
27681                 
27682                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27683                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27684                 
27685                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27686                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27687                 
27688                 var targetWidth = this.minWidth - 2 * x;
27689                 var targetHeight = this.minHeight - 2 * y;
27690                 
27691                 var scale = 1;
27692                 
27693                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27694                     scale = targetWidth / width;
27695                 }
27696                 
27697                 if(x > 0 && y == 0){
27698                     scale = targetHeight / height;
27699                 }
27700                 
27701                 if(x > 0 && y > 0){
27702                     scale = targetWidth / width;
27703                     
27704                     if(width < height){
27705                         scale = targetHeight / height;
27706                     }
27707                 }
27708                 
27709                 context.scale(scale, scale);
27710                 
27711                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27712                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27713
27714                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27715                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27716                 
27717                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27718                 
27719                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27720                 
27721                 break;
27722             case 180 :
27723                 
27724                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27725                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27726                 
27727                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27728                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27729                 
27730                 var targetWidth = this.minWidth - 2 * x;
27731                 var targetHeight = this.minHeight - 2 * y;
27732                 
27733                 var scale = 1;
27734                 
27735                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27736                     scale = targetWidth / width;
27737                 }
27738                 
27739                 if(x > 0 && y == 0){
27740                     scale = targetHeight / height;
27741                 }
27742                 
27743                 if(x > 0 && y > 0){
27744                     scale = targetWidth / width;
27745                     
27746                     if(width < height){
27747                         scale = targetHeight / height;
27748                     }
27749                 }
27750                 
27751                 context.scale(scale, scale);
27752                 
27753                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27754                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27755
27756                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27757                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27758
27759                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27760                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27761                 
27762                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27763                 
27764                 break;
27765             case 270 :
27766                 
27767                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27768                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27769                 
27770                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27771                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27772                 
27773                 var targetWidth = this.minWidth - 2 * x;
27774                 var targetHeight = this.minHeight - 2 * y;
27775                 
27776                 var scale = 1;
27777                 
27778                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27779                     scale = targetWidth / width;
27780                 }
27781                 
27782                 if(x > 0 && y == 0){
27783                     scale = targetHeight / height;
27784                 }
27785                 
27786                 if(x > 0 && y > 0){
27787                     scale = targetWidth / width;
27788                     
27789                     if(width < height){
27790                         scale = targetHeight / height;
27791                     }
27792                 }
27793                 
27794                 context.scale(scale, scale);
27795                 
27796                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27797                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27798
27799                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27800                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27801                 
27802                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27803                 
27804                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27805                 
27806                 break;
27807             default : 
27808                 break;
27809         }
27810         
27811         this.cropData = canvas.toDataURL(this.cropType);
27812         
27813         if(this.fireEvent('crop', this, this.cropData) !== false){
27814             this.process(this.file, this.cropData);
27815         }
27816         
27817         return;
27818         
27819     },
27820     
27821     setThumbBoxSize : function()
27822     {
27823         var width, height;
27824         
27825         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27826             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27827             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27828             
27829             this.minWidth = width;
27830             this.minHeight = height;
27831             
27832             if(this.rotate == 90 || this.rotate == 270){
27833                 this.minWidth = height;
27834                 this.minHeight = width;
27835             }
27836         }
27837         
27838         height = 300;
27839         width = Math.ceil(this.minWidth * height / this.minHeight);
27840         
27841         if(this.minWidth > this.minHeight){
27842             width = 300;
27843             height = Math.ceil(this.minHeight * width / this.minWidth);
27844         }
27845         
27846         this.thumbEl.setStyle({
27847             width : width + 'px',
27848             height : height + 'px'
27849         });
27850
27851         return;
27852             
27853     },
27854     
27855     setThumbBoxPosition : function()
27856     {
27857         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27858         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27859         
27860         this.thumbEl.setLeft(x);
27861         this.thumbEl.setTop(y);
27862         
27863     },
27864     
27865     baseRotateLevel : function()
27866     {
27867         this.baseRotate = 1;
27868         
27869         if(
27870                 typeof(this.exif) != 'undefined' &&
27871                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27872                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27873         ){
27874             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27875         }
27876         
27877         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27878         
27879     },
27880     
27881     baseScaleLevel : function()
27882     {
27883         var width, height;
27884         
27885         if(this.isDocument){
27886             
27887             if(this.baseRotate == 6 || this.baseRotate == 8){
27888             
27889                 height = this.thumbEl.getHeight();
27890                 this.baseScale = height / this.imageEl.OriginWidth;
27891
27892                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27893                     width = this.thumbEl.getWidth();
27894                     this.baseScale = width / this.imageEl.OriginHeight;
27895                 }
27896
27897                 return;
27898             }
27899
27900             height = this.thumbEl.getHeight();
27901             this.baseScale = height / this.imageEl.OriginHeight;
27902
27903             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27904                 width = this.thumbEl.getWidth();
27905                 this.baseScale = width / this.imageEl.OriginWidth;
27906             }
27907
27908             return;
27909         }
27910         
27911         if(this.baseRotate == 6 || this.baseRotate == 8){
27912             
27913             width = this.thumbEl.getHeight();
27914             this.baseScale = width / this.imageEl.OriginHeight;
27915             
27916             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27917                 height = this.thumbEl.getWidth();
27918                 this.baseScale = height / this.imageEl.OriginHeight;
27919             }
27920             
27921             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27922                 height = this.thumbEl.getWidth();
27923                 this.baseScale = height / this.imageEl.OriginHeight;
27924                 
27925                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27926                     width = this.thumbEl.getHeight();
27927                     this.baseScale = width / this.imageEl.OriginWidth;
27928                 }
27929             }
27930             
27931             return;
27932         }
27933         
27934         width = this.thumbEl.getWidth();
27935         this.baseScale = width / this.imageEl.OriginWidth;
27936         
27937         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27938             height = this.thumbEl.getHeight();
27939             this.baseScale = height / this.imageEl.OriginHeight;
27940         }
27941         
27942         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27943             
27944             height = this.thumbEl.getHeight();
27945             this.baseScale = height / this.imageEl.OriginHeight;
27946             
27947             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27948                 width = this.thumbEl.getWidth();
27949                 this.baseScale = width / this.imageEl.OriginWidth;
27950             }
27951             
27952         }
27953         
27954         return;
27955     },
27956     
27957     getScaleLevel : function()
27958     {
27959         return this.baseScale * Math.pow(1.1, this.scale);
27960     },
27961     
27962     onTouchStart : function(e)
27963     {
27964         if(!this.canvasLoaded){
27965             this.beforeSelectFile(e);
27966             return;
27967         }
27968         
27969         var touches = e.browserEvent.touches;
27970         
27971         if(!touches){
27972             return;
27973         }
27974         
27975         if(touches.length == 1){
27976             this.onMouseDown(e);
27977             return;
27978         }
27979         
27980         if(touches.length != 2){
27981             return;
27982         }
27983         
27984         var coords = [];
27985         
27986         for(var i = 0, finger; finger = touches[i]; i++){
27987             coords.push(finger.pageX, finger.pageY);
27988         }
27989         
27990         var x = Math.pow(coords[0] - coords[2], 2);
27991         var y = Math.pow(coords[1] - coords[3], 2);
27992         
27993         this.startDistance = Math.sqrt(x + y);
27994         
27995         this.startScale = this.scale;
27996         
27997         this.pinching = true;
27998         this.dragable = false;
27999         
28000     },
28001     
28002     onTouchMove : function(e)
28003     {
28004         if(!this.pinching && !this.dragable){
28005             return;
28006         }
28007         
28008         var touches = e.browserEvent.touches;
28009         
28010         if(!touches){
28011             return;
28012         }
28013         
28014         if(this.dragable){
28015             this.onMouseMove(e);
28016             return;
28017         }
28018         
28019         var coords = [];
28020         
28021         for(var i = 0, finger; finger = touches[i]; i++){
28022             coords.push(finger.pageX, finger.pageY);
28023         }
28024         
28025         var x = Math.pow(coords[0] - coords[2], 2);
28026         var y = Math.pow(coords[1] - coords[3], 2);
28027         
28028         this.endDistance = Math.sqrt(x + y);
28029         
28030         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28031         
28032         if(!this.zoomable()){
28033             this.scale = this.startScale;
28034             return;
28035         }
28036         
28037         this.draw();
28038         
28039     },
28040     
28041     onTouchEnd : function(e)
28042     {
28043         this.pinching = false;
28044         this.dragable = false;
28045         
28046     },
28047     
28048     process : function(file, crop)
28049     {
28050         if(this.loadMask){
28051             this.maskEl.mask(this.loadingText);
28052         }
28053         
28054         this.xhr = new XMLHttpRequest();
28055         
28056         file.xhr = this.xhr;
28057
28058         this.xhr.open(this.method, this.url, true);
28059         
28060         var headers = {
28061             "Accept": "application/json",
28062             "Cache-Control": "no-cache",
28063             "X-Requested-With": "XMLHttpRequest"
28064         };
28065         
28066         for (var headerName in headers) {
28067             var headerValue = headers[headerName];
28068             if (headerValue) {
28069                 this.xhr.setRequestHeader(headerName, headerValue);
28070             }
28071         }
28072         
28073         var _this = this;
28074         
28075         this.xhr.onload = function()
28076         {
28077             _this.xhrOnLoad(_this.xhr);
28078         }
28079         
28080         this.xhr.onerror = function()
28081         {
28082             _this.xhrOnError(_this.xhr);
28083         }
28084         
28085         var formData = new FormData();
28086
28087         formData.append('returnHTML', 'NO');
28088         
28089         if(crop){
28090             formData.append('crop', crop);
28091         }
28092         
28093         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28094             formData.append(this.paramName, file, file.name);
28095         }
28096         
28097         if(typeof(file.filename) != 'undefined'){
28098             formData.append('filename', file.filename);
28099         }
28100         
28101         if(typeof(file.mimetype) != 'undefined'){
28102             formData.append('mimetype', file.mimetype);
28103         }
28104         
28105         if(this.fireEvent('arrange', this, formData) != false){
28106             this.xhr.send(formData);
28107         };
28108     },
28109     
28110     xhrOnLoad : function(xhr)
28111     {
28112         if(this.loadMask){
28113             this.maskEl.unmask();
28114         }
28115         
28116         if (xhr.readyState !== 4) {
28117             this.fireEvent('exception', this, xhr);
28118             return;
28119         }
28120
28121         var response = Roo.decode(xhr.responseText);
28122         
28123         if(!response.success){
28124             this.fireEvent('exception', this, xhr);
28125             return;
28126         }
28127         
28128         var response = Roo.decode(xhr.responseText);
28129         
28130         this.fireEvent('upload', this, response);
28131         
28132     },
28133     
28134     xhrOnError : function()
28135     {
28136         if(this.loadMask){
28137             this.maskEl.unmask();
28138         }
28139         
28140         Roo.log('xhr on error');
28141         
28142         var response = Roo.decode(xhr.responseText);
28143           
28144         Roo.log(response);
28145         
28146     },
28147     
28148     prepare : function(file)
28149     {   
28150         if(this.loadMask){
28151             this.maskEl.mask(this.loadingText);
28152         }
28153         
28154         this.file = false;
28155         this.exif = {};
28156         
28157         if(typeof(file) === 'string'){
28158             this.loadCanvas(file);
28159             return;
28160         }
28161         
28162         if(!file || !this.urlAPI){
28163             return;
28164         }
28165         
28166         this.file = file;
28167         this.cropType = file.type;
28168         
28169         var _this = this;
28170         
28171         if(this.fireEvent('prepare', this, this.file) != false){
28172             
28173             var reader = new FileReader();
28174             
28175             reader.onload = function (e) {
28176                 if (e.target.error) {
28177                     Roo.log(e.target.error);
28178                     return;
28179                 }
28180                 
28181                 var buffer = e.target.result,
28182                     dataView = new DataView(buffer),
28183                     offset = 2,
28184                     maxOffset = dataView.byteLength - 4,
28185                     markerBytes,
28186                     markerLength;
28187                 
28188                 if (dataView.getUint16(0) === 0xffd8) {
28189                     while (offset < maxOffset) {
28190                         markerBytes = dataView.getUint16(offset);
28191                         
28192                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28193                             markerLength = dataView.getUint16(offset + 2) + 2;
28194                             if (offset + markerLength > dataView.byteLength) {
28195                                 Roo.log('Invalid meta data: Invalid segment size.');
28196                                 break;
28197                             }
28198                             
28199                             if(markerBytes == 0xffe1){
28200                                 _this.parseExifData(
28201                                     dataView,
28202                                     offset,
28203                                     markerLength
28204                                 );
28205                             }
28206                             
28207                             offset += markerLength;
28208                             
28209                             continue;
28210                         }
28211                         
28212                         break;
28213                     }
28214                     
28215                 }
28216                 
28217                 var url = _this.urlAPI.createObjectURL(_this.file);
28218                 
28219                 _this.loadCanvas(url);
28220                 
28221                 return;
28222             }
28223             
28224             reader.readAsArrayBuffer(this.file);
28225             
28226         }
28227         
28228     },
28229     
28230     parseExifData : function(dataView, offset, length)
28231     {
28232         var tiffOffset = offset + 10,
28233             littleEndian,
28234             dirOffset;
28235     
28236         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28237             // No Exif data, might be XMP data instead
28238             return;
28239         }
28240         
28241         // Check for the ASCII code for "Exif" (0x45786966):
28242         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28243             // No Exif data, might be XMP data instead
28244             return;
28245         }
28246         if (tiffOffset + 8 > dataView.byteLength) {
28247             Roo.log('Invalid Exif data: Invalid segment size.');
28248             return;
28249         }
28250         // Check for the two null bytes:
28251         if (dataView.getUint16(offset + 8) !== 0x0000) {
28252             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28253             return;
28254         }
28255         // Check the byte alignment:
28256         switch (dataView.getUint16(tiffOffset)) {
28257         case 0x4949:
28258             littleEndian = true;
28259             break;
28260         case 0x4D4D:
28261             littleEndian = false;
28262             break;
28263         default:
28264             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28265             return;
28266         }
28267         // Check for the TIFF tag marker (0x002A):
28268         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28269             Roo.log('Invalid Exif data: Missing TIFF marker.');
28270             return;
28271         }
28272         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28273         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28274         
28275         this.parseExifTags(
28276             dataView,
28277             tiffOffset,
28278             tiffOffset + dirOffset,
28279             littleEndian
28280         );
28281     },
28282     
28283     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28284     {
28285         var tagsNumber,
28286             dirEndOffset,
28287             i;
28288         if (dirOffset + 6 > dataView.byteLength) {
28289             Roo.log('Invalid Exif data: Invalid directory offset.');
28290             return;
28291         }
28292         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28293         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28294         if (dirEndOffset + 4 > dataView.byteLength) {
28295             Roo.log('Invalid Exif data: Invalid directory size.');
28296             return;
28297         }
28298         for (i = 0; i < tagsNumber; i += 1) {
28299             this.parseExifTag(
28300                 dataView,
28301                 tiffOffset,
28302                 dirOffset + 2 + 12 * i, // tag offset
28303                 littleEndian
28304             );
28305         }
28306         // Return the offset to the next directory:
28307         return dataView.getUint32(dirEndOffset, littleEndian);
28308     },
28309     
28310     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28311     {
28312         var tag = dataView.getUint16(offset, littleEndian);
28313         
28314         this.exif[tag] = this.getExifValue(
28315             dataView,
28316             tiffOffset,
28317             offset,
28318             dataView.getUint16(offset + 2, littleEndian), // tag type
28319             dataView.getUint32(offset + 4, littleEndian), // tag length
28320             littleEndian
28321         );
28322     },
28323     
28324     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28325     {
28326         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28327             tagSize,
28328             dataOffset,
28329             values,
28330             i,
28331             str,
28332             c;
28333     
28334         if (!tagType) {
28335             Roo.log('Invalid Exif data: Invalid tag type.');
28336             return;
28337         }
28338         
28339         tagSize = tagType.size * length;
28340         // Determine if the value is contained in the dataOffset bytes,
28341         // or if the value at the dataOffset is a pointer to the actual data:
28342         dataOffset = tagSize > 4 ?
28343                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28344         if (dataOffset + tagSize > dataView.byteLength) {
28345             Roo.log('Invalid Exif data: Invalid data offset.');
28346             return;
28347         }
28348         if (length === 1) {
28349             return tagType.getValue(dataView, dataOffset, littleEndian);
28350         }
28351         values = [];
28352         for (i = 0; i < length; i += 1) {
28353             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28354         }
28355         
28356         if (tagType.ascii) {
28357             str = '';
28358             // Concatenate the chars:
28359             for (i = 0; i < values.length; i += 1) {
28360                 c = values[i];
28361                 // Ignore the terminating NULL byte(s):
28362                 if (c === '\u0000') {
28363                     break;
28364                 }
28365                 str += c;
28366             }
28367             return str;
28368         }
28369         return values;
28370     }
28371     
28372 });
28373
28374 Roo.apply(Roo.bootstrap.UploadCropbox, {
28375     tags : {
28376         'Orientation': 0x0112
28377     },
28378     
28379     Orientation: {
28380             1: 0, //'top-left',
28381 //            2: 'top-right',
28382             3: 180, //'bottom-right',
28383 //            4: 'bottom-left',
28384 //            5: 'left-top',
28385             6: 90, //'right-top',
28386 //            7: 'right-bottom',
28387             8: 270 //'left-bottom'
28388     },
28389     
28390     exifTagTypes : {
28391         // byte, 8-bit unsigned int:
28392         1: {
28393             getValue: function (dataView, dataOffset) {
28394                 return dataView.getUint8(dataOffset);
28395             },
28396             size: 1
28397         },
28398         // ascii, 8-bit byte:
28399         2: {
28400             getValue: function (dataView, dataOffset) {
28401                 return String.fromCharCode(dataView.getUint8(dataOffset));
28402             },
28403             size: 1,
28404             ascii: true
28405         },
28406         // short, 16 bit int:
28407         3: {
28408             getValue: function (dataView, dataOffset, littleEndian) {
28409                 return dataView.getUint16(dataOffset, littleEndian);
28410             },
28411             size: 2
28412         },
28413         // long, 32 bit int:
28414         4: {
28415             getValue: function (dataView, dataOffset, littleEndian) {
28416                 return dataView.getUint32(dataOffset, littleEndian);
28417             },
28418             size: 4
28419         },
28420         // rational = two long values, first is numerator, second is denominator:
28421         5: {
28422             getValue: function (dataView, dataOffset, littleEndian) {
28423                 return dataView.getUint32(dataOffset, littleEndian) /
28424                     dataView.getUint32(dataOffset + 4, littleEndian);
28425             },
28426             size: 8
28427         },
28428         // slong, 32 bit signed int:
28429         9: {
28430             getValue: function (dataView, dataOffset, littleEndian) {
28431                 return dataView.getInt32(dataOffset, littleEndian);
28432             },
28433             size: 4
28434         },
28435         // srational, two slongs, first is numerator, second is denominator:
28436         10: {
28437             getValue: function (dataView, dataOffset, littleEndian) {
28438                 return dataView.getInt32(dataOffset, littleEndian) /
28439                     dataView.getInt32(dataOffset + 4, littleEndian);
28440             },
28441             size: 8
28442         }
28443     },
28444     
28445     footer : {
28446         STANDARD : [
28447             {
28448                 tag : 'div',
28449                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28450                 action : 'rotate-left',
28451                 cn : [
28452                     {
28453                         tag : 'button',
28454                         cls : 'btn btn-default',
28455                         html : '<i class="fa fa-undo"></i>'
28456                     }
28457                 ]
28458             },
28459             {
28460                 tag : 'div',
28461                 cls : 'btn-group roo-upload-cropbox-picture',
28462                 action : 'picture',
28463                 cn : [
28464                     {
28465                         tag : 'button',
28466                         cls : 'btn btn-default',
28467                         html : '<i class="fa fa-picture-o"></i>'
28468                     }
28469                 ]
28470             },
28471             {
28472                 tag : 'div',
28473                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28474                 action : 'rotate-right',
28475                 cn : [
28476                     {
28477                         tag : 'button',
28478                         cls : 'btn btn-default',
28479                         html : '<i class="fa fa-repeat"></i>'
28480                     }
28481                 ]
28482             }
28483         ],
28484         DOCUMENT : [
28485             {
28486                 tag : 'div',
28487                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28488                 action : 'rotate-left',
28489                 cn : [
28490                     {
28491                         tag : 'button',
28492                         cls : 'btn btn-default',
28493                         html : '<i class="fa fa-undo"></i>'
28494                     }
28495                 ]
28496             },
28497             {
28498                 tag : 'div',
28499                 cls : 'btn-group roo-upload-cropbox-download',
28500                 action : 'download',
28501                 cn : [
28502                     {
28503                         tag : 'button',
28504                         cls : 'btn btn-default',
28505                         html : '<i class="fa fa-download"></i>'
28506                     }
28507                 ]
28508             },
28509             {
28510                 tag : 'div',
28511                 cls : 'btn-group roo-upload-cropbox-crop',
28512                 action : 'crop',
28513                 cn : [
28514                     {
28515                         tag : 'button',
28516                         cls : 'btn btn-default',
28517                         html : '<i class="fa fa-crop"></i>'
28518                     }
28519                 ]
28520             },
28521             {
28522                 tag : 'div',
28523                 cls : 'btn-group roo-upload-cropbox-trash',
28524                 action : 'trash',
28525                 cn : [
28526                     {
28527                         tag : 'button',
28528                         cls : 'btn btn-default',
28529                         html : '<i class="fa fa-trash"></i>'
28530                     }
28531                 ]
28532             },
28533             {
28534                 tag : 'div',
28535                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28536                 action : 'rotate-right',
28537                 cn : [
28538                     {
28539                         tag : 'button',
28540                         cls : 'btn btn-default',
28541                         html : '<i class="fa fa-repeat"></i>'
28542                     }
28543                 ]
28544             }
28545         ],
28546         ROTATOR : [
28547             {
28548                 tag : 'div',
28549                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28550                 action : 'rotate-left',
28551                 cn : [
28552                     {
28553                         tag : 'button',
28554                         cls : 'btn btn-default',
28555                         html : '<i class="fa fa-undo"></i>'
28556                     }
28557                 ]
28558             },
28559             {
28560                 tag : 'div',
28561                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28562                 action : 'rotate-right',
28563                 cn : [
28564                     {
28565                         tag : 'button',
28566                         cls : 'btn btn-default',
28567                         html : '<i class="fa fa-repeat"></i>'
28568                     }
28569                 ]
28570             }
28571         ]
28572     }
28573 });
28574
28575 /*
28576 * Licence: LGPL
28577 */
28578
28579 /**
28580  * @class Roo.bootstrap.DocumentManager
28581  * @extends Roo.bootstrap.Component
28582  * Bootstrap DocumentManager class
28583  * @cfg {String} paramName default 'imageUpload'
28584  * @cfg {String} toolTipName default 'filename'
28585  * @cfg {String} method default POST
28586  * @cfg {String} url action url
28587  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28588  * @cfg {Boolean} multiple multiple upload default true
28589  * @cfg {Number} thumbSize default 300
28590  * @cfg {String} fieldLabel
28591  * @cfg {Number} labelWidth default 4
28592  * @cfg {String} labelAlign (left|top) default left
28593  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28594 * @cfg {Number} labellg set the width of label (1-12)
28595  * @cfg {Number} labelmd set the width of label (1-12)
28596  * @cfg {Number} labelsm set the width of label (1-12)
28597  * @cfg {Number} labelxs set the width of label (1-12)
28598  * 
28599  * @constructor
28600  * Create a new DocumentManager
28601  * @param {Object} config The config object
28602  */
28603
28604 Roo.bootstrap.DocumentManager = function(config){
28605     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28606     
28607     this.files = [];
28608     this.delegates = [];
28609     
28610     this.addEvents({
28611         /**
28612          * @event initial
28613          * Fire when initial the DocumentManager
28614          * @param {Roo.bootstrap.DocumentManager} this
28615          */
28616         "initial" : true,
28617         /**
28618          * @event inspect
28619          * inspect selected file
28620          * @param {Roo.bootstrap.DocumentManager} this
28621          * @param {File} file
28622          */
28623         "inspect" : true,
28624         /**
28625          * @event exception
28626          * Fire when xhr load exception
28627          * @param {Roo.bootstrap.DocumentManager} this
28628          * @param {XMLHttpRequest} xhr
28629          */
28630         "exception" : true,
28631         /**
28632          * @event afterupload
28633          * Fire when xhr load exception
28634          * @param {Roo.bootstrap.DocumentManager} this
28635          * @param {XMLHttpRequest} xhr
28636          */
28637         "afterupload" : true,
28638         /**
28639          * @event prepare
28640          * prepare the form data
28641          * @param {Roo.bootstrap.DocumentManager} this
28642          * @param {Object} formData
28643          */
28644         "prepare" : true,
28645         /**
28646          * @event remove
28647          * Fire when remove the file
28648          * @param {Roo.bootstrap.DocumentManager} this
28649          * @param {Object} file
28650          */
28651         "remove" : true,
28652         /**
28653          * @event refresh
28654          * Fire after refresh the file
28655          * @param {Roo.bootstrap.DocumentManager} this
28656          */
28657         "refresh" : true,
28658         /**
28659          * @event click
28660          * Fire after click the image
28661          * @param {Roo.bootstrap.DocumentManager} this
28662          * @param {Object} file
28663          */
28664         "click" : true,
28665         /**
28666          * @event edit
28667          * Fire when upload a image and editable set to true
28668          * @param {Roo.bootstrap.DocumentManager} this
28669          * @param {Object} file
28670          */
28671         "edit" : true,
28672         /**
28673          * @event beforeselectfile
28674          * Fire before select file
28675          * @param {Roo.bootstrap.DocumentManager} this
28676          */
28677         "beforeselectfile" : true,
28678         /**
28679          * @event process
28680          * Fire before process file
28681          * @param {Roo.bootstrap.DocumentManager} this
28682          * @param {Object} file
28683          */
28684         "process" : true,
28685         /**
28686          * @event previewrendered
28687          * Fire when preview rendered
28688          * @param {Roo.bootstrap.DocumentManager} this
28689          * @param {Object} file
28690          */
28691         "previewrendered" : true
28692         
28693     });
28694 };
28695
28696 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28697     
28698     boxes : 0,
28699     inputName : '',
28700     thumbSize : 300,
28701     multiple : true,
28702     files : false,
28703     method : 'POST',
28704     url : '',
28705     paramName : 'imageUpload',
28706     toolTipName : 'filename',
28707     fieldLabel : '',
28708     labelWidth : 4,
28709     labelAlign : 'left',
28710     editable : true,
28711     delegates : false,
28712     xhr : false, 
28713     
28714     labellg : 0,
28715     labelmd : 0,
28716     labelsm : 0,
28717     labelxs : 0,
28718     
28719     getAutoCreate : function()
28720     {   
28721         var managerWidget = {
28722             tag : 'div',
28723             cls : 'roo-document-manager',
28724             cn : [
28725                 {
28726                     tag : 'input',
28727                     cls : 'roo-document-manager-selector',
28728                     type : 'file'
28729                 },
28730                 {
28731                     tag : 'div',
28732                     cls : 'roo-document-manager-uploader',
28733                     cn : [
28734                         {
28735                             tag : 'div',
28736                             cls : 'roo-document-manager-upload-btn',
28737                             html : '<i class="fa fa-plus"></i>'
28738                         }
28739                     ]
28740                     
28741                 }
28742             ]
28743         };
28744         
28745         var content = [
28746             {
28747                 tag : 'div',
28748                 cls : 'column col-md-12',
28749                 cn : managerWidget
28750             }
28751         ];
28752         
28753         if(this.fieldLabel.length){
28754             
28755             content = [
28756                 {
28757                     tag : 'div',
28758                     cls : 'column col-md-12',
28759                     html : this.fieldLabel
28760                 },
28761                 {
28762                     tag : 'div',
28763                     cls : 'column col-md-12',
28764                     cn : managerWidget
28765                 }
28766             ];
28767
28768             if(this.labelAlign == 'left'){
28769                 content = [
28770                     {
28771                         tag : 'div',
28772                         cls : 'column',
28773                         html : this.fieldLabel
28774                     },
28775                     {
28776                         tag : 'div',
28777                         cls : 'column',
28778                         cn : managerWidget
28779                     }
28780                 ];
28781                 
28782                 if(this.labelWidth > 12){
28783                     content[0].style = "width: " + this.labelWidth + 'px';
28784                 }
28785
28786                 if(this.labelWidth < 13 && this.labelmd == 0){
28787                     this.labelmd = this.labelWidth;
28788                 }
28789
28790                 if(this.labellg > 0){
28791                     content[0].cls += ' col-lg-' + this.labellg;
28792                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28793                 }
28794
28795                 if(this.labelmd > 0){
28796                     content[0].cls += ' col-md-' + this.labelmd;
28797                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28798                 }
28799
28800                 if(this.labelsm > 0){
28801                     content[0].cls += ' col-sm-' + this.labelsm;
28802                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28803                 }
28804
28805                 if(this.labelxs > 0){
28806                     content[0].cls += ' col-xs-' + this.labelxs;
28807                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28808                 }
28809                 
28810             }
28811         }
28812         
28813         var cfg = {
28814             tag : 'div',
28815             cls : 'row clearfix',
28816             cn : content
28817         };
28818         
28819         return cfg;
28820         
28821     },
28822     
28823     initEvents : function()
28824     {
28825         this.managerEl = this.el.select('.roo-document-manager', true).first();
28826         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28827         
28828         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28829         this.selectorEl.hide();
28830         
28831         if(this.multiple){
28832             this.selectorEl.attr('multiple', 'multiple');
28833         }
28834         
28835         this.selectorEl.on('change', this.onFileSelected, this);
28836         
28837         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28838         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28839         
28840         this.uploader.on('click', this.onUploaderClick, this);
28841         
28842         this.renderProgressDialog();
28843         
28844         var _this = this;
28845         
28846         window.addEventListener("resize", function() { _this.refresh(); } );
28847         
28848         this.fireEvent('initial', this);
28849     },
28850     
28851     renderProgressDialog : function()
28852     {
28853         var _this = this;
28854         
28855         this.progressDialog = new Roo.bootstrap.Modal({
28856             cls : 'roo-document-manager-progress-dialog',
28857             allow_close : false,
28858             title : '',
28859             buttons : [
28860                 {
28861                     name  :'cancel',
28862                     weight : 'danger',
28863                     html : 'Cancel'
28864                 }
28865             ], 
28866             listeners : { 
28867                 btnclick : function() {
28868                     _this.uploadCancel();
28869                     this.hide();
28870                 }
28871             }
28872         });
28873          
28874         this.progressDialog.render(Roo.get(document.body));
28875          
28876         this.progress = new Roo.bootstrap.Progress({
28877             cls : 'roo-document-manager-progress',
28878             active : true,
28879             striped : true
28880         });
28881         
28882         this.progress.render(this.progressDialog.getChildContainer());
28883         
28884         this.progressBar = new Roo.bootstrap.ProgressBar({
28885             cls : 'roo-document-manager-progress-bar',
28886             aria_valuenow : 0,
28887             aria_valuemin : 0,
28888             aria_valuemax : 12,
28889             panel : 'success'
28890         });
28891         
28892         this.progressBar.render(this.progress.getChildContainer());
28893     },
28894     
28895     onUploaderClick : function(e)
28896     {
28897         e.preventDefault();
28898      
28899         if(this.fireEvent('beforeselectfile', this) != false){
28900             this.selectorEl.dom.click();
28901         }
28902         
28903     },
28904     
28905     onFileSelected : function(e)
28906     {
28907         e.preventDefault();
28908         
28909         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28910             return;
28911         }
28912         
28913         Roo.each(this.selectorEl.dom.files, function(file){
28914             if(this.fireEvent('inspect', this, file) != false){
28915                 this.files.push(file);
28916             }
28917         }, this);
28918         
28919         this.queue();
28920         
28921     },
28922     
28923     queue : function()
28924     {
28925         this.selectorEl.dom.value = '';
28926         
28927         if(!this.files || !this.files.length){
28928             return;
28929         }
28930         
28931         if(this.boxes > 0 && this.files.length > this.boxes){
28932             this.files = this.files.slice(0, this.boxes);
28933         }
28934         
28935         this.uploader.show();
28936         
28937         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28938             this.uploader.hide();
28939         }
28940         
28941         var _this = this;
28942         
28943         var files = [];
28944         
28945         var docs = [];
28946         
28947         Roo.each(this.files, function(file){
28948             
28949             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28950                 var f = this.renderPreview(file);
28951                 files.push(f);
28952                 return;
28953             }
28954             
28955             if(file.type.indexOf('image') != -1){
28956                 this.delegates.push(
28957                     (function(){
28958                         _this.process(file);
28959                     }).createDelegate(this)
28960                 );
28961         
28962                 return;
28963             }
28964             
28965             docs.push(
28966                 (function(){
28967                     _this.process(file);
28968                 }).createDelegate(this)
28969             );
28970             
28971         }, this);
28972         
28973         this.files = files;
28974         
28975         this.delegates = this.delegates.concat(docs);
28976         
28977         if(!this.delegates.length){
28978             this.refresh();
28979             return;
28980         }
28981         
28982         this.progressBar.aria_valuemax = this.delegates.length;
28983         
28984         this.arrange();
28985         
28986         return;
28987     },
28988     
28989     arrange : function()
28990     {
28991         if(!this.delegates.length){
28992             this.progressDialog.hide();
28993             this.refresh();
28994             return;
28995         }
28996         
28997         var delegate = this.delegates.shift();
28998         
28999         this.progressDialog.show();
29000         
29001         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29002         
29003         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29004         
29005         delegate();
29006     },
29007     
29008     refresh : function()
29009     {
29010         this.uploader.show();
29011         
29012         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29013             this.uploader.hide();
29014         }
29015         
29016         Roo.isTouch ? this.closable(false) : this.closable(true);
29017         
29018         this.fireEvent('refresh', this);
29019     },
29020     
29021     onRemove : function(e, el, o)
29022     {
29023         e.preventDefault();
29024         
29025         this.fireEvent('remove', this, o);
29026         
29027     },
29028     
29029     remove : function(o)
29030     {
29031         var files = [];
29032         
29033         Roo.each(this.files, function(file){
29034             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29035                 files.push(file);
29036                 return;
29037             }
29038
29039             o.target.remove();
29040
29041         }, this);
29042         
29043         this.files = files;
29044         
29045         this.refresh();
29046     },
29047     
29048     clear : function()
29049     {
29050         Roo.each(this.files, function(file){
29051             if(!file.target){
29052                 return;
29053             }
29054             
29055             file.target.remove();
29056
29057         }, this);
29058         
29059         this.files = [];
29060         
29061         this.refresh();
29062     },
29063     
29064     onClick : function(e, el, o)
29065     {
29066         e.preventDefault();
29067         
29068         this.fireEvent('click', this, o);
29069         
29070     },
29071     
29072     closable : function(closable)
29073     {
29074         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29075             
29076             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29077             
29078             if(closable){
29079                 el.show();
29080                 return;
29081             }
29082             
29083             el.hide();
29084             
29085         }, this);
29086     },
29087     
29088     xhrOnLoad : function(xhr)
29089     {
29090         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29091             el.remove();
29092         }, this);
29093         
29094         if (xhr.readyState !== 4) {
29095             this.arrange();
29096             this.fireEvent('exception', this, xhr);
29097             return;
29098         }
29099
29100         var response = Roo.decode(xhr.responseText);
29101         
29102         if(!response.success){
29103             this.arrange();
29104             this.fireEvent('exception', this, xhr);
29105             return;
29106         }
29107         
29108         var file = this.renderPreview(response.data);
29109         
29110         this.files.push(file);
29111         
29112         this.arrange();
29113         
29114         this.fireEvent('afterupload', this, xhr);
29115         
29116     },
29117     
29118     xhrOnError : function(xhr)
29119     {
29120         Roo.log('xhr on error');
29121         
29122         var response = Roo.decode(xhr.responseText);
29123           
29124         Roo.log(response);
29125         
29126         this.arrange();
29127     },
29128     
29129     process : function(file)
29130     {
29131         if(this.fireEvent('process', this, file) !== false){
29132             if(this.editable && file.type.indexOf('image') != -1){
29133                 this.fireEvent('edit', this, file);
29134                 return;
29135             }
29136
29137             this.uploadStart(file, false);
29138
29139             return;
29140         }
29141         
29142     },
29143     
29144     uploadStart : function(file, crop)
29145     {
29146         this.xhr = new XMLHttpRequest();
29147         
29148         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29149             this.arrange();
29150             return;
29151         }
29152         
29153         file.xhr = this.xhr;
29154             
29155         this.managerEl.createChild({
29156             tag : 'div',
29157             cls : 'roo-document-manager-loading',
29158             cn : [
29159                 {
29160                     tag : 'div',
29161                     tooltip : file.name,
29162                     cls : 'roo-document-manager-thumb',
29163                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29164                 }
29165             ]
29166
29167         });
29168
29169         this.xhr.open(this.method, this.url, true);
29170         
29171         var headers = {
29172             "Accept": "application/json",
29173             "Cache-Control": "no-cache",
29174             "X-Requested-With": "XMLHttpRequest"
29175         };
29176         
29177         for (var headerName in headers) {
29178             var headerValue = headers[headerName];
29179             if (headerValue) {
29180                 this.xhr.setRequestHeader(headerName, headerValue);
29181             }
29182         }
29183         
29184         var _this = this;
29185         
29186         this.xhr.onload = function()
29187         {
29188             _this.xhrOnLoad(_this.xhr);
29189         }
29190         
29191         this.xhr.onerror = function()
29192         {
29193             _this.xhrOnError(_this.xhr);
29194         }
29195         
29196         var formData = new FormData();
29197
29198         formData.append('returnHTML', 'NO');
29199         
29200         if(crop){
29201             formData.append('crop', crop);
29202         }
29203         
29204         formData.append(this.paramName, file, file.name);
29205         
29206         var options = {
29207             file : file, 
29208             manually : false
29209         };
29210         
29211         if(this.fireEvent('prepare', this, formData, options) != false){
29212             
29213             if(options.manually){
29214                 return;
29215             }
29216             
29217             this.xhr.send(formData);
29218             return;
29219         };
29220         
29221         this.uploadCancel();
29222     },
29223     
29224     uploadCancel : function()
29225     {
29226         if (this.xhr) {
29227             this.xhr.abort();
29228         }
29229         
29230         this.delegates = [];
29231         
29232         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29233             el.remove();
29234         }, this);
29235         
29236         this.arrange();
29237     },
29238     
29239     renderPreview : function(file)
29240     {
29241         if(typeof(file.target) != 'undefined' && file.target){
29242             return file;
29243         }
29244         
29245         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29246         
29247         var previewEl = this.managerEl.createChild({
29248             tag : 'div',
29249             cls : 'roo-document-manager-preview',
29250             cn : [
29251                 {
29252                     tag : 'div',
29253                     tooltip : file[this.toolTipName],
29254                     cls : 'roo-document-manager-thumb',
29255                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29256                 },
29257                 {
29258                     tag : 'button',
29259                     cls : 'close',
29260                     html : '<i class="fa fa-times-circle"></i>'
29261                 }
29262             ]
29263         });
29264
29265         var close = previewEl.select('button.close', true).first();
29266
29267         close.on('click', this.onRemove, this, file);
29268
29269         file.target = previewEl;
29270
29271         var image = previewEl.select('img', true).first();
29272         
29273         var _this = this;
29274         
29275         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29276         
29277         image.on('click', this.onClick, this, file);
29278         
29279         this.fireEvent('previewrendered', this, file);
29280         
29281         return file;
29282         
29283     },
29284     
29285     onPreviewLoad : function(file, image)
29286     {
29287         if(typeof(file.target) == 'undefined' || !file.target){
29288             return;
29289         }
29290         
29291         var width = image.dom.naturalWidth || image.dom.width;
29292         var height = image.dom.naturalHeight || image.dom.height;
29293         
29294         if(width > height){
29295             file.target.addClass('wide');
29296             return;
29297         }
29298         
29299         file.target.addClass('tall');
29300         return;
29301         
29302     },
29303     
29304     uploadFromSource : function(file, crop)
29305     {
29306         this.xhr = new XMLHttpRequest();
29307         
29308         this.managerEl.createChild({
29309             tag : 'div',
29310             cls : 'roo-document-manager-loading',
29311             cn : [
29312                 {
29313                     tag : 'div',
29314                     tooltip : file.name,
29315                     cls : 'roo-document-manager-thumb',
29316                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29317                 }
29318             ]
29319
29320         });
29321
29322         this.xhr.open(this.method, this.url, true);
29323         
29324         var headers = {
29325             "Accept": "application/json",
29326             "Cache-Control": "no-cache",
29327             "X-Requested-With": "XMLHttpRequest"
29328         };
29329         
29330         for (var headerName in headers) {
29331             var headerValue = headers[headerName];
29332             if (headerValue) {
29333                 this.xhr.setRequestHeader(headerName, headerValue);
29334             }
29335         }
29336         
29337         var _this = this;
29338         
29339         this.xhr.onload = function()
29340         {
29341             _this.xhrOnLoad(_this.xhr);
29342         }
29343         
29344         this.xhr.onerror = function()
29345         {
29346             _this.xhrOnError(_this.xhr);
29347         }
29348         
29349         var formData = new FormData();
29350
29351         formData.append('returnHTML', 'NO');
29352         
29353         formData.append('crop', crop);
29354         
29355         if(typeof(file.filename) != 'undefined'){
29356             formData.append('filename', file.filename);
29357         }
29358         
29359         if(typeof(file.mimetype) != 'undefined'){
29360             formData.append('mimetype', file.mimetype);
29361         }
29362         
29363         Roo.log(formData);
29364         
29365         if(this.fireEvent('prepare', this, formData) != false){
29366             this.xhr.send(formData);
29367         };
29368     }
29369 });
29370
29371 /*
29372 * Licence: LGPL
29373 */
29374
29375 /**
29376  * @class Roo.bootstrap.DocumentViewer
29377  * @extends Roo.bootstrap.Component
29378  * Bootstrap DocumentViewer class
29379  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29380  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29381  * 
29382  * @constructor
29383  * Create a new DocumentViewer
29384  * @param {Object} config The config object
29385  */
29386
29387 Roo.bootstrap.DocumentViewer = function(config){
29388     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29389     
29390     this.addEvents({
29391         /**
29392          * @event initial
29393          * Fire after initEvent
29394          * @param {Roo.bootstrap.DocumentViewer} this
29395          */
29396         "initial" : true,
29397         /**
29398          * @event click
29399          * Fire after click
29400          * @param {Roo.bootstrap.DocumentViewer} this
29401          */
29402         "click" : true,
29403         /**
29404          * @event download
29405          * Fire after download button
29406          * @param {Roo.bootstrap.DocumentViewer} this
29407          */
29408         "download" : true,
29409         /**
29410          * @event trash
29411          * Fire after trash button
29412          * @param {Roo.bootstrap.DocumentViewer} this
29413          */
29414         "trash" : true
29415         
29416     });
29417 };
29418
29419 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29420     
29421     showDownload : true,
29422     
29423     showTrash : true,
29424     
29425     getAutoCreate : function()
29426     {
29427         var cfg = {
29428             tag : 'div',
29429             cls : 'roo-document-viewer',
29430             cn : [
29431                 {
29432                     tag : 'div',
29433                     cls : 'roo-document-viewer-body',
29434                     cn : [
29435                         {
29436                             tag : 'div',
29437                             cls : 'roo-document-viewer-thumb',
29438                             cn : [
29439                                 {
29440                                     tag : 'img',
29441                                     cls : 'roo-document-viewer-image'
29442                                 }
29443                             ]
29444                         }
29445                     ]
29446                 },
29447                 {
29448                     tag : 'div',
29449                     cls : 'roo-document-viewer-footer',
29450                     cn : {
29451                         tag : 'div',
29452                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29453                         cn : [
29454                             {
29455                                 tag : 'div',
29456                                 cls : 'btn-group roo-document-viewer-download',
29457                                 cn : [
29458                                     {
29459                                         tag : 'button',
29460                                         cls : 'btn btn-default',
29461                                         html : '<i class="fa fa-download"></i>'
29462                                     }
29463                                 ]
29464                             },
29465                             {
29466                                 tag : 'div',
29467                                 cls : 'btn-group roo-document-viewer-trash',
29468                                 cn : [
29469                                     {
29470                                         tag : 'button',
29471                                         cls : 'btn btn-default',
29472                                         html : '<i class="fa fa-trash"></i>'
29473                                     }
29474                                 ]
29475                             }
29476                         ]
29477                     }
29478                 }
29479             ]
29480         };
29481         
29482         return cfg;
29483     },
29484     
29485     initEvents : function()
29486     {
29487         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29488         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29489         
29490         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29491         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29492         
29493         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29494         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29495         
29496         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29497         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29498         
29499         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29500         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29501         
29502         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29503         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29504         
29505         this.bodyEl.on('click', this.onClick, this);
29506         this.downloadBtn.on('click', this.onDownload, this);
29507         this.trashBtn.on('click', this.onTrash, this);
29508         
29509         this.downloadBtn.hide();
29510         this.trashBtn.hide();
29511         
29512         if(this.showDownload){
29513             this.downloadBtn.show();
29514         }
29515         
29516         if(this.showTrash){
29517             this.trashBtn.show();
29518         }
29519         
29520         if(!this.showDownload && !this.showTrash) {
29521             this.footerEl.hide();
29522         }
29523         
29524     },
29525     
29526     initial : function()
29527     {
29528         this.fireEvent('initial', this);
29529         
29530     },
29531     
29532     onClick : function(e)
29533     {
29534         e.preventDefault();
29535         
29536         this.fireEvent('click', this);
29537     },
29538     
29539     onDownload : function(e)
29540     {
29541         e.preventDefault();
29542         
29543         this.fireEvent('download', this);
29544     },
29545     
29546     onTrash : function(e)
29547     {
29548         e.preventDefault();
29549         
29550         this.fireEvent('trash', this);
29551     }
29552     
29553 });
29554 /*
29555  * - LGPL
29556  *
29557  * nav progress bar
29558  * 
29559  */
29560
29561 /**
29562  * @class Roo.bootstrap.NavProgressBar
29563  * @extends Roo.bootstrap.Component
29564  * Bootstrap NavProgressBar class
29565  * 
29566  * @constructor
29567  * Create a new nav progress bar
29568  * @param {Object} config The config object
29569  */
29570
29571 Roo.bootstrap.NavProgressBar = function(config){
29572     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29573
29574     this.bullets = this.bullets || [];
29575    
29576 //    Roo.bootstrap.NavProgressBar.register(this);
29577      this.addEvents({
29578         /**
29579              * @event changed
29580              * Fires when the active item changes
29581              * @param {Roo.bootstrap.NavProgressBar} this
29582              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29583              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29584          */
29585         'changed': true
29586      });
29587     
29588 };
29589
29590 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29591     
29592     bullets : [],
29593     barItems : [],
29594     
29595     getAutoCreate : function()
29596     {
29597         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29598         
29599         cfg = {
29600             tag : 'div',
29601             cls : 'roo-navigation-bar-group',
29602             cn : [
29603                 {
29604                     tag : 'div',
29605                     cls : 'roo-navigation-top-bar'
29606                 },
29607                 {
29608                     tag : 'div',
29609                     cls : 'roo-navigation-bullets-bar',
29610                     cn : [
29611                         {
29612                             tag : 'ul',
29613                             cls : 'roo-navigation-bar'
29614                         }
29615                     ]
29616                 },
29617                 
29618                 {
29619                     tag : 'div',
29620                     cls : 'roo-navigation-bottom-bar'
29621                 }
29622             ]
29623             
29624         };
29625         
29626         return cfg;
29627         
29628     },
29629     
29630     initEvents: function() 
29631     {
29632         
29633     },
29634     
29635     onRender : function(ct, position) 
29636     {
29637         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29638         
29639         if(this.bullets.length){
29640             Roo.each(this.bullets, function(b){
29641                this.addItem(b);
29642             }, this);
29643         }
29644         
29645         this.format();
29646         
29647     },
29648     
29649     addItem : function(cfg)
29650     {
29651         var item = new Roo.bootstrap.NavProgressItem(cfg);
29652         
29653         item.parentId = this.id;
29654         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29655         
29656         if(cfg.html){
29657             var top = new Roo.bootstrap.Element({
29658                 tag : 'div',
29659                 cls : 'roo-navigation-bar-text'
29660             });
29661             
29662             var bottom = new Roo.bootstrap.Element({
29663                 tag : 'div',
29664                 cls : 'roo-navigation-bar-text'
29665             });
29666             
29667             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29668             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29669             
29670             var topText = new Roo.bootstrap.Element({
29671                 tag : 'span',
29672                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29673             });
29674             
29675             var bottomText = new Roo.bootstrap.Element({
29676                 tag : 'span',
29677                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29678             });
29679             
29680             topText.onRender(top.el, null);
29681             bottomText.onRender(bottom.el, null);
29682             
29683             item.topEl = top;
29684             item.bottomEl = bottom;
29685         }
29686         
29687         this.barItems.push(item);
29688         
29689         return item;
29690     },
29691     
29692     getActive : function()
29693     {
29694         var active = false;
29695         
29696         Roo.each(this.barItems, function(v){
29697             
29698             if (!v.isActive()) {
29699                 return;
29700             }
29701             
29702             active = v;
29703             return false;
29704             
29705         });
29706         
29707         return active;
29708     },
29709     
29710     setActiveItem : function(item)
29711     {
29712         var prev = false;
29713         
29714         Roo.each(this.barItems, function(v){
29715             if (v.rid == item.rid) {
29716                 return ;
29717             }
29718             
29719             if (v.isActive()) {
29720                 v.setActive(false);
29721                 prev = v;
29722             }
29723         });
29724
29725         item.setActive(true);
29726         
29727         this.fireEvent('changed', this, item, prev);
29728     },
29729     
29730     getBarItem: function(rid)
29731     {
29732         var ret = false;
29733         
29734         Roo.each(this.barItems, function(e) {
29735             if (e.rid != rid) {
29736                 return;
29737             }
29738             
29739             ret =  e;
29740             return false;
29741         });
29742         
29743         return ret;
29744     },
29745     
29746     indexOfItem : function(item)
29747     {
29748         var index = false;
29749         
29750         Roo.each(this.barItems, function(v, i){
29751             
29752             if (v.rid != item.rid) {
29753                 return;
29754             }
29755             
29756             index = i;
29757             return false
29758         });
29759         
29760         return index;
29761     },
29762     
29763     setActiveNext : function()
29764     {
29765         var i = this.indexOfItem(this.getActive());
29766         
29767         if (i > this.barItems.length) {
29768             return;
29769         }
29770         
29771         this.setActiveItem(this.barItems[i+1]);
29772     },
29773     
29774     setActivePrev : function()
29775     {
29776         var i = this.indexOfItem(this.getActive());
29777         
29778         if (i  < 1) {
29779             return;
29780         }
29781         
29782         this.setActiveItem(this.barItems[i-1]);
29783     },
29784     
29785     format : function()
29786     {
29787         if(!this.barItems.length){
29788             return;
29789         }
29790      
29791         var width = 100 / this.barItems.length;
29792         
29793         Roo.each(this.barItems, function(i){
29794             i.el.setStyle('width', width + '%');
29795             i.topEl.el.setStyle('width', width + '%');
29796             i.bottomEl.el.setStyle('width', width + '%');
29797         }, this);
29798         
29799     }
29800     
29801 });
29802 /*
29803  * - LGPL
29804  *
29805  * Nav Progress Item
29806  * 
29807  */
29808
29809 /**
29810  * @class Roo.bootstrap.NavProgressItem
29811  * @extends Roo.bootstrap.Component
29812  * Bootstrap NavProgressItem class
29813  * @cfg {String} rid the reference id
29814  * @cfg {Boolean} active (true|false) Is item active default false
29815  * @cfg {Boolean} disabled (true|false) Is item active default false
29816  * @cfg {String} html
29817  * @cfg {String} position (top|bottom) text position default bottom
29818  * @cfg {String} icon show icon instead of number
29819  * 
29820  * @constructor
29821  * Create a new NavProgressItem
29822  * @param {Object} config The config object
29823  */
29824 Roo.bootstrap.NavProgressItem = function(config){
29825     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29826     this.addEvents({
29827         // raw events
29828         /**
29829          * @event click
29830          * The raw click event for the entire grid.
29831          * @param {Roo.bootstrap.NavProgressItem} this
29832          * @param {Roo.EventObject} e
29833          */
29834         "click" : true
29835     });
29836    
29837 };
29838
29839 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29840     
29841     rid : '',
29842     active : false,
29843     disabled : false,
29844     html : '',
29845     position : 'bottom',
29846     icon : false,
29847     
29848     getAutoCreate : function()
29849     {
29850         var iconCls = 'roo-navigation-bar-item-icon';
29851         
29852         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29853         
29854         var cfg = {
29855             tag: 'li',
29856             cls: 'roo-navigation-bar-item',
29857             cn : [
29858                 {
29859                     tag : 'i',
29860                     cls : iconCls
29861                 }
29862             ]
29863         };
29864         
29865         if(this.active){
29866             cfg.cls += ' active';
29867         }
29868         if(this.disabled){
29869             cfg.cls += ' disabled';
29870         }
29871         
29872         return cfg;
29873     },
29874     
29875     disable : function()
29876     {
29877         this.setDisabled(true);
29878     },
29879     
29880     enable : function()
29881     {
29882         this.setDisabled(false);
29883     },
29884     
29885     initEvents: function() 
29886     {
29887         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29888         
29889         this.iconEl.on('click', this.onClick, this);
29890     },
29891     
29892     onClick : function(e)
29893     {
29894         e.preventDefault();
29895         
29896         if(this.disabled){
29897             return;
29898         }
29899         
29900         if(this.fireEvent('click', this, e) === false){
29901             return;
29902         };
29903         
29904         this.parent().setActiveItem(this);
29905     },
29906     
29907     isActive: function () 
29908     {
29909         return this.active;
29910     },
29911     
29912     setActive : function(state)
29913     {
29914         if(this.active == state){
29915             return;
29916         }
29917         
29918         this.active = state;
29919         
29920         if (state) {
29921             this.el.addClass('active');
29922             return;
29923         }
29924         
29925         this.el.removeClass('active');
29926         
29927         return;
29928     },
29929     
29930     setDisabled : function(state)
29931     {
29932         if(this.disabled == state){
29933             return;
29934         }
29935         
29936         this.disabled = state;
29937         
29938         if (state) {
29939             this.el.addClass('disabled');
29940             return;
29941         }
29942         
29943         this.el.removeClass('disabled');
29944     },
29945     
29946     tooltipEl : function()
29947     {
29948         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29949     }
29950 });
29951  
29952
29953  /*
29954  * - LGPL
29955  *
29956  * FieldLabel
29957  * 
29958  */
29959
29960 /**
29961  * @class Roo.bootstrap.FieldLabel
29962  * @extends Roo.bootstrap.Component
29963  * Bootstrap FieldLabel class
29964  * @cfg {String} html contents of the element
29965  * @cfg {String} tag tag of the element default label
29966  * @cfg {String} cls class of the element
29967  * @cfg {String} target label target 
29968  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29969  * @cfg {String} invalidClass default "text-warning"
29970  * @cfg {String} validClass default "text-success"
29971  * @cfg {String} iconTooltip default "This field is required"
29972  * @cfg {String} indicatorpos (left|right) default left
29973  * 
29974  * @constructor
29975  * Create a new FieldLabel
29976  * @param {Object} config The config object
29977  */
29978
29979 Roo.bootstrap.FieldLabel = function(config){
29980     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29981     
29982     this.addEvents({
29983             /**
29984              * @event invalid
29985              * Fires after the field has been marked as invalid.
29986              * @param {Roo.form.FieldLabel} this
29987              * @param {String} msg The validation message
29988              */
29989             invalid : true,
29990             /**
29991              * @event valid
29992              * Fires after the field has been validated with no errors.
29993              * @param {Roo.form.FieldLabel} this
29994              */
29995             valid : true
29996         });
29997 };
29998
29999 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30000     
30001     tag: 'label',
30002     cls: '',
30003     html: '',
30004     target: '',
30005     allowBlank : true,
30006     invalidClass : 'has-warning',
30007     validClass : 'has-success',
30008     iconTooltip : 'This field is required',
30009     indicatorpos : 'left',
30010     
30011     getAutoCreate : function(){
30012         
30013         var cfg = {
30014             tag : this.tag,
30015             cls : 'roo-bootstrap-field-label ' + this.cls,
30016             for : this.target,
30017             cn : [
30018                 {
30019                     tag : 'i',
30020                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
30021                     tooltip : this.iconTooltip
30022                 },
30023                 {
30024                     tag : 'span',
30025                     html : this.html
30026                 }
30027             ] 
30028         };
30029         
30030         if(this.indicatorpos == 'right'){
30031             var cfg = {
30032                 tag : this.tag,
30033                 cls : 'roo-bootstrap-field-label ' + this.cls,
30034                 for : this.target,
30035                 cn : [
30036                     {
30037                         tag : 'span',
30038                         html : this.html
30039                     },
30040                     {
30041                         tag : 'i',
30042                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
30043                         tooltip : this.iconTooltip
30044                     }
30045                 ] 
30046             };
30047         }
30048         
30049         return cfg;
30050     },
30051     
30052     initEvents: function() 
30053     {
30054         Roo.bootstrap.Element.superclass.initEvents.call(this);
30055         
30056         this.indicator = this.indicatorEl();
30057         
30058         if(this.indicator){
30059             this.indicator.removeClass('visible');
30060             this.indicator.addClass('invisible');
30061         }
30062         
30063         Roo.bootstrap.FieldLabel.register(this);
30064     },
30065     
30066     indicatorEl : function()
30067     {
30068         var indicator = this.el.select('i.roo-required-indicator',true).first();
30069         
30070         if(!indicator){
30071             return false;
30072         }
30073         
30074         return indicator;
30075         
30076     },
30077     
30078     /**
30079      * Mark this field as valid
30080      */
30081     markValid : function()
30082     {
30083         if(this.indicator){
30084             this.indicator.removeClass('visible');
30085             this.indicator.addClass('invisible');
30086         }
30087         
30088         this.el.removeClass(this.invalidClass);
30089         
30090         this.el.addClass(this.validClass);
30091         
30092         this.fireEvent('valid', this);
30093     },
30094     
30095     /**
30096      * Mark this field as invalid
30097      * @param {String} msg The validation message
30098      */
30099     markInvalid : function(msg)
30100     {
30101         if(this.indicator){
30102             this.indicator.removeClass('invisible');
30103             this.indicator.addClass('visible');
30104         }
30105         
30106         this.el.removeClass(this.validClass);
30107         
30108         this.el.addClass(this.invalidClass);
30109         
30110         this.fireEvent('invalid', this, msg);
30111     }
30112     
30113    
30114 });
30115
30116 Roo.apply(Roo.bootstrap.FieldLabel, {
30117     
30118     groups: {},
30119     
30120      /**
30121     * register a FieldLabel Group
30122     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30123     */
30124     register : function(label)
30125     {
30126         if(this.groups.hasOwnProperty(label.target)){
30127             return;
30128         }
30129      
30130         this.groups[label.target] = label;
30131         
30132     },
30133     /**
30134     * fetch a FieldLabel Group based on the target
30135     * @param {string} target
30136     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30137     */
30138     get: function(target) {
30139         if (typeof(this.groups[target]) == 'undefined') {
30140             return false;
30141         }
30142         
30143         return this.groups[target] ;
30144     }
30145 });
30146
30147  
30148
30149  /*
30150  * - LGPL
30151  *
30152  * page DateSplitField.
30153  * 
30154  */
30155
30156
30157 /**
30158  * @class Roo.bootstrap.DateSplitField
30159  * @extends Roo.bootstrap.Component
30160  * Bootstrap DateSplitField class
30161  * @cfg {string} fieldLabel - the label associated
30162  * @cfg {Number} labelWidth set the width of label (0-12)
30163  * @cfg {String} labelAlign (top|left)
30164  * @cfg {Boolean} dayAllowBlank (true|false) default false
30165  * @cfg {Boolean} monthAllowBlank (true|false) default false
30166  * @cfg {Boolean} yearAllowBlank (true|false) default false
30167  * @cfg {string} dayPlaceholder 
30168  * @cfg {string} monthPlaceholder
30169  * @cfg {string} yearPlaceholder
30170  * @cfg {string} dayFormat default 'd'
30171  * @cfg {string} monthFormat default 'm'
30172  * @cfg {string} yearFormat default 'Y'
30173  * @cfg {Number} labellg set the width of label (1-12)
30174  * @cfg {Number} labelmd set the width of label (1-12)
30175  * @cfg {Number} labelsm set the width of label (1-12)
30176  * @cfg {Number} labelxs set the width of label (1-12)
30177
30178  *     
30179  * @constructor
30180  * Create a new DateSplitField
30181  * @param {Object} config The config object
30182  */
30183
30184 Roo.bootstrap.DateSplitField = function(config){
30185     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30186     
30187     this.addEvents({
30188         // raw events
30189          /**
30190          * @event years
30191          * getting the data of years
30192          * @param {Roo.bootstrap.DateSplitField} this
30193          * @param {Object} years
30194          */
30195         "years" : true,
30196         /**
30197          * @event days
30198          * getting the data of days
30199          * @param {Roo.bootstrap.DateSplitField} this
30200          * @param {Object} days
30201          */
30202         "days" : true,
30203         /**
30204          * @event invalid
30205          * Fires after the field has been marked as invalid.
30206          * @param {Roo.form.Field} this
30207          * @param {String} msg The validation message
30208          */
30209         invalid : true,
30210        /**
30211          * @event valid
30212          * Fires after the field has been validated with no errors.
30213          * @param {Roo.form.Field} this
30214          */
30215         valid : true
30216     });
30217 };
30218
30219 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30220     
30221     fieldLabel : '',
30222     labelAlign : 'top',
30223     labelWidth : 3,
30224     dayAllowBlank : false,
30225     monthAllowBlank : false,
30226     yearAllowBlank : false,
30227     dayPlaceholder : '',
30228     monthPlaceholder : '',
30229     yearPlaceholder : '',
30230     dayFormat : 'd',
30231     monthFormat : 'm',
30232     yearFormat : 'Y',
30233     isFormField : true,
30234     labellg : 0,
30235     labelmd : 0,
30236     labelsm : 0,
30237     labelxs : 0,
30238     
30239     getAutoCreate : function()
30240     {
30241         var cfg = {
30242             tag : 'div',
30243             cls : 'row roo-date-split-field-group',
30244             cn : [
30245                 {
30246                     tag : 'input',
30247                     type : 'hidden',
30248                     cls : 'form-hidden-field roo-date-split-field-group-value',
30249                     name : this.name
30250                 }
30251             ]
30252         };
30253         
30254         var labelCls = 'col-md-12';
30255         var contentCls = 'col-md-4';
30256         
30257         if(this.fieldLabel){
30258             
30259             var label = {
30260                 tag : 'div',
30261                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30262                 cn : [
30263                     {
30264                         tag : 'label',
30265                         html : this.fieldLabel
30266                     }
30267                 ]
30268             };
30269             
30270             if(this.labelAlign == 'left'){
30271             
30272                 if(this.labelWidth > 12){
30273                     label.style = "width: " + this.labelWidth + 'px';
30274                 }
30275
30276                 if(this.labelWidth < 13 && this.labelmd == 0){
30277                     this.labelmd = this.labelWidth;
30278                 }
30279
30280                 if(this.labellg > 0){
30281                     labelCls = ' col-lg-' + this.labellg;
30282                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30283                 }
30284
30285                 if(this.labelmd > 0){
30286                     labelCls = ' col-md-' + this.labelmd;
30287                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30288                 }
30289
30290                 if(this.labelsm > 0){
30291                     labelCls = ' col-sm-' + this.labelsm;
30292                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30293                 }
30294
30295                 if(this.labelxs > 0){
30296                     labelCls = ' col-xs-' + this.labelxs;
30297                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30298                 }
30299             }
30300             
30301             label.cls += ' ' + labelCls;
30302             
30303             cfg.cn.push(label);
30304         }
30305         
30306         Roo.each(['day', 'month', 'year'], function(t){
30307             cfg.cn.push({
30308                 tag : 'div',
30309                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30310             });
30311         }, this);
30312         
30313         return cfg;
30314     },
30315     
30316     inputEl: function ()
30317     {
30318         return this.el.select('.roo-date-split-field-group-value', true).first();
30319     },
30320     
30321     onRender : function(ct, position) 
30322     {
30323         var _this = this;
30324         
30325         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30326         
30327         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30328         
30329         this.dayField = new Roo.bootstrap.ComboBox({
30330             allowBlank : this.dayAllowBlank,
30331             alwaysQuery : true,
30332             displayField : 'value',
30333             editable : false,
30334             fieldLabel : '',
30335             forceSelection : true,
30336             mode : 'local',
30337             placeholder : this.dayPlaceholder,
30338             selectOnFocus : true,
30339             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30340             triggerAction : 'all',
30341             typeAhead : true,
30342             valueField : 'value',
30343             store : new Roo.data.SimpleStore({
30344                 data : (function() {    
30345                     var days = [];
30346                     _this.fireEvent('days', _this, days);
30347                     return days;
30348                 })(),
30349                 fields : [ 'value' ]
30350             }),
30351             listeners : {
30352                 select : function (_self, record, index)
30353                 {
30354                     _this.setValue(_this.getValue());
30355                 }
30356             }
30357         });
30358
30359         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30360         
30361         this.monthField = new Roo.bootstrap.MonthField({
30362             after : '<i class=\"fa fa-calendar\"></i>',
30363             allowBlank : this.monthAllowBlank,
30364             placeholder : this.monthPlaceholder,
30365             readOnly : true,
30366             listeners : {
30367                 render : function (_self)
30368                 {
30369                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30370                         e.preventDefault();
30371                         _self.focus();
30372                     });
30373                 },
30374                 select : function (_self, oldvalue, newvalue)
30375                 {
30376                     _this.setValue(_this.getValue());
30377                 }
30378             }
30379         });
30380         
30381         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30382         
30383         this.yearField = new Roo.bootstrap.ComboBox({
30384             allowBlank : this.yearAllowBlank,
30385             alwaysQuery : true,
30386             displayField : 'value',
30387             editable : false,
30388             fieldLabel : '',
30389             forceSelection : true,
30390             mode : 'local',
30391             placeholder : this.yearPlaceholder,
30392             selectOnFocus : true,
30393             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30394             triggerAction : 'all',
30395             typeAhead : true,
30396             valueField : 'value',
30397             store : new Roo.data.SimpleStore({
30398                 data : (function() {
30399                     var years = [];
30400                     _this.fireEvent('years', _this, years);
30401                     return years;
30402                 })(),
30403                 fields : [ 'value' ]
30404             }),
30405             listeners : {
30406                 select : function (_self, record, index)
30407                 {
30408                     _this.setValue(_this.getValue());
30409                 }
30410             }
30411         });
30412
30413         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30414     },
30415     
30416     setValue : function(v, format)
30417     {
30418         this.inputEl.dom.value = v;
30419         
30420         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30421         
30422         var d = Date.parseDate(v, f);
30423         
30424         if(!d){
30425             this.validate();
30426             return;
30427         }
30428         
30429         this.setDay(d.format(this.dayFormat));
30430         this.setMonth(d.format(this.monthFormat));
30431         this.setYear(d.format(this.yearFormat));
30432         
30433         this.validate();
30434         
30435         return;
30436     },
30437     
30438     setDay : function(v)
30439     {
30440         this.dayField.setValue(v);
30441         this.inputEl.dom.value = this.getValue();
30442         this.validate();
30443         return;
30444     },
30445     
30446     setMonth : function(v)
30447     {
30448         this.monthField.setValue(v, true);
30449         this.inputEl.dom.value = this.getValue();
30450         this.validate();
30451         return;
30452     },
30453     
30454     setYear : function(v)
30455     {
30456         this.yearField.setValue(v);
30457         this.inputEl.dom.value = this.getValue();
30458         this.validate();
30459         return;
30460     },
30461     
30462     getDay : function()
30463     {
30464         return this.dayField.getValue();
30465     },
30466     
30467     getMonth : function()
30468     {
30469         return this.monthField.getValue();
30470     },
30471     
30472     getYear : function()
30473     {
30474         return this.yearField.getValue();
30475     },
30476     
30477     getValue : function()
30478     {
30479         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30480         
30481         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30482         
30483         return date;
30484     },
30485     
30486     reset : function()
30487     {
30488         this.setDay('');
30489         this.setMonth('');
30490         this.setYear('');
30491         this.inputEl.dom.value = '';
30492         this.validate();
30493         return;
30494     },
30495     
30496     validate : function()
30497     {
30498         var d = this.dayField.validate();
30499         var m = this.monthField.validate();
30500         var y = this.yearField.validate();
30501         
30502         var valid = true;
30503         
30504         if(
30505                 (!this.dayAllowBlank && !d) ||
30506                 (!this.monthAllowBlank && !m) ||
30507                 (!this.yearAllowBlank && !y)
30508         ){
30509             valid = false;
30510         }
30511         
30512         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30513             return valid;
30514         }
30515         
30516         if(valid){
30517             this.markValid();
30518             return valid;
30519         }
30520         
30521         this.markInvalid();
30522         
30523         return valid;
30524     },
30525     
30526     markValid : function()
30527     {
30528         
30529         var label = this.el.select('label', true).first();
30530         var icon = this.el.select('i.fa-star', true).first();
30531
30532         if(label && icon){
30533             icon.remove();
30534         }
30535         
30536         this.fireEvent('valid', this);
30537     },
30538     
30539      /**
30540      * Mark this field as invalid
30541      * @param {String} msg The validation message
30542      */
30543     markInvalid : function(msg)
30544     {
30545         
30546         var label = this.el.select('label', true).first();
30547         var icon = this.el.select('i.fa-star', true).first();
30548
30549         if(label && !icon){
30550             this.el.select('.roo-date-split-field-label', true).createChild({
30551                 tag : 'i',
30552                 cls : 'text-danger fa fa-lg fa-star',
30553                 tooltip : 'This field is required',
30554                 style : 'margin-right:5px;'
30555             }, label, true);
30556         }
30557         
30558         this.fireEvent('invalid', this, msg);
30559     },
30560     
30561     clearInvalid : function()
30562     {
30563         var label = this.el.select('label', true).first();
30564         var icon = this.el.select('i.fa-star', true).first();
30565
30566         if(label && icon){
30567             icon.remove();
30568         }
30569         
30570         this.fireEvent('valid', this);
30571     },
30572     
30573     getName: function()
30574     {
30575         return this.name;
30576     }
30577     
30578 });
30579
30580  /**
30581  *
30582  * This is based on 
30583  * http://masonry.desandro.com
30584  *
30585  * The idea is to render all the bricks based on vertical width...
30586  *
30587  * The original code extends 'outlayer' - we might need to use that....
30588  * 
30589  */
30590
30591
30592 /**
30593  * @class Roo.bootstrap.LayoutMasonry
30594  * @extends Roo.bootstrap.Component
30595  * Bootstrap Layout Masonry class
30596  * 
30597  * @constructor
30598  * Create a new Element
30599  * @param {Object} config The config object
30600  */
30601
30602 Roo.bootstrap.LayoutMasonry = function(config){
30603     
30604     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30605     
30606     this.bricks = [];
30607     
30608     Roo.bootstrap.LayoutMasonry.register(this);
30609     
30610     this.addEvents({
30611         // raw events
30612         /**
30613          * @event layout
30614          * Fire after layout the items
30615          * @param {Roo.bootstrap.LayoutMasonry} this
30616          * @param {Roo.EventObject} e
30617          */
30618         "layout" : true
30619     });
30620     
30621 };
30622
30623 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30624     
30625     /**
30626      * @cfg {Boolean} isLayoutInstant = no animation?
30627      */   
30628     isLayoutInstant : false, // needed?
30629    
30630     /**
30631      * @cfg {Number} boxWidth  width of the columns
30632      */   
30633     boxWidth : 450,
30634     
30635       /**
30636      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30637      */   
30638     boxHeight : 0,
30639     
30640     /**
30641      * @cfg {Number} padWidth padding below box..
30642      */   
30643     padWidth : 10, 
30644     
30645     /**
30646      * @cfg {Number} gutter gutter width..
30647      */   
30648     gutter : 10,
30649     
30650      /**
30651      * @cfg {Number} maxCols maximum number of columns
30652      */   
30653     
30654     maxCols: 0,
30655     
30656     /**
30657      * @cfg {Boolean} isAutoInitial defalut true
30658      */   
30659     isAutoInitial : true, 
30660     
30661     containerWidth: 0,
30662     
30663     /**
30664      * @cfg {Boolean} isHorizontal defalut false
30665      */   
30666     isHorizontal : false, 
30667
30668     currentSize : null,
30669     
30670     tag: 'div',
30671     
30672     cls: '',
30673     
30674     bricks: null, //CompositeElement
30675     
30676     cols : 1,
30677     
30678     _isLayoutInited : false,
30679     
30680 //    isAlternative : false, // only use for vertical layout...
30681     
30682     /**
30683      * @cfg {Number} alternativePadWidth padding below box..
30684      */   
30685     alternativePadWidth : 50,
30686     
30687     selectedBrick : [],
30688     
30689     getAutoCreate : function(){
30690         
30691         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30692         
30693         var cfg = {
30694             tag: this.tag,
30695             cls: 'blog-masonary-wrapper ' + this.cls,
30696             cn : {
30697                 cls : 'mas-boxes masonary'
30698             }
30699         };
30700         
30701         return cfg;
30702     },
30703     
30704     getChildContainer: function( )
30705     {
30706         if (this.boxesEl) {
30707             return this.boxesEl;
30708         }
30709         
30710         this.boxesEl = this.el.select('.mas-boxes').first();
30711         
30712         return this.boxesEl;
30713     },
30714     
30715     
30716     initEvents : function()
30717     {
30718         var _this = this;
30719         
30720         if(this.isAutoInitial){
30721             Roo.log('hook children rendered');
30722             this.on('childrenrendered', function() {
30723                 Roo.log('children rendered');
30724                 _this.initial();
30725             } ,this);
30726         }
30727     },
30728     
30729     initial : function()
30730     {
30731         this.selectedBrick = [];
30732         
30733         this.currentSize = this.el.getBox(true);
30734         
30735         Roo.EventManager.onWindowResize(this.resize, this); 
30736
30737         if(!this.isAutoInitial){
30738             this.layout();
30739             return;
30740         }
30741         
30742         this.layout();
30743         
30744         return;
30745         //this.layout.defer(500,this);
30746         
30747     },
30748     
30749     resize : function()
30750     {
30751         var cs = this.el.getBox(true);
30752         
30753         if (
30754                 this.currentSize.width == cs.width && 
30755                 this.currentSize.x == cs.x && 
30756                 this.currentSize.height == cs.height && 
30757                 this.currentSize.y == cs.y 
30758         ) {
30759             Roo.log("no change in with or X or Y");
30760             return;
30761         }
30762         
30763         this.currentSize = cs;
30764         
30765         this.layout();
30766         
30767     },
30768     
30769     layout : function()
30770     {   
30771         this._resetLayout();
30772         
30773         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30774         
30775         this.layoutItems( isInstant );
30776       
30777         this._isLayoutInited = true;
30778         
30779         this.fireEvent('layout', this);
30780         
30781     },
30782     
30783     _resetLayout : function()
30784     {
30785         if(this.isHorizontal){
30786             this.horizontalMeasureColumns();
30787             return;
30788         }
30789         
30790         this.verticalMeasureColumns();
30791         
30792     },
30793     
30794     verticalMeasureColumns : function()
30795     {
30796         this.getContainerWidth();
30797         
30798 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30799 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30800 //            return;
30801 //        }
30802         
30803         var boxWidth = this.boxWidth + this.padWidth;
30804         
30805         if(this.containerWidth < this.boxWidth){
30806             boxWidth = this.containerWidth
30807         }
30808         
30809         var containerWidth = this.containerWidth;
30810         
30811         var cols = Math.floor(containerWidth / boxWidth);
30812         
30813         this.cols = Math.max( cols, 1 );
30814         
30815         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30816         
30817         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30818         
30819         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30820         
30821         this.colWidth = boxWidth + avail - this.padWidth;
30822         
30823         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30824         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30825     },
30826     
30827     horizontalMeasureColumns : function()
30828     {
30829         this.getContainerWidth();
30830         
30831         var boxWidth = this.boxWidth;
30832         
30833         if(this.containerWidth < boxWidth){
30834             boxWidth = this.containerWidth;
30835         }
30836         
30837         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30838         
30839         this.el.setHeight(boxWidth);
30840         
30841     },
30842     
30843     getContainerWidth : function()
30844     {
30845         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30846     },
30847     
30848     layoutItems : function( isInstant )
30849     {
30850         Roo.log(this.bricks);
30851         
30852         var items = Roo.apply([], this.bricks);
30853         
30854         if(this.isHorizontal){
30855             this._horizontalLayoutItems( items , isInstant );
30856             return;
30857         }
30858         
30859 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30860 //            this._verticalAlternativeLayoutItems( items , isInstant );
30861 //            return;
30862 //        }
30863         
30864         this._verticalLayoutItems( items , isInstant );
30865         
30866     },
30867     
30868     _verticalLayoutItems : function ( items , isInstant)
30869     {
30870         if ( !items || !items.length ) {
30871             return;
30872         }
30873         
30874         var standard = [
30875             ['xs', 'xs', 'xs', 'tall'],
30876             ['xs', 'xs', 'tall'],
30877             ['xs', 'xs', 'sm'],
30878             ['xs', 'xs', 'xs'],
30879             ['xs', 'tall'],
30880             ['xs', 'sm'],
30881             ['xs', 'xs'],
30882             ['xs'],
30883             
30884             ['sm', 'xs', 'xs'],
30885             ['sm', 'xs'],
30886             ['sm'],
30887             
30888             ['tall', 'xs', 'xs', 'xs'],
30889             ['tall', 'xs', 'xs'],
30890             ['tall', 'xs'],
30891             ['tall']
30892             
30893         ];
30894         
30895         var queue = [];
30896         
30897         var boxes = [];
30898         
30899         var box = [];
30900         
30901         Roo.each(items, function(item, k){
30902             
30903             switch (item.size) {
30904                 // these layouts take up a full box,
30905                 case 'md' :
30906                 case 'md-left' :
30907                 case 'md-right' :
30908                 case 'wide' :
30909                     
30910                     if(box.length){
30911                         boxes.push(box);
30912                         box = [];
30913                     }
30914                     
30915                     boxes.push([item]);
30916                     
30917                     break;
30918                     
30919                 case 'xs' :
30920                 case 'sm' :
30921                 case 'tall' :
30922                     
30923                     box.push(item);
30924                     
30925                     break;
30926                 default :
30927                     break;
30928                     
30929             }
30930             
30931         }, this);
30932         
30933         if(box.length){
30934             boxes.push(box);
30935             box = [];
30936         }
30937         
30938         var filterPattern = function(box, length)
30939         {
30940             if(!box.length){
30941                 return;
30942             }
30943             
30944             var match = false;
30945             
30946             var pattern = box.slice(0, length);
30947             
30948             var format = [];
30949             
30950             Roo.each(pattern, function(i){
30951                 format.push(i.size);
30952             }, this);
30953             
30954             Roo.each(standard, function(s){
30955                 
30956                 if(String(s) != String(format)){
30957                     return;
30958                 }
30959                 
30960                 match = true;
30961                 return false;
30962                 
30963             }, this);
30964             
30965             if(!match && length == 1){
30966                 return;
30967             }
30968             
30969             if(!match){
30970                 filterPattern(box, length - 1);
30971                 return;
30972             }
30973                 
30974             queue.push(pattern);
30975
30976             box = box.slice(length, box.length);
30977
30978             filterPattern(box, 4);
30979
30980             return;
30981             
30982         }
30983         
30984         Roo.each(boxes, function(box, k){
30985             
30986             if(!box.length){
30987                 return;
30988             }
30989             
30990             if(box.length == 1){
30991                 queue.push(box);
30992                 return;
30993             }
30994             
30995             filterPattern(box, 4);
30996             
30997         }, this);
30998         
30999         this._processVerticalLayoutQueue( queue, isInstant );
31000         
31001     },
31002     
31003 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31004 //    {
31005 //        if ( !items || !items.length ) {
31006 //            return;
31007 //        }
31008 //
31009 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31010 //        
31011 //    },
31012     
31013     _horizontalLayoutItems : function ( items , isInstant)
31014     {
31015         if ( !items || !items.length || items.length < 3) {
31016             return;
31017         }
31018         
31019         items.reverse();
31020         
31021         var eItems = items.slice(0, 3);
31022         
31023         items = items.slice(3, items.length);
31024         
31025         var standard = [
31026             ['xs', 'xs', 'xs', 'wide'],
31027             ['xs', 'xs', 'wide'],
31028             ['xs', 'xs', 'sm'],
31029             ['xs', 'xs', 'xs'],
31030             ['xs', 'wide'],
31031             ['xs', 'sm'],
31032             ['xs', 'xs'],
31033             ['xs'],
31034             
31035             ['sm', 'xs', 'xs'],
31036             ['sm', 'xs'],
31037             ['sm'],
31038             
31039             ['wide', 'xs', 'xs', 'xs'],
31040             ['wide', 'xs', 'xs'],
31041             ['wide', 'xs'],
31042             ['wide'],
31043             
31044             ['wide-thin']
31045         ];
31046         
31047         var queue = [];
31048         
31049         var boxes = [];
31050         
31051         var box = [];
31052         
31053         Roo.each(items, function(item, k){
31054             
31055             switch (item.size) {
31056                 case 'md' :
31057                 case 'md-left' :
31058                 case 'md-right' :
31059                 case 'tall' :
31060                     
31061                     if(box.length){
31062                         boxes.push(box);
31063                         box = [];
31064                     }
31065                     
31066                     boxes.push([item]);
31067                     
31068                     break;
31069                     
31070                 case 'xs' :
31071                 case 'sm' :
31072                 case 'wide' :
31073                 case 'wide-thin' :
31074                     
31075                     box.push(item);
31076                     
31077                     break;
31078                 default :
31079                     break;
31080                     
31081             }
31082             
31083         }, this);
31084         
31085         if(box.length){
31086             boxes.push(box);
31087             box = [];
31088         }
31089         
31090         var filterPattern = function(box, length)
31091         {
31092             if(!box.length){
31093                 return;
31094             }
31095             
31096             var match = false;
31097             
31098             var pattern = box.slice(0, length);
31099             
31100             var format = [];
31101             
31102             Roo.each(pattern, function(i){
31103                 format.push(i.size);
31104             }, this);
31105             
31106             Roo.each(standard, function(s){
31107                 
31108                 if(String(s) != String(format)){
31109                     return;
31110                 }
31111                 
31112                 match = true;
31113                 return false;
31114                 
31115             }, this);
31116             
31117             if(!match && length == 1){
31118                 return;
31119             }
31120             
31121             if(!match){
31122                 filterPattern(box, length - 1);
31123                 return;
31124             }
31125                 
31126             queue.push(pattern);
31127
31128             box = box.slice(length, box.length);
31129
31130             filterPattern(box, 4);
31131
31132             return;
31133             
31134         }
31135         
31136         Roo.each(boxes, function(box, k){
31137             
31138             if(!box.length){
31139                 return;
31140             }
31141             
31142             if(box.length == 1){
31143                 queue.push(box);
31144                 return;
31145             }
31146             
31147             filterPattern(box, 4);
31148             
31149         }, this);
31150         
31151         
31152         var prune = [];
31153         
31154         var pos = this.el.getBox(true);
31155         
31156         var minX = pos.x;
31157         
31158         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31159         
31160         var hit_end = false;
31161         
31162         Roo.each(queue, function(box){
31163             
31164             if(hit_end){
31165                 
31166                 Roo.each(box, function(b){
31167                 
31168                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31169                     b.el.hide();
31170
31171                 }, this);
31172
31173                 return;
31174             }
31175             
31176             var mx = 0;
31177             
31178             Roo.each(box, function(b){
31179                 
31180                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31181                 b.el.show();
31182
31183                 mx = Math.max(mx, b.x);
31184                 
31185             }, this);
31186             
31187             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31188             
31189             if(maxX < minX){
31190                 
31191                 Roo.each(box, function(b){
31192                 
31193                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31194                     b.el.hide();
31195                     
31196                 }, this);
31197                 
31198                 hit_end = true;
31199                 
31200                 return;
31201             }
31202             
31203             prune.push(box);
31204             
31205         }, this);
31206         
31207         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31208     },
31209     
31210     /** Sets position of item in DOM
31211     * @param {Element} item
31212     * @param {Number} x - horizontal position
31213     * @param {Number} y - vertical position
31214     * @param {Boolean} isInstant - disables transitions
31215     */
31216     _processVerticalLayoutQueue : function( queue, isInstant )
31217     {
31218         var pos = this.el.getBox(true);
31219         var x = pos.x;
31220         var y = pos.y;
31221         var maxY = [];
31222         
31223         for (var i = 0; i < this.cols; i++){
31224             maxY[i] = pos.y;
31225         }
31226         
31227         Roo.each(queue, function(box, k){
31228             
31229             var col = k % this.cols;
31230             
31231             Roo.each(box, function(b,kk){
31232                 
31233                 b.el.position('absolute');
31234                 
31235                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31236                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31237                 
31238                 if(b.size == 'md-left' || b.size == 'md-right'){
31239                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31240                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31241                 }
31242                 
31243                 b.el.setWidth(width);
31244                 b.el.setHeight(height);
31245                 // iframe?
31246                 b.el.select('iframe',true).setSize(width,height);
31247                 
31248             }, this);
31249             
31250             for (var i = 0; i < this.cols; i++){
31251                 
31252                 if(maxY[i] < maxY[col]){
31253                     col = i;
31254                     continue;
31255                 }
31256                 
31257                 col = Math.min(col, i);
31258                 
31259             }
31260             
31261             x = pos.x + col * (this.colWidth + this.padWidth);
31262             
31263             y = maxY[col];
31264             
31265             var positions = [];
31266             
31267             switch (box.length){
31268                 case 1 :
31269                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31270                     break;
31271                 case 2 :
31272                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31273                     break;
31274                 case 3 :
31275                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31276                     break;
31277                 case 4 :
31278                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31279                     break;
31280                 default :
31281                     break;
31282             }
31283             
31284             Roo.each(box, function(b,kk){
31285                 
31286                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31287                 
31288                 var sz = b.el.getSize();
31289                 
31290                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31291                 
31292             }, this);
31293             
31294         }, this);
31295         
31296         var mY = 0;
31297         
31298         for (var i = 0; i < this.cols; i++){
31299             mY = Math.max(mY, maxY[i]);
31300         }
31301         
31302         this.el.setHeight(mY - pos.y);
31303         
31304     },
31305     
31306 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31307 //    {
31308 //        var pos = this.el.getBox(true);
31309 //        var x = pos.x;
31310 //        var y = pos.y;
31311 //        var maxX = pos.right;
31312 //        
31313 //        var maxHeight = 0;
31314 //        
31315 //        Roo.each(items, function(item, k){
31316 //            
31317 //            var c = k % 2;
31318 //            
31319 //            item.el.position('absolute');
31320 //                
31321 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31322 //
31323 //            item.el.setWidth(width);
31324 //
31325 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31326 //
31327 //            item.el.setHeight(height);
31328 //            
31329 //            if(c == 0){
31330 //                item.el.setXY([x, y], isInstant ? false : true);
31331 //            } else {
31332 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31333 //            }
31334 //            
31335 //            y = y + height + this.alternativePadWidth;
31336 //            
31337 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31338 //            
31339 //        }, this);
31340 //        
31341 //        this.el.setHeight(maxHeight);
31342 //        
31343 //    },
31344     
31345     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31346     {
31347         var pos = this.el.getBox(true);
31348         
31349         var minX = pos.x;
31350         var minY = pos.y;
31351         
31352         var maxX = pos.right;
31353         
31354         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31355         
31356         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31357         
31358         Roo.each(queue, function(box, k){
31359             
31360             Roo.each(box, function(b, kk){
31361                 
31362                 b.el.position('absolute');
31363                 
31364                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31365                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31366                 
31367                 if(b.size == 'md-left' || b.size == 'md-right'){
31368                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31369                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31370                 }
31371                 
31372                 b.el.setWidth(width);
31373                 b.el.setHeight(height);
31374                 
31375             }, this);
31376             
31377             if(!box.length){
31378                 return;
31379             }
31380             
31381             var positions = [];
31382             
31383             switch (box.length){
31384                 case 1 :
31385                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31386                     break;
31387                 case 2 :
31388                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31389                     break;
31390                 case 3 :
31391                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31392                     break;
31393                 case 4 :
31394                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31395                     break;
31396                 default :
31397                     break;
31398             }
31399             
31400             Roo.each(box, function(b,kk){
31401                 
31402                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31403                 
31404                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31405                 
31406             }, this);
31407             
31408         }, this);
31409         
31410     },
31411     
31412     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31413     {
31414         Roo.each(eItems, function(b,k){
31415             
31416             b.size = (k == 0) ? 'sm' : 'xs';
31417             b.x = (k == 0) ? 2 : 1;
31418             b.y = (k == 0) ? 2 : 1;
31419             
31420             b.el.position('absolute');
31421             
31422             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31423                 
31424             b.el.setWidth(width);
31425             
31426             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31427             
31428             b.el.setHeight(height);
31429             
31430         }, this);
31431
31432         var positions = [];
31433         
31434         positions.push({
31435             x : maxX - this.unitWidth * 2 - this.gutter,
31436             y : minY
31437         });
31438         
31439         positions.push({
31440             x : maxX - this.unitWidth,
31441             y : minY + (this.unitWidth + this.gutter) * 2
31442         });
31443         
31444         positions.push({
31445             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31446             y : minY
31447         });
31448         
31449         Roo.each(eItems, function(b,k){
31450             
31451             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31452
31453         }, this);
31454         
31455     },
31456     
31457     getVerticalOneBoxColPositions : function(x, y, box)
31458     {
31459         var pos = [];
31460         
31461         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31462         
31463         if(box[0].size == 'md-left'){
31464             rand = 0;
31465         }
31466         
31467         if(box[0].size == 'md-right'){
31468             rand = 1;
31469         }
31470         
31471         pos.push({
31472             x : x + (this.unitWidth + this.gutter) * rand,
31473             y : y
31474         });
31475         
31476         return pos;
31477     },
31478     
31479     getVerticalTwoBoxColPositions : function(x, y, box)
31480     {
31481         var pos = [];
31482         
31483         if(box[0].size == 'xs'){
31484             
31485             pos.push({
31486                 x : x,
31487                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31488             });
31489
31490             pos.push({
31491                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31492                 y : y
31493             });
31494             
31495             return pos;
31496             
31497         }
31498         
31499         pos.push({
31500             x : x,
31501             y : y
31502         });
31503
31504         pos.push({
31505             x : x + (this.unitWidth + this.gutter) * 2,
31506             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31507         });
31508         
31509         return pos;
31510         
31511     },
31512     
31513     getVerticalThreeBoxColPositions : function(x, y, box)
31514     {
31515         var pos = [];
31516         
31517         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31518             
31519             pos.push({
31520                 x : x,
31521                 y : y
31522             });
31523
31524             pos.push({
31525                 x : x + (this.unitWidth + this.gutter) * 1,
31526                 y : y
31527             });
31528             
31529             pos.push({
31530                 x : x + (this.unitWidth + this.gutter) * 2,
31531                 y : y
31532             });
31533             
31534             return pos;
31535             
31536         }
31537         
31538         if(box[0].size == 'xs' && box[1].size == 'xs'){
31539             
31540             pos.push({
31541                 x : x,
31542                 y : y
31543             });
31544
31545             pos.push({
31546                 x : x,
31547                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31548             });
31549             
31550             pos.push({
31551                 x : x + (this.unitWidth + this.gutter) * 1,
31552                 y : y
31553             });
31554             
31555             return pos;
31556             
31557         }
31558         
31559         pos.push({
31560             x : x,
31561             y : y
31562         });
31563
31564         pos.push({
31565             x : x + (this.unitWidth + this.gutter) * 2,
31566             y : y
31567         });
31568
31569         pos.push({
31570             x : x + (this.unitWidth + this.gutter) * 2,
31571             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31572         });
31573             
31574         return pos;
31575         
31576     },
31577     
31578     getVerticalFourBoxColPositions : function(x, y, box)
31579     {
31580         var pos = [];
31581         
31582         if(box[0].size == 'xs'){
31583             
31584             pos.push({
31585                 x : x,
31586                 y : y
31587             });
31588
31589             pos.push({
31590                 x : x,
31591                 y : y + (this.unitHeight + this.gutter) * 1
31592             });
31593             
31594             pos.push({
31595                 x : x,
31596                 y : y + (this.unitHeight + this.gutter) * 2
31597             });
31598             
31599             pos.push({
31600                 x : x + (this.unitWidth + this.gutter) * 1,
31601                 y : y
31602             });
31603             
31604             return pos;
31605             
31606         }
31607         
31608         pos.push({
31609             x : x,
31610             y : y
31611         });
31612
31613         pos.push({
31614             x : x + (this.unitWidth + this.gutter) * 2,
31615             y : y
31616         });
31617
31618         pos.push({
31619             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31620             y : y + (this.unitHeight + this.gutter) * 1
31621         });
31622
31623         pos.push({
31624             x : x + (this.unitWidth + this.gutter) * 2,
31625             y : y + (this.unitWidth + this.gutter) * 2
31626         });
31627
31628         return pos;
31629         
31630     },
31631     
31632     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31633     {
31634         var pos = [];
31635         
31636         if(box[0].size == 'md-left'){
31637             pos.push({
31638                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31639                 y : minY
31640             });
31641             
31642             return pos;
31643         }
31644         
31645         if(box[0].size == 'md-right'){
31646             pos.push({
31647                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31648                 y : minY + (this.unitWidth + this.gutter) * 1
31649             });
31650             
31651             return pos;
31652         }
31653         
31654         var rand = Math.floor(Math.random() * (4 - box[0].y));
31655         
31656         pos.push({
31657             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31658             y : minY + (this.unitWidth + this.gutter) * rand
31659         });
31660         
31661         return pos;
31662         
31663     },
31664     
31665     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31666     {
31667         var pos = [];
31668         
31669         if(box[0].size == 'xs'){
31670             
31671             pos.push({
31672                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31673                 y : minY
31674             });
31675
31676             pos.push({
31677                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31678                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31679             });
31680             
31681             return pos;
31682             
31683         }
31684         
31685         pos.push({
31686             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31687             y : minY
31688         });
31689
31690         pos.push({
31691             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31692             y : minY + (this.unitWidth + this.gutter) * 2
31693         });
31694         
31695         return pos;
31696         
31697     },
31698     
31699     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31700     {
31701         var pos = [];
31702         
31703         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31704             
31705             pos.push({
31706                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31707                 y : minY
31708             });
31709
31710             pos.push({
31711                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31712                 y : minY + (this.unitWidth + this.gutter) * 1
31713             });
31714             
31715             pos.push({
31716                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31717                 y : minY + (this.unitWidth + this.gutter) * 2
31718             });
31719             
31720             return pos;
31721             
31722         }
31723         
31724         if(box[0].size == 'xs' && box[1].size == 'xs'){
31725             
31726             pos.push({
31727                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31728                 y : minY
31729             });
31730
31731             pos.push({
31732                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31733                 y : minY
31734             });
31735             
31736             pos.push({
31737                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31738                 y : minY + (this.unitWidth + this.gutter) * 1
31739             });
31740             
31741             return pos;
31742             
31743         }
31744         
31745         pos.push({
31746             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31747             y : minY
31748         });
31749
31750         pos.push({
31751             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31752             y : minY + (this.unitWidth + this.gutter) * 2
31753         });
31754
31755         pos.push({
31756             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31757             y : minY + (this.unitWidth + this.gutter) * 2
31758         });
31759             
31760         return pos;
31761         
31762     },
31763     
31764     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31765     {
31766         var pos = [];
31767         
31768         if(box[0].size == 'xs'){
31769             
31770             pos.push({
31771                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31772                 y : minY
31773             });
31774
31775             pos.push({
31776                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31777                 y : minY
31778             });
31779             
31780             pos.push({
31781                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31782                 y : minY
31783             });
31784             
31785             pos.push({
31786                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31787                 y : minY + (this.unitWidth + this.gutter) * 1
31788             });
31789             
31790             return pos;
31791             
31792         }
31793         
31794         pos.push({
31795             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31796             y : minY
31797         });
31798         
31799         pos.push({
31800             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31801             y : minY + (this.unitWidth + this.gutter) * 2
31802         });
31803         
31804         pos.push({
31805             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31806             y : minY + (this.unitWidth + this.gutter) * 2
31807         });
31808         
31809         pos.push({
31810             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),
31811             y : minY + (this.unitWidth + this.gutter) * 2
31812         });
31813
31814         return pos;
31815         
31816     },
31817     
31818     /**
31819     * remove a Masonry Brick
31820     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31821     */
31822     removeBrick : function(brick_id)
31823     {
31824         if (!brick_id) {
31825             return;
31826         }
31827         
31828         for (var i = 0; i<this.bricks.length; i++) {
31829             if (this.bricks[i].id == brick_id) {
31830                 this.bricks.splice(i,1);
31831                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31832                 this.initial();
31833             }
31834         }
31835     },
31836     
31837     /**
31838     * adds a Masonry Brick
31839     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31840     */
31841     addBrick : function(cfg)
31842     {
31843         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31844         //this.register(cn);
31845         cn.parentId = this.id;
31846         cn.onRender(this.el, null);
31847         return cn;
31848     },
31849     
31850     /**
31851     * register a Masonry Brick
31852     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31853     */
31854     
31855     register : function(brick)
31856     {
31857         this.bricks.push(brick);
31858         brick.masonryId = this.id;
31859     },
31860     
31861     /**
31862     * clear all the Masonry Brick
31863     */
31864     clearAll : function()
31865     {
31866         this.bricks = [];
31867         //this.getChildContainer().dom.innerHTML = "";
31868         this.el.dom.innerHTML = '';
31869     },
31870     
31871     getSelected : function()
31872     {
31873         if (!this.selectedBrick) {
31874             return false;
31875         }
31876         
31877         return this.selectedBrick;
31878     }
31879 });
31880
31881 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31882     
31883     groups: {},
31884      /**
31885     * register a Masonry Layout
31886     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31887     */
31888     
31889     register : function(layout)
31890     {
31891         this.groups[layout.id] = layout;
31892     },
31893     /**
31894     * fetch a  Masonry Layout based on the masonry layout ID
31895     * @param {string} the masonry layout to add
31896     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31897     */
31898     
31899     get: function(layout_id) {
31900         if (typeof(this.groups[layout_id]) == 'undefined') {
31901             return false;
31902         }
31903         return this.groups[layout_id] ;
31904     }
31905     
31906     
31907     
31908 });
31909
31910  
31911
31912  /**
31913  *
31914  * This is based on 
31915  * http://masonry.desandro.com
31916  *
31917  * The idea is to render all the bricks based on vertical width...
31918  *
31919  * The original code extends 'outlayer' - we might need to use that....
31920  * 
31921  */
31922
31923
31924 /**
31925  * @class Roo.bootstrap.LayoutMasonryAuto
31926  * @extends Roo.bootstrap.Component
31927  * Bootstrap Layout Masonry class
31928  * 
31929  * @constructor
31930  * Create a new Element
31931  * @param {Object} config The config object
31932  */
31933
31934 Roo.bootstrap.LayoutMasonryAuto = function(config){
31935     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31936 };
31937
31938 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31939     
31940       /**
31941      * @cfg {Boolean} isFitWidth  - resize the width..
31942      */   
31943     isFitWidth : false,  // options..
31944     /**
31945      * @cfg {Boolean} isOriginLeft = left align?
31946      */   
31947     isOriginLeft : true,
31948     /**
31949      * @cfg {Boolean} isOriginTop = top align?
31950      */   
31951     isOriginTop : false,
31952     /**
31953      * @cfg {Boolean} isLayoutInstant = no animation?
31954      */   
31955     isLayoutInstant : false, // needed?
31956     /**
31957      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31958      */   
31959     isResizingContainer : true,
31960     /**
31961      * @cfg {Number} columnWidth  width of the columns 
31962      */   
31963     
31964     columnWidth : 0,
31965     
31966     /**
31967      * @cfg {Number} maxCols maximum number of columns
31968      */   
31969     
31970     maxCols: 0,
31971     /**
31972      * @cfg {Number} padHeight padding below box..
31973      */   
31974     
31975     padHeight : 10, 
31976     
31977     /**
31978      * @cfg {Boolean} isAutoInitial defalut true
31979      */   
31980     
31981     isAutoInitial : true, 
31982     
31983     // private?
31984     gutter : 0,
31985     
31986     containerWidth: 0,
31987     initialColumnWidth : 0,
31988     currentSize : null,
31989     
31990     colYs : null, // array.
31991     maxY : 0,
31992     padWidth: 10,
31993     
31994     
31995     tag: 'div',
31996     cls: '',
31997     bricks: null, //CompositeElement
31998     cols : 0, // array?
31999     // element : null, // wrapped now this.el
32000     _isLayoutInited : null, 
32001     
32002     
32003     getAutoCreate : function(){
32004         
32005         var cfg = {
32006             tag: this.tag,
32007             cls: 'blog-masonary-wrapper ' + this.cls,
32008             cn : {
32009                 cls : 'mas-boxes masonary'
32010             }
32011         };
32012         
32013         return cfg;
32014     },
32015     
32016     getChildContainer: function( )
32017     {
32018         if (this.boxesEl) {
32019             return this.boxesEl;
32020         }
32021         
32022         this.boxesEl = this.el.select('.mas-boxes').first();
32023         
32024         return this.boxesEl;
32025     },
32026     
32027     
32028     initEvents : function()
32029     {
32030         var _this = this;
32031         
32032         if(this.isAutoInitial){
32033             Roo.log('hook children rendered');
32034             this.on('childrenrendered', function() {
32035                 Roo.log('children rendered');
32036                 _this.initial();
32037             } ,this);
32038         }
32039         
32040     },
32041     
32042     initial : function()
32043     {
32044         this.reloadItems();
32045
32046         this.currentSize = this.el.getBox(true);
32047
32048         /// was window resize... - let's see if this works..
32049         Roo.EventManager.onWindowResize(this.resize, this); 
32050
32051         if(!this.isAutoInitial){
32052             this.layout();
32053             return;
32054         }
32055         
32056         this.layout.defer(500,this);
32057     },
32058     
32059     reloadItems: function()
32060     {
32061         this.bricks = this.el.select('.masonry-brick', true);
32062         
32063         this.bricks.each(function(b) {
32064             //Roo.log(b.getSize());
32065             if (!b.attr('originalwidth')) {
32066                 b.attr('originalwidth',  b.getSize().width);
32067             }
32068             
32069         });
32070         
32071         Roo.log(this.bricks.elements.length);
32072     },
32073     
32074     resize : function()
32075     {
32076         Roo.log('resize');
32077         var cs = this.el.getBox(true);
32078         
32079         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32080             Roo.log("no change in with or X");
32081             return;
32082         }
32083         this.currentSize = cs;
32084         this.layout();
32085     },
32086     
32087     layout : function()
32088     {
32089          Roo.log('layout');
32090         this._resetLayout();
32091         //this._manageStamps();
32092       
32093         // don't animate first layout
32094         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32095         this.layoutItems( isInstant );
32096       
32097         // flag for initalized
32098         this._isLayoutInited = true;
32099     },
32100     
32101     layoutItems : function( isInstant )
32102     {
32103         //var items = this._getItemsForLayout( this.items );
32104         // original code supports filtering layout items.. we just ignore it..
32105         
32106         this._layoutItems( this.bricks , isInstant );
32107       
32108         this._postLayout();
32109     },
32110     _layoutItems : function ( items , isInstant)
32111     {
32112        //this.fireEvent( 'layout', this, items );
32113     
32114
32115         if ( !items || !items.elements.length ) {
32116           // no items, emit event with empty array
32117             return;
32118         }
32119
32120         var queue = [];
32121         items.each(function(item) {
32122             Roo.log("layout item");
32123             Roo.log(item);
32124             // get x/y object from method
32125             var position = this._getItemLayoutPosition( item );
32126             // enqueue
32127             position.item = item;
32128             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32129             queue.push( position );
32130         }, this);
32131       
32132         this._processLayoutQueue( queue );
32133     },
32134     /** Sets position of item in DOM
32135     * @param {Element} item
32136     * @param {Number} x - horizontal position
32137     * @param {Number} y - vertical position
32138     * @param {Boolean} isInstant - disables transitions
32139     */
32140     _processLayoutQueue : function( queue )
32141     {
32142         for ( var i=0, len = queue.length; i < len; i++ ) {
32143             var obj = queue[i];
32144             obj.item.position('absolute');
32145             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32146         }
32147     },
32148       
32149     
32150     /**
32151     * Any logic you want to do after each layout,
32152     * i.e. size the container
32153     */
32154     _postLayout : function()
32155     {
32156         this.resizeContainer();
32157     },
32158     
32159     resizeContainer : function()
32160     {
32161         if ( !this.isResizingContainer ) {
32162             return;
32163         }
32164         var size = this._getContainerSize();
32165         if ( size ) {
32166             this.el.setSize(size.width,size.height);
32167             this.boxesEl.setSize(size.width,size.height);
32168         }
32169     },
32170     
32171     
32172     
32173     _resetLayout : function()
32174     {
32175         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32176         this.colWidth = this.el.getWidth();
32177         //this.gutter = this.el.getWidth(); 
32178         
32179         this.measureColumns();
32180
32181         // reset column Y
32182         var i = this.cols;
32183         this.colYs = [];
32184         while (i--) {
32185             this.colYs.push( 0 );
32186         }
32187     
32188         this.maxY = 0;
32189     },
32190
32191     measureColumns : function()
32192     {
32193         this.getContainerWidth();
32194       // if columnWidth is 0, default to outerWidth of first item
32195         if ( !this.columnWidth ) {
32196             var firstItem = this.bricks.first();
32197             Roo.log(firstItem);
32198             this.columnWidth  = this.containerWidth;
32199             if (firstItem && firstItem.attr('originalwidth') ) {
32200                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32201             }
32202             // columnWidth fall back to item of first element
32203             Roo.log("set column width?");
32204                         this.initialColumnWidth = this.columnWidth  ;
32205
32206             // if first elem has no width, default to size of container
32207             
32208         }
32209         
32210         
32211         if (this.initialColumnWidth) {
32212             this.columnWidth = this.initialColumnWidth;
32213         }
32214         
32215         
32216             
32217         // column width is fixed at the top - however if container width get's smaller we should
32218         // reduce it...
32219         
32220         // this bit calcs how man columns..
32221             
32222         var columnWidth = this.columnWidth += this.gutter;
32223       
32224         // calculate columns
32225         var containerWidth = this.containerWidth + this.gutter;
32226         
32227         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32228         // fix rounding errors, typically with gutters
32229         var excess = columnWidth - containerWidth % columnWidth;
32230         
32231         
32232         // if overshoot is less than a pixel, round up, otherwise floor it
32233         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32234         cols = Math[ mathMethod ]( cols );
32235         this.cols = Math.max( cols, 1 );
32236         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32237         
32238          // padding positioning..
32239         var totalColWidth = this.cols * this.columnWidth;
32240         var padavail = this.containerWidth - totalColWidth;
32241         // so for 2 columns - we need 3 'pads'
32242         
32243         var padNeeded = (1+this.cols) * this.padWidth;
32244         
32245         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32246         
32247         this.columnWidth += padExtra
32248         //this.padWidth = Math.floor(padavail /  ( this.cols));
32249         
32250         // adjust colum width so that padding is fixed??
32251         
32252         // we have 3 columns ... total = width * 3
32253         // we have X left over... that should be used by 
32254         
32255         //if (this.expandC) {
32256             
32257         //}
32258         
32259         
32260         
32261     },
32262     
32263     getContainerWidth : function()
32264     {
32265        /* // container is parent if fit width
32266         var container = this.isFitWidth ? this.element.parentNode : this.element;
32267         // check that this.size and size are there
32268         // IE8 triggers resize on body size change, so they might not be
32269         
32270         var size = getSize( container );  //FIXME
32271         this.containerWidth = size && size.innerWidth; //FIXME
32272         */
32273          
32274         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32275         
32276     },
32277     
32278     _getItemLayoutPosition : function( item )  // what is item?
32279     {
32280         // we resize the item to our columnWidth..
32281       
32282         item.setWidth(this.columnWidth);
32283         item.autoBoxAdjust  = false;
32284         
32285         var sz = item.getSize();
32286  
32287         // how many columns does this brick span
32288         var remainder = this.containerWidth % this.columnWidth;
32289         
32290         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32291         // round if off by 1 pixel, otherwise use ceil
32292         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32293         colSpan = Math.min( colSpan, this.cols );
32294         
32295         // normally this should be '1' as we dont' currently allow multi width columns..
32296         
32297         var colGroup = this._getColGroup( colSpan );
32298         // get the minimum Y value from the columns
32299         var minimumY = Math.min.apply( Math, colGroup );
32300         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32301         
32302         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32303          
32304         // position the brick
32305         var position = {
32306             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32307             y: this.currentSize.y + minimumY + this.padHeight
32308         };
32309         
32310         Roo.log(position);
32311         // apply setHeight to necessary columns
32312         var setHeight = minimumY + sz.height + this.padHeight;
32313         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32314         
32315         var setSpan = this.cols + 1 - colGroup.length;
32316         for ( var i = 0; i < setSpan; i++ ) {
32317           this.colYs[ shortColIndex + i ] = setHeight ;
32318         }
32319       
32320         return position;
32321     },
32322     
32323     /**
32324      * @param {Number} colSpan - number of columns the element spans
32325      * @returns {Array} colGroup
32326      */
32327     _getColGroup : function( colSpan )
32328     {
32329         if ( colSpan < 2 ) {
32330           // if brick spans only one column, use all the column Ys
32331           return this.colYs;
32332         }
32333       
32334         var colGroup = [];
32335         // how many different places could this brick fit horizontally
32336         var groupCount = this.cols + 1 - colSpan;
32337         // for each group potential horizontal position
32338         for ( var i = 0; i < groupCount; i++ ) {
32339           // make an array of colY values for that one group
32340           var groupColYs = this.colYs.slice( i, i + colSpan );
32341           // and get the max value of the array
32342           colGroup[i] = Math.max.apply( Math, groupColYs );
32343         }
32344         return colGroup;
32345     },
32346     /*
32347     _manageStamp : function( stamp )
32348     {
32349         var stampSize =  stamp.getSize();
32350         var offset = stamp.getBox();
32351         // get the columns that this stamp affects
32352         var firstX = this.isOriginLeft ? offset.x : offset.right;
32353         var lastX = firstX + stampSize.width;
32354         var firstCol = Math.floor( firstX / this.columnWidth );
32355         firstCol = Math.max( 0, firstCol );
32356         
32357         var lastCol = Math.floor( lastX / this.columnWidth );
32358         // lastCol should not go over if multiple of columnWidth #425
32359         lastCol -= lastX % this.columnWidth ? 0 : 1;
32360         lastCol = Math.min( this.cols - 1, lastCol );
32361         
32362         // set colYs to bottom of the stamp
32363         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32364             stampSize.height;
32365             
32366         for ( var i = firstCol; i <= lastCol; i++ ) {
32367           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32368         }
32369     },
32370     */
32371     
32372     _getContainerSize : function()
32373     {
32374         this.maxY = Math.max.apply( Math, this.colYs );
32375         var size = {
32376             height: this.maxY
32377         };
32378       
32379         if ( this.isFitWidth ) {
32380             size.width = this._getContainerFitWidth();
32381         }
32382       
32383         return size;
32384     },
32385     
32386     _getContainerFitWidth : function()
32387     {
32388         var unusedCols = 0;
32389         // count unused columns
32390         var i = this.cols;
32391         while ( --i ) {
32392           if ( this.colYs[i] !== 0 ) {
32393             break;
32394           }
32395           unusedCols++;
32396         }
32397         // fit container to columns that have been used
32398         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32399     },
32400     
32401     needsResizeLayout : function()
32402     {
32403         var previousWidth = this.containerWidth;
32404         this.getContainerWidth();
32405         return previousWidth !== this.containerWidth;
32406     }
32407  
32408 });
32409
32410  
32411
32412  /*
32413  * - LGPL
32414  *
32415  * element
32416  * 
32417  */
32418
32419 /**
32420  * @class Roo.bootstrap.MasonryBrick
32421  * @extends Roo.bootstrap.Component
32422  * Bootstrap MasonryBrick class
32423  * 
32424  * @constructor
32425  * Create a new MasonryBrick
32426  * @param {Object} config The config object
32427  */
32428
32429 Roo.bootstrap.MasonryBrick = function(config){
32430     
32431     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32432     
32433     Roo.bootstrap.MasonryBrick.register(this);
32434     
32435     this.addEvents({
32436         // raw events
32437         /**
32438          * @event click
32439          * When a MasonryBrick is clcik
32440          * @param {Roo.bootstrap.MasonryBrick} this
32441          * @param {Roo.EventObject} e
32442          */
32443         "click" : true
32444     });
32445 };
32446
32447 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32448     
32449     /**
32450      * @cfg {String} title
32451      */   
32452     title : '',
32453     /**
32454      * @cfg {String} html
32455      */   
32456     html : '',
32457     /**
32458      * @cfg {String} bgimage
32459      */   
32460     bgimage : '',
32461     /**
32462      * @cfg {String} videourl
32463      */   
32464     videourl : '',
32465     /**
32466      * @cfg {String} cls
32467      */   
32468     cls : '',
32469     /**
32470      * @cfg {String} href
32471      */   
32472     href : '',
32473     /**
32474      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32475      */   
32476     size : 'xs',
32477     
32478     /**
32479      * @cfg {String} placetitle (center|bottom)
32480      */   
32481     placetitle : '',
32482     
32483     /**
32484      * @cfg {Boolean} isFitContainer defalut true
32485      */   
32486     isFitContainer : true, 
32487     
32488     /**
32489      * @cfg {Boolean} preventDefault defalut false
32490      */   
32491     preventDefault : false, 
32492     
32493     /**
32494      * @cfg {Boolean} inverse defalut false
32495      */   
32496     maskInverse : false, 
32497     
32498     getAutoCreate : function()
32499     {
32500         if(!this.isFitContainer){
32501             return this.getSplitAutoCreate();
32502         }
32503         
32504         var cls = 'masonry-brick masonry-brick-full';
32505         
32506         if(this.href.length){
32507             cls += ' masonry-brick-link';
32508         }
32509         
32510         if(this.bgimage.length){
32511             cls += ' masonry-brick-image';
32512         }
32513         
32514         if(this.maskInverse){
32515             cls += ' mask-inverse';
32516         }
32517         
32518         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32519             cls += ' enable-mask';
32520         }
32521         
32522         if(this.size){
32523             cls += ' masonry-' + this.size + '-brick';
32524         }
32525         
32526         if(this.placetitle.length){
32527             
32528             switch (this.placetitle) {
32529                 case 'center' :
32530                     cls += ' masonry-center-title';
32531                     break;
32532                 case 'bottom' :
32533                     cls += ' masonry-bottom-title';
32534                     break;
32535                 default:
32536                     break;
32537             }
32538             
32539         } else {
32540             if(!this.html.length && !this.bgimage.length){
32541                 cls += ' masonry-center-title';
32542             }
32543
32544             if(!this.html.length && this.bgimage.length){
32545                 cls += ' masonry-bottom-title';
32546             }
32547         }
32548         
32549         if(this.cls){
32550             cls += ' ' + this.cls;
32551         }
32552         
32553         var cfg = {
32554             tag: (this.href.length) ? 'a' : 'div',
32555             cls: cls,
32556             cn: [
32557                 {
32558                     tag: 'div',
32559                     cls: 'masonry-brick-mask'
32560                 },
32561                 {
32562                     tag: 'div',
32563                     cls: 'masonry-brick-paragraph',
32564                     cn: []
32565                 }
32566             ]
32567         };
32568         
32569         if(this.href.length){
32570             cfg.href = this.href;
32571         }
32572         
32573         var cn = cfg.cn[1].cn;
32574         
32575         if(this.title.length){
32576             cn.push({
32577                 tag: 'h4',
32578                 cls: 'masonry-brick-title',
32579                 html: this.title
32580             });
32581         }
32582         
32583         if(this.html.length){
32584             cn.push({
32585                 tag: 'p',
32586                 cls: 'masonry-brick-text',
32587                 html: this.html
32588             });
32589         }
32590         
32591         if (!this.title.length && !this.html.length) {
32592             cfg.cn[1].cls += ' hide';
32593         }
32594         
32595         if(this.bgimage.length){
32596             cfg.cn.push({
32597                 tag: 'img',
32598                 cls: 'masonry-brick-image-view',
32599                 src: this.bgimage
32600             });
32601         }
32602         
32603         if(this.videourl.length){
32604             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32605             // youtube support only?
32606             cfg.cn.push({
32607                 tag: 'iframe',
32608                 cls: 'masonry-brick-image-view',
32609                 src: vurl,
32610                 frameborder : 0,
32611                 allowfullscreen : true
32612             });
32613         }
32614         
32615         return cfg;
32616         
32617     },
32618     
32619     getSplitAutoCreate : function()
32620     {
32621         var cls = 'masonry-brick masonry-brick-split';
32622         
32623         if(this.href.length){
32624             cls += ' masonry-brick-link';
32625         }
32626         
32627         if(this.bgimage.length){
32628             cls += ' masonry-brick-image';
32629         }
32630         
32631         if(this.size){
32632             cls += ' masonry-' + this.size + '-brick';
32633         }
32634         
32635         switch (this.placetitle) {
32636             case 'center' :
32637                 cls += ' masonry-center-title';
32638                 break;
32639             case 'bottom' :
32640                 cls += ' masonry-bottom-title';
32641                 break;
32642             default:
32643                 if(!this.bgimage.length){
32644                     cls += ' masonry-center-title';
32645                 }
32646
32647                 if(this.bgimage.length){
32648                     cls += ' masonry-bottom-title';
32649                 }
32650                 break;
32651         }
32652         
32653         if(this.cls){
32654             cls += ' ' + this.cls;
32655         }
32656         
32657         var cfg = {
32658             tag: (this.href.length) ? 'a' : 'div',
32659             cls: cls,
32660             cn: [
32661                 {
32662                     tag: 'div',
32663                     cls: 'masonry-brick-split-head',
32664                     cn: [
32665                         {
32666                             tag: 'div',
32667                             cls: 'masonry-brick-paragraph',
32668                             cn: []
32669                         }
32670                     ]
32671                 },
32672                 {
32673                     tag: 'div',
32674                     cls: 'masonry-brick-split-body',
32675                     cn: []
32676                 }
32677             ]
32678         };
32679         
32680         if(this.href.length){
32681             cfg.href = this.href;
32682         }
32683         
32684         if(this.title.length){
32685             cfg.cn[0].cn[0].cn.push({
32686                 tag: 'h4',
32687                 cls: 'masonry-brick-title',
32688                 html: this.title
32689             });
32690         }
32691         
32692         if(this.html.length){
32693             cfg.cn[1].cn.push({
32694                 tag: 'p',
32695                 cls: 'masonry-brick-text',
32696                 html: this.html
32697             });
32698         }
32699
32700         if(this.bgimage.length){
32701             cfg.cn[0].cn.push({
32702                 tag: 'img',
32703                 cls: 'masonry-brick-image-view',
32704                 src: this.bgimage
32705             });
32706         }
32707         
32708         if(this.videourl.length){
32709             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32710             // youtube support only?
32711             cfg.cn[0].cn.cn.push({
32712                 tag: 'iframe',
32713                 cls: 'masonry-brick-image-view',
32714                 src: vurl,
32715                 frameborder : 0,
32716                 allowfullscreen : true
32717             });
32718         }
32719         
32720         return cfg;
32721     },
32722     
32723     initEvents: function() 
32724     {
32725         switch (this.size) {
32726             case 'xs' :
32727                 this.x = 1;
32728                 this.y = 1;
32729                 break;
32730             case 'sm' :
32731                 this.x = 2;
32732                 this.y = 2;
32733                 break;
32734             case 'md' :
32735             case 'md-left' :
32736             case 'md-right' :
32737                 this.x = 3;
32738                 this.y = 3;
32739                 break;
32740             case 'tall' :
32741                 this.x = 2;
32742                 this.y = 3;
32743                 break;
32744             case 'wide' :
32745                 this.x = 3;
32746                 this.y = 2;
32747                 break;
32748             case 'wide-thin' :
32749                 this.x = 3;
32750                 this.y = 1;
32751                 break;
32752                         
32753             default :
32754                 break;
32755         }
32756         
32757         if(Roo.isTouch){
32758             this.el.on('touchstart', this.onTouchStart, this);
32759             this.el.on('touchmove', this.onTouchMove, this);
32760             this.el.on('touchend', this.onTouchEnd, this);
32761             this.el.on('contextmenu', this.onContextMenu, this);
32762         } else {
32763             this.el.on('mouseenter'  ,this.enter, this);
32764             this.el.on('mouseleave', this.leave, this);
32765             this.el.on('click', this.onClick, this);
32766         }
32767         
32768         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32769             this.parent().bricks.push(this);   
32770         }
32771         
32772     },
32773     
32774     onClick: function(e, el)
32775     {
32776         var time = this.endTimer - this.startTimer;
32777         // Roo.log(e.preventDefault());
32778         if(Roo.isTouch){
32779             if(time > 1000){
32780                 e.preventDefault();
32781                 return;
32782             }
32783         }
32784         
32785         if(!this.preventDefault){
32786             return;
32787         }
32788         
32789         e.preventDefault();
32790         
32791         if (this.activeClass != '') {
32792             this.selectBrick();
32793         }
32794         
32795         this.fireEvent('click', this, e);
32796     },
32797     
32798     enter: function(e, el)
32799     {
32800         e.preventDefault();
32801         
32802         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32803             return;
32804         }
32805         
32806         if(this.bgimage.length && this.html.length){
32807             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32808         }
32809     },
32810     
32811     leave: function(e, el)
32812     {
32813         e.preventDefault();
32814         
32815         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32816             return;
32817         }
32818         
32819         if(this.bgimage.length && this.html.length){
32820             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32821         }
32822     },
32823     
32824     onTouchStart: function(e, el)
32825     {
32826 //        e.preventDefault();
32827         
32828         this.touchmoved = false;
32829         
32830         if(!this.isFitContainer){
32831             return;
32832         }
32833         
32834         if(!this.bgimage.length || !this.html.length){
32835             return;
32836         }
32837         
32838         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32839         
32840         this.timer = new Date().getTime();
32841         
32842     },
32843     
32844     onTouchMove: function(e, el)
32845     {
32846         this.touchmoved = true;
32847     },
32848     
32849     onContextMenu : function(e,el)
32850     {
32851         e.preventDefault();
32852         e.stopPropagation();
32853         return false;
32854     },
32855     
32856     onTouchEnd: function(e, el)
32857     {
32858 //        e.preventDefault();
32859         
32860         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32861         
32862             this.leave(e,el);
32863             
32864             return;
32865         }
32866         
32867         if(!this.bgimage.length || !this.html.length){
32868             
32869             if(this.href.length){
32870                 window.location.href = this.href;
32871             }
32872             
32873             return;
32874         }
32875         
32876         if(!this.isFitContainer){
32877             return;
32878         }
32879         
32880         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32881         
32882         window.location.href = this.href;
32883     },
32884     
32885     //selection on single brick only
32886     selectBrick : function() {
32887         
32888         if (!this.parentId) {
32889             return;
32890         }
32891         
32892         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32893         var index = m.selectedBrick.indexOf(this.id);
32894         
32895         if ( index > -1) {
32896             m.selectedBrick.splice(index,1);
32897             this.el.removeClass(this.activeClass);
32898             return;
32899         }
32900         
32901         for(var i = 0; i < m.selectedBrick.length; i++) {
32902             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32903             b.el.removeClass(b.activeClass);
32904         }
32905         
32906         m.selectedBrick = [];
32907         
32908         m.selectedBrick.push(this.id);
32909         this.el.addClass(this.activeClass);
32910         return;
32911     },
32912     
32913     isSelected : function(){
32914         return this.el.hasClass(this.activeClass);
32915         
32916     }
32917 });
32918
32919 Roo.apply(Roo.bootstrap.MasonryBrick, {
32920     
32921     //groups: {},
32922     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32923      /**
32924     * register a Masonry Brick
32925     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32926     */
32927     
32928     register : function(brick)
32929     {
32930         //this.groups[brick.id] = brick;
32931         this.groups.add(brick.id, brick);
32932     },
32933     /**
32934     * fetch a  masonry brick based on the masonry brick ID
32935     * @param {string} the masonry brick to add
32936     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32937     */
32938     
32939     get: function(brick_id) 
32940     {
32941         // if (typeof(this.groups[brick_id]) == 'undefined') {
32942         //     return false;
32943         // }
32944         // return this.groups[brick_id] ;
32945         
32946         if(this.groups.key(brick_id)) {
32947             return this.groups.key(brick_id);
32948         }
32949         
32950         return false;
32951     }
32952     
32953     
32954     
32955 });
32956
32957  /*
32958  * - LGPL
32959  *
32960  * element
32961  * 
32962  */
32963
32964 /**
32965  * @class Roo.bootstrap.Brick
32966  * @extends Roo.bootstrap.Component
32967  * Bootstrap Brick class
32968  * 
32969  * @constructor
32970  * Create a new Brick
32971  * @param {Object} config The config object
32972  */
32973
32974 Roo.bootstrap.Brick = function(config){
32975     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32976     
32977     this.addEvents({
32978         // raw events
32979         /**
32980          * @event click
32981          * When a Brick is click
32982          * @param {Roo.bootstrap.Brick} this
32983          * @param {Roo.EventObject} e
32984          */
32985         "click" : true
32986     });
32987 };
32988
32989 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32990     
32991     /**
32992      * @cfg {String} title
32993      */   
32994     title : '',
32995     /**
32996      * @cfg {String} html
32997      */   
32998     html : '',
32999     /**
33000      * @cfg {String} bgimage
33001      */   
33002     bgimage : '',
33003     /**
33004      * @cfg {String} cls
33005      */   
33006     cls : '',
33007     /**
33008      * @cfg {String} href
33009      */   
33010     href : '',
33011     /**
33012      * @cfg {String} video
33013      */   
33014     video : '',
33015     /**
33016      * @cfg {Boolean} square
33017      */   
33018     square : true,
33019     
33020     getAutoCreate : function()
33021     {
33022         var cls = 'roo-brick';
33023         
33024         if(this.href.length){
33025             cls += ' roo-brick-link';
33026         }
33027         
33028         if(this.bgimage.length){
33029             cls += ' roo-brick-image';
33030         }
33031         
33032         if(!this.html.length && !this.bgimage.length){
33033             cls += ' roo-brick-center-title';
33034         }
33035         
33036         if(!this.html.length && this.bgimage.length){
33037             cls += ' roo-brick-bottom-title';
33038         }
33039         
33040         if(this.cls){
33041             cls += ' ' + this.cls;
33042         }
33043         
33044         var cfg = {
33045             tag: (this.href.length) ? 'a' : 'div',
33046             cls: cls,
33047             cn: [
33048                 {
33049                     tag: 'div',
33050                     cls: 'roo-brick-paragraph',
33051                     cn: []
33052                 }
33053             ]
33054         };
33055         
33056         if(this.href.length){
33057             cfg.href = this.href;
33058         }
33059         
33060         var cn = cfg.cn[0].cn;
33061         
33062         if(this.title.length){
33063             cn.push({
33064                 tag: 'h4',
33065                 cls: 'roo-brick-title',
33066                 html: this.title
33067             });
33068         }
33069         
33070         if(this.html.length){
33071             cn.push({
33072                 tag: 'p',
33073                 cls: 'roo-brick-text',
33074                 html: this.html
33075             });
33076         } else {
33077             cn.cls += ' hide';
33078         }
33079         
33080         if(this.bgimage.length){
33081             cfg.cn.push({
33082                 tag: 'img',
33083                 cls: 'roo-brick-image-view',
33084                 src: this.bgimage
33085             });
33086         }
33087         
33088         return cfg;
33089     },
33090     
33091     initEvents: function() 
33092     {
33093         if(this.title.length || this.html.length){
33094             this.el.on('mouseenter'  ,this.enter, this);
33095             this.el.on('mouseleave', this.leave, this);
33096         }
33097         
33098         Roo.EventManager.onWindowResize(this.resize, this); 
33099         
33100         if(this.bgimage.length){
33101             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33102             this.imageEl.on('load', this.onImageLoad, this);
33103             return;
33104         }
33105         
33106         this.resize();
33107     },
33108     
33109     onImageLoad : function()
33110     {
33111         this.resize();
33112     },
33113     
33114     resize : function()
33115     {
33116         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33117         
33118         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33119         
33120         if(this.bgimage.length){
33121             var image = this.el.select('.roo-brick-image-view', true).first();
33122             
33123             image.setWidth(paragraph.getWidth());
33124             
33125             if(this.square){
33126                 image.setHeight(paragraph.getWidth());
33127             }
33128             
33129             this.el.setHeight(image.getHeight());
33130             paragraph.setHeight(image.getHeight());
33131             
33132         }
33133         
33134     },
33135     
33136     enter: function(e, el)
33137     {
33138         e.preventDefault();
33139         
33140         if(this.bgimage.length){
33141             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33142             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33143         }
33144     },
33145     
33146     leave: function(e, el)
33147     {
33148         e.preventDefault();
33149         
33150         if(this.bgimage.length){
33151             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33152             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33153         }
33154     }
33155     
33156 });
33157
33158  
33159
33160  /*
33161  * - LGPL
33162  *
33163  * Number field 
33164  */
33165
33166 /**
33167  * @class Roo.bootstrap.NumberField
33168  * @extends Roo.bootstrap.Input
33169  * Bootstrap NumberField class
33170  * 
33171  * 
33172  * 
33173  * 
33174  * @constructor
33175  * Create a new NumberField
33176  * @param {Object} config The config object
33177  */
33178
33179 Roo.bootstrap.NumberField = function(config){
33180     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33181 };
33182
33183 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33184     
33185     /**
33186      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33187      */
33188     allowDecimals : true,
33189     /**
33190      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33191      */
33192     decimalSeparator : ".",
33193     /**
33194      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33195      */
33196     decimalPrecision : 2,
33197     /**
33198      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33199      */
33200     allowNegative : true,
33201     
33202     /**
33203      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33204      */
33205     allowZero: true,
33206     /**
33207      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33208      */
33209     minValue : Number.NEGATIVE_INFINITY,
33210     /**
33211      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33212      */
33213     maxValue : Number.MAX_VALUE,
33214     /**
33215      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33216      */
33217     minText : "The minimum value for this field is {0}",
33218     /**
33219      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33220      */
33221     maxText : "The maximum value for this field is {0}",
33222     /**
33223      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33224      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33225      */
33226     nanText : "{0} is not a valid number",
33227     /**
33228      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33229      */
33230     castInt : true,
33231     /**
33232      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33233      */
33234     thousandsDelimiter : false,
33235     /**
33236      * @cfg {String} valueAlign alignment of value
33237      */
33238     valueAlign : "left",
33239
33240     getAutoCreate : function()
33241     {
33242         var hiddenInput = {
33243             tag: 'input',
33244             type: 'hidden',
33245             id: Roo.id(),
33246             cls: 'hidden-number-input'
33247         };
33248         
33249         if (this.name) {
33250             hiddenInput.name = this.name;
33251         }
33252         
33253         this.name = '';
33254         
33255         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33256         
33257         this.name = hiddenInput.name;
33258         
33259         if(cfg.cn.length > 0) {
33260             cfg.cn.push(hiddenInput);
33261         }
33262         
33263         return cfg;
33264     },
33265
33266     // private
33267     initEvents : function()
33268     {   
33269         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33270         
33271         var allowed = "0123456789";
33272         
33273         if(this.allowDecimals){
33274             allowed += this.decimalSeparator;
33275         }
33276         
33277         if(this.allowNegative){
33278             allowed += "-";
33279         }
33280         
33281         if(this.thousandsDelimiter) {
33282             allowed += ",";
33283         }
33284         
33285         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33286         
33287         var keyPress = function(e){
33288             
33289             var k = e.getKey();
33290             
33291             var c = e.getCharCode();
33292             
33293             if(
33294                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33295                     allowed.indexOf(String.fromCharCode(c)) === -1
33296             ){
33297                 e.stopEvent();
33298                 return;
33299             }
33300             
33301             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33302                 return;
33303             }
33304             
33305             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33306                 e.stopEvent();
33307             }
33308         };
33309         
33310         this.el.on("keypress", keyPress, this);
33311     },
33312     
33313     validateValue : function(value)
33314     {
33315         
33316         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33317             return false;
33318         }
33319         
33320         var num = this.parseValue(value);
33321         
33322         if(isNaN(num)){
33323             this.markInvalid(String.format(this.nanText, value));
33324             return false;
33325         }
33326         
33327         if(num < this.minValue){
33328             this.markInvalid(String.format(this.minText, this.minValue));
33329             return false;
33330         }
33331         
33332         if(num > this.maxValue){
33333             this.markInvalid(String.format(this.maxText, this.maxValue));
33334             return false;
33335         }
33336         
33337         return true;
33338     },
33339
33340     getValue : function()
33341     {
33342         var v = this.hiddenEl().getValue();
33343         
33344         return this.fixPrecision(this.parseValue(v));
33345     },
33346
33347     parseValue : function(value)
33348     {
33349         if(this.thousandsDelimiter) {
33350             value += "";
33351             r = new RegExp(",", "g");
33352             value = value.replace(r, "");
33353         }
33354         
33355         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33356         return isNaN(value) ? '' : value;
33357     },
33358
33359     fixPrecision : function(value)
33360     {
33361         if(this.thousandsDelimiter) {
33362             value += "";
33363             r = new RegExp(",", "g");
33364             value = value.replace(r, "");
33365         }
33366         
33367         var nan = isNaN(value);
33368         
33369         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33370             return nan ? '' : value;
33371         }
33372         return parseFloat(value).toFixed(this.decimalPrecision);
33373     },
33374
33375     setValue : function(v)
33376     {
33377         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33378         
33379         this.value = v;
33380         
33381         if(this.rendered){
33382             
33383             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33384             
33385             this.inputEl().dom.value = (v == '') ? '' :
33386                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33387             
33388             if(!this.allowZero && v === '0') {
33389                 this.hiddenEl().dom.value = '';
33390                 this.inputEl().dom.value = '';
33391             }
33392             
33393             this.validate();
33394         }
33395     },
33396
33397     decimalPrecisionFcn : function(v)
33398     {
33399         return Math.floor(v);
33400     },
33401
33402     beforeBlur : function()
33403     {
33404         if(!this.castInt){
33405             return;
33406         }
33407         
33408         var v = this.parseValue(this.getRawValue());
33409         
33410         if(v || v === 0){
33411             this.setValue(v);
33412         }
33413     },
33414     
33415     hiddenEl : function()
33416     {
33417         return this.el.select('input.hidden-number-input',true).first();
33418     }
33419     
33420 });
33421
33422  
33423
33424 /*
33425 * Licence: LGPL
33426 */
33427
33428 /**
33429  * @class Roo.bootstrap.DocumentSlider
33430  * @extends Roo.bootstrap.Component
33431  * Bootstrap DocumentSlider class
33432  * 
33433  * @constructor
33434  * Create a new DocumentViewer
33435  * @param {Object} config The config object
33436  */
33437
33438 Roo.bootstrap.DocumentSlider = function(config){
33439     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33440     
33441     this.files = [];
33442     
33443     this.addEvents({
33444         /**
33445          * @event initial
33446          * Fire after initEvent
33447          * @param {Roo.bootstrap.DocumentSlider} this
33448          */
33449         "initial" : true,
33450         /**
33451          * @event update
33452          * Fire after update
33453          * @param {Roo.bootstrap.DocumentSlider} this
33454          */
33455         "update" : true,
33456         /**
33457          * @event click
33458          * Fire after click
33459          * @param {Roo.bootstrap.DocumentSlider} this
33460          */
33461         "click" : true
33462     });
33463 };
33464
33465 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33466     
33467     files : false,
33468     
33469     indicator : 0,
33470     
33471     getAutoCreate : function()
33472     {
33473         var cfg = {
33474             tag : 'div',
33475             cls : 'roo-document-slider',
33476             cn : [
33477                 {
33478                     tag : 'div',
33479                     cls : 'roo-document-slider-header',
33480                     cn : [
33481                         {
33482                             tag : 'div',
33483                             cls : 'roo-document-slider-header-title'
33484                         }
33485                     ]
33486                 },
33487                 {
33488                     tag : 'div',
33489                     cls : 'roo-document-slider-body',
33490                     cn : [
33491                         {
33492                             tag : 'div',
33493                             cls : 'roo-document-slider-prev',
33494                             cn : [
33495                                 {
33496                                     tag : 'i',
33497                                     cls : 'fa fa-chevron-left'
33498                                 }
33499                             ]
33500                         },
33501                         {
33502                             tag : 'div',
33503                             cls : 'roo-document-slider-thumb',
33504                             cn : [
33505                                 {
33506                                     tag : 'img',
33507                                     cls : 'roo-document-slider-image'
33508                                 }
33509                             ]
33510                         },
33511                         {
33512                             tag : 'div',
33513                             cls : 'roo-document-slider-next',
33514                             cn : [
33515                                 {
33516                                     tag : 'i',
33517                                     cls : 'fa fa-chevron-right'
33518                                 }
33519                             ]
33520                         }
33521                     ]
33522                 }
33523             ]
33524         };
33525         
33526         return cfg;
33527     },
33528     
33529     initEvents : function()
33530     {
33531         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33532         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33533         
33534         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33535         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33536         
33537         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33538         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33539         
33540         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33541         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33542         
33543         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33544         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33545         
33546         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33547         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33548         
33549         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33550         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33551         
33552         this.thumbEl.on('click', this.onClick, this);
33553         
33554         this.prevIndicator.on('click', this.prev, this);
33555         
33556         this.nextIndicator.on('click', this.next, this);
33557         
33558     },
33559     
33560     initial : function()
33561     {
33562         if(this.files.length){
33563             this.indicator = 1;
33564             this.update()
33565         }
33566         
33567         this.fireEvent('initial', this);
33568     },
33569     
33570     update : function()
33571     {
33572         this.imageEl.attr('src', this.files[this.indicator - 1]);
33573         
33574         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33575         
33576         this.prevIndicator.show();
33577         
33578         if(this.indicator == 1){
33579             this.prevIndicator.hide();
33580         }
33581         
33582         this.nextIndicator.show();
33583         
33584         if(this.indicator == this.files.length){
33585             this.nextIndicator.hide();
33586         }
33587         
33588         this.thumbEl.scrollTo('top');
33589         
33590         this.fireEvent('update', this);
33591     },
33592     
33593     onClick : function(e)
33594     {
33595         e.preventDefault();
33596         
33597         this.fireEvent('click', this);
33598     },
33599     
33600     prev : function(e)
33601     {
33602         e.preventDefault();
33603         
33604         this.indicator = Math.max(1, this.indicator - 1);
33605         
33606         this.update();
33607     },
33608     
33609     next : function(e)
33610     {
33611         e.preventDefault();
33612         
33613         this.indicator = Math.min(this.files.length, this.indicator + 1);
33614         
33615         this.update();
33616     }
33617 });
33618 /*
33619  * - LGPL
33620  *
33621  * RadioSet
33622  *
33623  *
33624  */
33625
33626 /**
33627  * @class Roo.bootstrap.RadioSet
33628  * @extends Roo.bootstrap.Input
33629  * Bootstrap RadioSet class
33630  * @cfg {String} indicatorpos (left|right) default left
33631  * @cfg {Boolean} inline (true|false) inline the element (default true)
33632  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33633  * @constructor
33634  * Create a new RadioSet
33635  * @param {Object} config The config object
33636  */
33637
33638 Roo.bootstrap.RadioSet = function(config){
33639     
33640     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33641     
33642     this.radioes = [];
33643     
33644     Roo.bootstrap.RadioSet.register(this);
33645     
33646     this.addEvents({
33647         /**
33648         * @event check
33649         * Fires when the element is checked or unchecked.
33650         * @param {Roo.bootstrap.RadioSet} this This radio
33651         * @param {Roo.bootstrap.Radio} item The checked item
33652         */
33653        check : true,
33654        /**
33655         * @event click
33656         * Fires when the element is click.
33657         * @param {Roo.bootstrap.RadioSet} this This radio set
33658         * @param {Roo.bootstrap.Radio} item The checked item
33659         * @param {Roo.EventObject} e The event object
33660         */
33661        click : true
33662     });
33663     
33664 };
33665
33666 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33667
33668     radioes : false,
33669     
33670     inline : true,
33671     
33672     weight : '',
33673     
33674     indicatorpos : 'left',
33675     
33676     getAutoCreate : function()
33677     {
33678         var label = {
33679             tag : 'label',
33680             cls : 'roo-radio-set-label',
33681             cn : [
33682                 {
33683                     tag : 'span',
33684                     html : this.fieldLabel
33685                 }
33686             ]
33687         };
33688         
33689         if(this.indicatorpos == 'left'){
33690             label.cn.unshift({
33691                 tag : 'i',
33692                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33693                 tooltip : 'This field is required'
33694             });
33695         } else {
33696             label.cn.push({
33697                 tag : 'i',
33698                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33699                 tooltip : 'This field is required'
33700             });
33701         }
33702         
33703         var items = {
33704             tag : 'div',
33705             cls : 'roo-radio-set-items'
33706         };
33707         
33708         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33709         
33710         if (align === 'left' && this.fieldLabel.length) {
33711             
33712             items = {
33713                 cls : "roo-radio-set-right", 
33714                 cn: [
33715                     items
33716                 ]
33717             };
33718             
33719             if(this.labelWidth > 12){
33720                 label.style = "width: " + this.labelWidth + 'px';
33721             }
33722             
33723             if(this.labelWidth < 13 && this.labelmd == 0){
33724                 this.labelmd = this.labelWidth;
33725             }
33726             
33727             if(this.labellg > 0){
33728                 label.cls += ' col-lg-' + this.labellg;
33729                 items.cls += ' col-lg-' + (12 - this.labellg);
33730             }
33731             
33732             if(this.labelmd > 0){
33733                 label.cls += ' col-md-' + this.labelmd;
33734                 items.cls += ' col-md-' + (12 - this.labelmd);
33735             }
33736             
33737             if(this.labelsm > 0){
33738                 label.cls += ' col-sm-' + this.labelsm;
33739                 items.cls += ' col-sm-' + (12 - this.labelsm);
33740             }
33741             
33742             if(this.labelxs > 0){
33743                 label.cls += ' col-xs-' + this.labelxs;
33744                 items.cls += ' col-xs-' + (12 - this.labelxs);
33745             }
33746         }
33747         
33748         var cfg = {
33749             tag : 'div',
33750             cls : 'roo-radio-set',
33751             cn : [
33752                 {
33753                     tag : 'input',
33754                     cls : 'roo-radio-set-input',
33755                     type : 'hidden',
33756                     name : this.name,
33757                     value : this.value ? this.value :  ''
33758                 },
33759                 label,
33760                 items
33761             ]
33762         };
33763         
33764         if(this.weight.length){
33765             cfg.cls += ' roo-radio-' + this.weight;
33766         }
33767         
33768         if(this.inline) {
33769             cfg.cls += ' roo-radio-set-inline';
33770         }
33771         
33772         var settings=this;
33773         ['xs','sm','md','lg'].map(function(size){
33774             if (settings[size]) {
33775                 cfg.cls += ' col-' + size + '-' + settings[size];
33776             }
33777         });
33778         
33779         return cfg;
33780         
33781     },
33782
33783     initEvents : function()
33784     {
33785         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33786         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33787         
33788         if(!this.fieldLabel.length){
33789             this.labelEl.hide();
33790         }
33791         
33792         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33793         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33794         
33795         this.indicator = this.indicatorEl();
33796         
33797         if(this.indicator){
33798             this.indicator.addClass('invisible');
33799         }
33800         
33801         this.originalValue = this.getValue();
33802         
33803     },
33804     
33805     inputEl: function ()
33806     {
33807         return this.el.select('.roo-radio-set-input', true).first();
33808     },
33809     
33810     getChildContainer : function()
33811     {
33812         return this.itemsEl;
33813     },
33814     
33815     register : function(item)
33816     {
33817         this.radioes.push(item);
33818         
33819     },
33820     
33821     validate : function()
33822     {   
33823         if(this.getVisibilityEl().hasClass('hidden')){
33824             return true;
33825         }
33826         
33827         var valid = false;
33828         
33829         Roo.each(this.radioes, function(i){
33830             if(!i.checked){
33831                 return;
33832             }
33833             
33834             valid = true;
33835             return false;
33836         });
33837         
33838         if(this.allowBlank) {
33839             return true;
33840         }
33841         
33842         if(this.disabled || valid){
33843             this.markValid();
33844             return true;
33845         }
33846         
33847         this.markInvalid();
33848         return false;
33849         
33850     },
33851     
33852     markValid : function()
33853     {
33854         if(this.labelEl.isVisible(true)){
33855             this.indicatorEl().removeClass('visible');
33856             this.indicatorEl().addClass('invisible');
33857         }
33858         
33859         this.el.removeClass([this.invalidClass, this.validClass]);
33860         this.el.addClass(this.validClass);
33861         
33862         this.fireEvent('valid', this);
33863     },
33864     
33865     markInvalid : function(msg)
33866     {
33867         if(this.allowBlank || this.disabled){
33868             return;
33869         }
33870         
33871         if(this.labelEl.isVisible(true)){
33872             this.indicatorEl().removeClass('invisible');
33873             this.indicatorEl().addClass('visible');
33874         }
33875         
33876         this.el.removeClass([this.invalidClass, this.validClass]);
33877         this.el.addClass(this.invalidClass);
33878         
33879         this.fireEvent('invalid', this, msg);
33880         
33881     },
33882     
33883     setValue : function(v, suppressEvent)
33884     {   
33885         if(this.value === v){
33886             return;
33887         }
33888         
33889         this.value = v;
33890         
33891         if(this.rendered){
33892             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33893         }
33894         
33895         Roo.each(this.radioes, function(i){
33896             i.checked = false;
33897             i.el.removeClass('checked');
33898         });
33899         
33900         Roo.each(this.radioes, function(i){
33901             
33902             if(i.value === v || i.value.toString() === v.toString()){
33903                 i.checked = true;
33904                 i.el.addClass('checked');
33905                 
33906                 if(suppressEvent !== true){
33907                     this.fireEvent('check', this, i);
33908                 }
33909                 
33910                 return false;
33911             }
33912             
33913         }, this);
33914         
33915         this.validate();
33916     },
33917     
33918     clearInvalid : function(){
33919         
33920         if(!this.el || this.preventMark){
33921             return;
33922         }
33923         
33924         this.el.removeClass([this.invalidClass]);
33925         
33926         this.fireEvent('valid', this);
33927     }
33928     
33929 });
33930
33931 Roo.apply(Roo.bootstrap.RadioSet, {
33932     
33933     groups: {},
33934     
33935     register : function(set)
33936     {
33937         this.groups[set.name] = set;
33938     },
33939     
33940     get: function(name) 
33941     {
33942         if (typeof(this.groups[name]) == 'undefined') {
33943             return false;
33944         }
33945         
33946         return this.groups[name] ;
33947     }
33948     
33949 });
33950 /*
33951  * Based on:
33952  * Ext JS Library 1.1.1
33953  * Copyright(c) 2006-2007, Ext JS, LLC.
33954  *
33955  * Originally Released Under LGPL - original licence link has changed is not relivant.
33956  *
33957  * Fork - LGPL
33958  * <script type="text/javascript">
33959  */
33960
33961
33962 /**
33963  * @class Roo.bootstrap.SplitBar
33964  * @extends Roo.util.Observable
33965  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33966  * <br><br>
33967  * Usage:
33968  * <pre><code>
33969 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33970                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33971 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33972 split.minSize = 100;
33973 split.maxSize = 600;
33974 split.animate = true;
33975 split.on('moved', splitterMoved);
33976 </code></pre>
33977  * @constructor
33978  * Create a new SplitBar
33979  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33980  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33981  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33982  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33983                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33984                         position of the SplitBar).
33985  */
33986 Roo.bootstrap.SplitBar = function(cfg){
33987     
33988     /** @private */
33989     
33990     //{
33991     //  dragElement : elm
33992     //  resizingElement: el,
33993         // optional..
33994     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33995     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33996         // existingProxy ???
33997     //}
33998     
33999     this.el = Roo.get(cfg.dragElement, true);
34000     this.el.dom.unselectable = "on";
34001     /** @private */
34002     this.resizingEl = Roo.get(cfg.resizingElement, true);
34003
34004     /**
34005      * @private
34006      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34007      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34008      * @type Number
34009      */
34010     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34011     
34012     /**
34013      * The minimum size of the resizing element. (Defaults to 0)
34014      * @type Number
34015      */
34016     this.minSize = 0;
34017     
34018     /**
34019      * The maximum size of the resizing element. (Defaults to 2000)
34020      * @type Number
34021      */
34022     this.maxSize = 2000;
34023     
34024     /**
34025      * Whether to animate the transition to the new size
34026      * @type Boolean
34027      */
34028     this.animate = false;
34029     
34030     /**
34031      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34032      * @type Boolean
34033      */
34034     this.useShim = false;
34035     
34036     /** @private */
34037     this.shim = null;
34038     
34039     if(!cfg.existingProxy){
34040         /** @private */
34041         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34042     }else{
34043         this.proxy = Roo.get(cfg.existingProxy).dom;
34044     }
34045     /** @private */
34046     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34047     
34048     /** @private */
34049     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34050     
34051     /** @private */
34052     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34053     
34054     /** @private */
34055     this.dragSpecs = {};
34056     
34057     /**
34058      * @private The adapter to use to positon and resize elements
34059      */
34060     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34061     this.adapter.init(this);
34062     
34063     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34064         /** @private */
34065         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34066         this.el.addClass("roo-splitbar-h");
34067     }else{
34068         /** @private */
34069         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34070         this.el.addClass("roo-splitbar-v");
34071     }
34072     
34073     this.addEvents({
34074         /**
34075          * @event resize
34076          * Fires when the splitter is moved (alias for {@link #event-moved})
34077          * @param {Roo.bootstrap.SplitBar} this
34078          * @param {Number} newSize the new width or height
34079          */
34080         "resize" : true,
34081         /**
34082          * @event moved
34083          * Fires when the splitter is moved
34084          * @param {Roo.bootstrap.SplitBar} this
34085          * @param {Number} newSize the new width or height
34086          */
34087         "moved" : true,
34088         /**
34089          * @event beforeresize
34090          * Fires before the splitter is dragged
34091          * @param {Roo.bootstrap.SplitBar} this
34092          */
34093         "beforeresize" : true,
34094
34095         "beforeapply" : true
34096     });
34097
34098     Roo.util.Observable.call(this);
34099 };
34100
34101 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34102     onStartProxyDrag : function(x, y){
34103         this.fireEvent("beforeresize", this);
34104         if(!this.overlay){
34105             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34106             o.unselectable();
34107             o.enableDisplayMode("block");
34108             // all splitbars share the same overlay
34109             Roo.bootstrap.SplitBar.prototype.overlay = o;
34110         }
34111         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34112         this.overlay.show();
34113         Roo.get(this.proxy).setDisplayed("block");
34114         var size = this.adapter.getElementSize(this);
34115         this.activeMinSize = this.getMinimumSize();;
34116         this.activeMaxSize = this.getMaximumSize();;
34117         var c1 = size - this.activeMinSize;
34118         var c2 = Math.max(this.activeMaxSize - size, 0);
34119         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34120             this.dd.resetConstraints();
34121             this.dd.setXConstraint(
34122                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34123                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34124             );
34125             this.dd.setYConstraint(0, 0);
34126         }else{
34127             this.dd.resetConstraints();
34128             this.dd.setXConstraint(0, 0);
34129             this.dd.setYConstraint(
34130                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34131                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34132             );
34133          }
34134         this.dragSpecs.startSize = size;
34135         this.dragSpecs.startPoint = [x, y];
34136         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34137     },
34138     
34139     /** 
34140      * @private Called after the drag operation by the DDProxy
34141      */
34142     onEndProxyDrag : function(e){
34143         Roo.get(this.proxy).setDisplayed(false);
34144         var endPoint = Roo.lib.Event.getXY(e);
34145         if(this.overlay){
34146             this.overlay.hide();
34147         }
34148         var newSize;
34149         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34150             newSize = this.dragSpecs.startSize + 
34151                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34152                     endPoint[0] - this.dragSpecs.startPoint[0] :
34153                     this.dragSpecs.startPoint[0] - endPoint[0]
34154                 );
34155         }else{
34156             newSize = this.dragSpecs.startSize + 
34157                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34158                     endPoint[1] - this.dragSpecs.startPoint[1] :
34159                     this.dragSpecs.startPoint[1] - endPoint[1]
34160                 );
34161         }
34162         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34163         if(newSize != this.dragSpecs.startSize){
34164             if(this.fireEvent('beforeapply', this, newSize) !== false){
34165                 this.adapter.setElementSize(this, newSize);
34166                 this.fireEvent("moved", this, newSize);
34167                 this.fireEvent("resize", this, newSize);
34168             }
34169         }
34170     },
34171     
34172     /**
34173      * Get the adapter this SplitBar uses
34174      * @return The adapter object
34175      */
34176     getAdapter : function(){
34177         return this.adapter;
34178     },
34179     
34180     /**
34181      * Set the adapter this SplitBar uses
34182      * @param {Object} adapter A SplitBar adapter object
34183      */
34184     setAdapter : function(adapter){
34185         this.adapter = adapter;
34186         this.adapter.init(this);
34187     },
34188     
34189     /**
34190      * Gets the minimum size for the resizing element
34191      * @return {Number} The minimum size
34192      */
34193     getMinimumSize : function(){
34194         return this.minSize;
34195     },
34196     
34197     /**
34198      * Sets the minimum size for the resizing element
34199      * @param {Number} minSize The minimum size
34200      */
34201     setMinimumSize : function(minSize){
34202         this.minSize = minSize;
34203     },
34204     
34205     /**
34206      * Gets the maximum size for the resizing element
34207      * @return {Number} The maximum size
34208      */
34209     getMaximumSize : function(){
34210         return this.maxSize;
34211     },
34212     
34213     /**
34214      * Sets the maximum size for the resizing element
34215      * @param {Number} maxSize The maximum size
34216      */
34217     setMaximumSize : function(maxSize){
34218         this.maxSize = maxSize;
34219     },
34220     
34221     /**
34222      * Sets the initialize size for the resizing element
34223      * @param {Number} size The initial size
34224      */
34225     setCurrentSize : function(size){
34226         var oldAnimate = this.animate;
34227         this.animate = false;
34228         this.adapter.setElementSize(this, size);
34229         this.animate = oldAnimate;
34230     },
34231     
34232     /**
34233      * Destroy this splitbar. 
34234      * @param {Boolean} removeEl True to remove the element
34235      */
34236     destroy : function(removeEl){
34237         if(this.shim){
34238             this.shim.remove();
34239         }
34240         this.dd.unreg();
34241         this.proxy.parentNode.removeChild(this.proxy);
34242         if(removeEl){
34243             this.el.remove();
34244         }
34245     }
34246 });
34247
34248 /**
34249  * @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.
34250  */
34251 Roo.bootstrap.SplitBar.createProxy = function(dir){
34252     var proxy = new Roo.Element(document.createElement("div"));
34253     proxy.unselectable();
34254     var cls = 'roo-splitbar-proxy';
34255     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34256     document.body.appendChild(proxy.dom);
34257     return proxy.dom;
34258 };
34259
34260 /** 
34261  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34262  * Default Adapter. It assumes the splitter and resizing element are not positioned
34263  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34264  */
34265 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34266 };
34267
34268 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34269     // do nothing for now
34270     init : function(s){
34271     
34272     },
34273     /**
34274      * Called before drag operations to get the current size of the resizing element. 
34275      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34276      */
34277      getElementSize : function(s){
34278         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34279             return s.resizingEl.getWidth();
34280         }else{
34281             return s.resizingEl.getHeight();
34282         }
34283     },
34284     
34285     /**
34286      * Called after drag operations to set the size of the resizing element.
34287      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34288      * @param {Number} newSize The new size to set
34289      * @param {Function} onComplete A function to be invoked when resizing is complete
34290      */
34291     setElementSize : function(s, newSize, onComplete){
34292         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34293             if(!s.animate){
34294                 s.resizingEl.setWidth(newSize);
34295                 if(onComplete){
34296                     onComplete(s, newSize);
34297                 }
34298             }else{
34299                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34300             }
34301         }else{
34302             
34303             if(!s.animate){
34304                 s.resizingEl.setHeight(newSize);
34305                 if(onComplete){
34306                     onComplete(s, newSize);
34307                 }
34308             }else{
34309                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34310             }
34311         }
34312     }
34313 };
34314
34315 /** 
34316  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34317  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34318  * Adapter that  moves the splitter element to align with the resized sizing element. 
34319  * Used with an absolute positioned SplitBar.
34320  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34321  * document.body, make sure you assign an id to the body element.
34322  */
34323 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34324     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34325     this.container = Roo.get(container);
34326 };
34327
34328 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34329     init : function(s){
34330         this.basic.init(s);
34331     },
34332     
34333     getElementSize : function(s){
34334         return this.basic.getElementSize(s);
34335     },
34336     
34337     setElementSize : function(s, newSize, onComplete){
34338         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34339     },
34340     
34341     moveSplitter : function(s){
34342         var yes = Roo.bootstrap.SplitBar;
34343         switch(s.placement){
34344             case yes.LEFT:
34345                 s.el.setX(s.resizingEl.getRight());
34346                 break;
34347             case yes.RIGHT:
34348                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34349                 break;
34350             case yes.TOP:
34351                 s.el.setY(s.resizingEl.getBottom());
34352                 break;
34353             case yes.BOTTOM:
34354                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34355                 break;
34356         }
34357     }
34358 };
34359
34360 /**
34361  * Orientation constant - Create a vertical SplitBar
34362  * @static
34363  * @type Number
34364  */
34365 Roo.bootstrap.SplitBar.VERTICAL = 1;
34366
34367 /**
34368  * Orientation constant - Create a horizontal SplitBar
34369  * @static
34370  * @type Number
34371  */
34372 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34373
34374 /**
34375  * Placement constant - The resizing element is to the left of the splitter element
34376  * @static
34377  * @type Number
34378  */
34379 Roo.bootstrap.SplitBar.LEFT = 1;
34380
34381 /**
34382  * Placement constant - The resizing element is to the right of the splitter element
34383  * @static
34384  * @type Number
34385  */
34386 Roo.bootstrap.SplitBar.RIGHT = 2;
34387
34388 /**
34389  * Placement constant - The resizing element is positioned above the splitter element
34390  * @static
34391  * @type Number
34392  */
34393 Roo.bootstrap.SplitBar.TOP = 3;
34394
34395 /**
34396  * Placement constant - The resizing element is positioned under splitter element
34397  * @static
34398  * @type Number
34399  */
34400 Roo.bootstrap.SplitBar.BOTTOM = 4;
34401 Roo.namespace("Roo.bootstrap.layout");/*
34402  * Based on:
34403  * Ext JS Library 1.1.1
34404  * Copyright(c) 2006-2007, Ext JS, LLC.
34405  *
34406  * Originally Released Under LGPL - original licence link has changed is not relivant.
34407  *
34408  * Fork - LGPL
34409  * <script type="text/javascript">
34410  */
34411
34412 /**
34413  * @class Roo.bootstrap.layout.Manager
34414  * @extends Roo.bootstrap.Component
34415  * Base class for layout managers.
34416  */
34417 Roo.bootstrap.layout.Manager = function(config)
34418 {
34419     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34420
34421
34422
34423
34424
34425     /** false to disable window resize monitoring @type Boolean */
34426     this.monitorWindowResize = true;
34427     this.regions = {};
34428     this.addEvents({
34429         /**
34430          * @event layout
34431          * Fires when a layout is performed.
34432          * @param {Roo.LayoutManager} this
34433          */
34434         "layout" : true,
34435         /**
34436          * @event regionresized
34437          * Fires when the user resizes a region.
34438          * @param {Roo.LayoutRegion} region The resized region
34439          * @param {Number} newSize The new size (width for east/west, height for north/south)
34440          */
34441         "regionresized" : true,
34442         /**
34443          * @event regioncollapsed
34444          * Fires when a region is collapsed.
34445          * @param {Roo.LayoutRegion} region The collapsed region
34446          */
34447         "regioncollapsed" : true,
34448         /**
34449          * @event regionexpanded
34450          * Fires when a region is expanded.
34451          * @param {Roo.LayoutRegion} region The expanded region
34452          */
34453         "regionexpanded" : true
34454     });
34455     this.updating = false;
34456
34457     if (config.el) {
34458         this.el = Roo.get(config.el);
34459         this.initEvents();
34460     }
34461
34462 };
34463
34464 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34465
34466
34467     regions : null,
34468
34469     monitorWindowResize : true,
34470
34471
34472     updating : false,
34473
34474
34475     onRender : function(ct, position)
34476     {
34477         if(!this.el){
34478             this.el = Roo.get(ct);
34479             this.initEvents();
34480         }
34481         //this.fireEvent('render',this);
34482     },
34483
34484
34485     initEvents: function()
34486     {
34487
34488
34489         // ie scrollbar fix
34490         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34491             document.body.scroll = "no";
34492         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34493             this.el.position('relative');
34494         }
34495         this.id = this.el.id;
34496         this.el.addClass("roo-layout-container");
34497         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34498         if(this.el.dom != document.body ) {
34499             this.el.on('resize', this.layout,this);
34500             this.el.on('show', this.layout,this);
34501         }
34502
34503     },
34504
34505     /**
34506      * Returns true if this layout is currently being updated
34507      * @return {Boolean}
34508      */
34509     isUpdating : function(){
34510         return this.updating;
34511     },
34512
34513     /**
34514      * Suspend the LayoutManager from doing auto-layouts while
34515      * making multiple add or remove calls
34516      */
34517     beginUpdate : function(){
34518         this.updating = true;
34519     },
34520
34521     /**
34522      * Restore auto-layouts and optionally disable the manager from performing a layout
34523      * @param {Boolean} noLayout true to disable a layout update
34524      */
34525     endUpdate : function(noLayout){
34526         this.updating = false;
34527         if(!noLayout){
34528             this.layout();
34529         }
34530     },
34531
34532     layout: function(){
34533         // abstract...
34534     },
34535
34536     onRegionResized : function(region, newSize){
34537         this.fireEvent("regionresized", region, newSize);
34538         this.layout();
34539     },
34540
34541     onRegionCollapsed : function(region){
34542         this.fireEvent("regioncollapsed", region);
34543     },
34544
34545     onRegionExpanded : function(region){
34546         this.fireEvent("regionexpanded", region);
34547     },
34548
34549     /**
34550      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34551      * performs box-model adjustments.
34552      * @return {Object} The size as an object {width: (the width), height: (the height)}
34553      */
34554     getViewSize : function()
34555     {
34556         var size;
34557         if(this.el.dom != document.body){
34558             size = this.el.getSize();
34559         }else{
34560             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34561         }
34562         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34563         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34564         return size;
34565     },
34566
34567     /**
34568      * Returns the Element this layout is bound to.
34569      * @return {Roo.Element}
34570      */
34571     getEl : function(){
34572         return this.el;
34573     },
34574
34575     /**
34576      * Returns the specified region.
34577      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34578      * @return {Roo.LayoutRegion}
34579      */
34580     getRegion : function(target){
34581         return this.regions[target.toLowerCase()];
34582     },
34583
34584     onWindowResize : function(){
34585         if(this.monitorWindowResize){
34586             this.layout();
34587         }
34588     }
34589 });
34590 /*
34591  * Based on:
34592  * Ext JS Library 1.1.1
34593  * Copyright(c) 2006-2007, Ext JS, LLC.
34594  *
34595  * Originally Released Under LGPL - original licence link has changed is not relivant.
34596  *
34597  * Fork - LGPL
34598  * <script type="text/javascript">
34599  */
34600 /**
34601  * @class Roo.bootstrap.layout.Border
34602  * @extends Roo.bootstrap.layout.Manager
34603  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34604  * please see: examples/bootstrap/nested.html<br><br>
34605  
34606 <b>The container the layout is rendered into can be either the body element or any other element.
34607 If it is not the body element, the container needs to either be an absolute positioned element,
34608 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34609 the container size if it is not the body element.</b>
34610
34611 * @constructor
34612 * Create a new Border
34613 * @param {Object} config Configuration options
34614  */
34615 Roo.bootstrap.layout.Border = function(config){
34616     config = config || {};
34617     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34618     
34619     
34620     
34621     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34622         if(config[region]){
34623             config[region].region = region;
34624             this.addRegion(config[region]);
34625         }
34626     },this);
34627     
34628 };
34629
34630 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34631
34632 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34633     /**
34634      * Creates and adds a new region if it doesn't already exist.
34635      * @param {String} target The target region key (north, south, east, west or center).
34636      * @param {Object} config The regions config object
34637      * @return {BorderLayoutRegion} The new region
34638      */
34639     addRegion : function(config)
34640     {
34641         if(!this.regions[config.region]){
34642             var r = this.factory(config);
34643             this.bindRegion(r);
34644         }
34645         return this.regions[config.region];
34646     },
34647
34648     // private (kinda)
34649     bindRegion : function(r){
34650         this.regions[r.config.region] = r;
34651         
34652         r.on("visibilitychange",    this.layout, this);
34653         r.on("paneladded",          this.layout, this);
34654         r.on("panelremoved",        this.layout, this);
34655         r.on("invalidated",         this.layout, this);
34656         r.on("resized",             this.onRegionResized, this);
34657         r.on("collapsed",           this.onRegionCollapsed, this);
34658         r.on("expanded",            this.onRegionExpanded, this);
34659     },
34660
34661     /**
34662      * Performs a layout update.
34663      */
34664     layout : function()
34665     {
34666         if(this.updating) {
34667             return;
34668         }
34669         
34670         // render all the rebions if they have not been done alreayd?
34671         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34672             if(this.regions[region] && !this.regions[region].bodyEl){
34673                 this.regions[region].onRender(this.el)
34674             }
34675         },this);
34676         
34677         var size = this.getViewSize();
34678         var w = size.width;
34679         var h = size.height;
34680         var centerW = w;
34681         var centerH = h;
34682         var centerY = 0;
34683         var centerX = 0;
34684         //var x = 0, y = 0;
34685
34686         var rs = this.regions;
34687         var north = rs["north"];
34688         var south = rs["south"]; 
34689         var west = rs["west"];
34690         var east = rs["east"];
34691         var center = rs["center"];
34692         //if(this.hideOnLayout){ // not supported anymore
34693             //c.el.setStyle("display", "none");
34694         //}
34695         if(north && north.isVisible()){
34696             var b = north.getBox();
34697             var m = north.getMargins();
34698             b.width = w - (m.left+m.right);
34699             b.x = m.left;
34700             b.y = m.top;
34701             centerY = b.height + b.y + m.bottom;
34702             centerH -= centerY;
34703             north.updateBox(this.safeBox(b));
34704         }
34705         if(south && south.isVisible()){
34706             var b = south.getBox();
34707             var m = south.getMargins();
34708             b.width = w - (m.left+m.right);
34709             b.x = m.left;
34710             var totalHeight = (b.height + m.top + m.bottom);
34711             b.y = h - totalHeight + m.top;
34712             centerH -= totalHeight;
34713             south.updateBox(this.safeBox(b));
34714         }
34715         if(west && west.isVisible()){
34716             var b = west.getBox();
34717             var m = west.getMargins();
34718             b.height = centerH - (m.top+m.bottom);
34719             b.x = m.left;
34720             b.y = centerY + m.top;
34721             var totalWidth = (b.width + m.left + m.right);
34722             centerX += totalWidth;
34723             centerW -= totalWidth;
34724             west.updateBox(this.safeBox(b));
34725         }
34726         if(east && east.isVisible()){
34727             var b = east.getBox();
34728             var m = east.getMargins();
34729             b.height = centerH - (m.top+m.bottom);
34730             var totalWidth = (b.width + m.left + m.right);
34731             b.x = w - totalWidth + m.left;
34732             b.y = centerY + m.top;
34733             centerW -= totalWidth;
34734             east.updateBox(this.safeBox(b));
34735         }
34736         if(center){
34737             var m = center.getMargins();
34738             var centerBox = {
34739                 x: centerX + m.left,
34740                 y: centerY + m.top,
34741                 width: centerW - (m.left+m.right),
34742                 height: centerH - (m.top+m.bottom)
34743             };
34744             //if(this.hideOnLayout){
34745                 //center.el.setStyle("display", "block");
34746             //}
34747             center.updateBox(this.safeBox(centerBox));
34748         }
34749         this.el.repaint();
34750         this.fireEvent("layout", this);
34751     },
34752
34753     // private
34754     safeBox : function(box){
34755         box.width = Math.max(0, box.width);
34756         box.height = Math.max(0, box.height);
34757         return box;
34758     },
34759
34760     /**
34761      * Adds a ContentPanel (or subclass) to this layout.
34762      * @param {String} target The target region key (north, south, east, west or center).
34763      * @param {Roo.ContentPanel} panel The panel to add
34764      * @return {Roo.ContentPanel} The added panel
34765      */
34766     add : function(target, panel){
34767          
34768         target = target.toLowerCase();
34769         return this.regions[target].add(panel);
34770     },
34771
34772     /**
34773      * Remove a ContentPanel (or subclass) to this layout.
34774      * @param {String} target The target region key (north, south, east, west or center).
34775      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34776      * @return {Roo.ContentPanel} The removed panel
34777      */
34778     remove : function(target, panel){
34779         target = target.toLowerCase();
34780         return this.regions[target].remove(panel);
34781     },
34782
34783     /**
34784      * Searches all regions for a panel with the specified id
34785      * @param {String} panelId
34786      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34787      */
34788     findPanel : function(panelId){
34789         var rs = this.regions;
34790         for(var target in rs){
34791             if(typeof rs[target] != "function"){
34792                 var p = rs[target].getPanel(panelId);
34793                 if(p){
34794                     return p;
34795                 }
34796             }
34797         }
34798         return null;
34799     },
34800
34801     /**
34802      * Searches all regions for a panel with the specified id and activates (shows) it.
34803      * @param {String/ContentPanel} panelId The panels id or the panel itself
34804      * @return {Roo.ContentPanel} The shown panel or null
34805      */
34806     showPanel : function(panelId) {
34807       var rs = this.regions;
34808       for(var target in rs){
34809          var r = rs[target];
34810          if(typeof r != "function"){
34811             if(r.hasPanel(panelId)){
34812                return r.showPanel(panelId);
34813             }
34814          }
34815       }
34816       return null;
34817    },
34818
34819    /**
34820      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34821      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34822      */
34823    /*
34824     restoreState : function(provider){
34825         if(!provider){
34826             provider = Roo.state.Manager;
34827         }
34828         var sm = new Roo.LayoutStateManager();
34829         sm.init(this, provider);
34830     },
34831 */
34832  
34833  
34834     /**
34835      * Adds a xtype elements to the layout.
34836      * <pre><code>
34837
34838 layout.addxtype({
34839        xtype : 'ContentPanel',
34840        region: 'west',
34841        items: [ .... ]
34842    }
34843 );
34844
34845 layout.addxtype({
34846         xtype : 'NestedLayoutPanel',
34847         region: 'west',
34848         layout: {
34849            center: { },
34850            west: { }   
34851         },
34852         items : [ ... list of content panels or nested layout panels.. ]
34853    }
34854 );
34855 </code></pre>
34856      * @param {Object} cfg Xtype definition of item to add.
34857      */
34858     addxtype : function(cfg)
34859     {
34860         // basically accepts a pannel...
34861         // can accept a layout region..!?!?
34862         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34863         
34864         
34865         // theory?  children can only be panels??
34866         
34867         //if (!cfg.xtype.match(/Panel$/)) {
34868         //    return false;
34869         //}
34870         var ret = false;
34871         
34872         if (typeof(cfg.region) == 'undefined') {
34873             Roo.log("Failed to add Panel, region was not set");
34874             Roo.log(cfg);
34875             return false;
34876         }
34877         var region = cfg.region;
34878         delete cfg.region;
34879         
34880           
34881         var xitems = [];
34882         if (cfg.items) {
34883             xitems = cfg.items;
34884             delete cfg.items;
34885         }
34886         var nb = false;
34887         
34888         switch(cfg.xtype) 
34889         {
34890             case 'Content':  // ContentPanel (el, cfg)
34891             case 'Scroll':  // ContentPanel (el, cfg)
34892             case 'View': 
34893                 cfg.autoCreate = true;
34894                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34895                 //} else {
34896                 //    var el = this.el.createChild();
34897                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34898                 //}
34899                 
34900                 this.add(region, ret);
34901                 break;
34902             
34903             /*
34904             case 'TreePanel': // our new panel!
34905                 cfg.el = this.el.createChild();
34906                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34907                 this.add(region, ret);
34908                 break;
34909             */
34910             
34911             case 'Nest': 
34912                 // create a new Layout (which is  a Border Layout...
34913                 
34914                 var clayout = cfg.layout;
34915                 clayout.el  = this.el.createChild();
34916                 clayout.items   = clayout.items  || [];
34917                 
34918                 delete cfg.layout;
34919                 
34920                 // replace this exitems with the clayout ones..
34921                 xitems = clayout.items;
34922                  
34923                 // force background off if it's in center...
34924                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34925                     cfg.background = false;
34926                 }
34927                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34928                 
34929                 
34930                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34931                 //console.log('adding nested layout panel '  + cfg.toSource());
34932                 this.add(region, ret);
34933                 nb = {}; /// find first...
34934                 break;
34935             
34936             case 'Grid':
34937                 
34938                 // needs grid and region
34939                 
34940                 //var el = this.getRegion(region).el.createChild();
34941                 /*
34942                  *var el = this.el.createChild();
34943                 // create the grid first...
34944                 cfg.grid.container = el;
34945                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34946                 */
34947                 
34948                 if (region == 'center' && this.active ) {
34949                     cfg.background = false;
34950                 }
34951                 
34952                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34953                 
34954                 this.add(region, ret);
34955                 /*
34956                 if (cfg.background) {
34957                     // render grid on panel activation (if panel background)
34958                     ret.on('activate', function(gp) {
34959                         if (!gp.grid.rendered) {
34960                     //        gp.grid.render(el);
34961                         }
34962                     });
34963                 } else {
34964                   //  cfg.grid.render(el);
34965                 }
34966                 */
34967                 break;
34968            
34969            
34970             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34971                 // it was the old xcomponent building that caused this before.
34972                 // espeically if border is the top element in the tree.
34973                 ret = this;
34974                 break; 
34975                 
34976                     
34977                 
34978                 
34979                 
34980             default:
34981                 /*
34982                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34983                     
34984                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34985                     this.add(region, ret);
34986                 } else {
34987                 */
34988                     Roo.log(cfg);
34989                     throw "Can not add '" + cfg.xtype + "' to Border";
34990                     return null;
34991              
34992                                 
34993              
34994         }
34995         this.beginUpdate();
34996         // add children..
34997         var region = '';
34998         var abn = {};
34999         Roo.each(xitems, function(i)  {
35000             region = nb && i.region ? i.region : false;
35001             
35002             var add = ret.addxtype(i);
35003            
35004             if (region) {
35005                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35006                 if (!i.background) {
35007                     abn[region] = nb[region] ;
35008                 }
35009             }
35010             
35011         });
35012         this.endUpdate();
35013
35014         // make the last non-background panel active..
35015         //if (nb) { Roo.log(abn); }
35016         if (nb) {
35017             
35018             for(var r in abn) {
35019                 region = this.getRegion(r);
35020                 if (region) {
35021                     // tried using nb[r], but it does not work..
35022                      
35023                     region.showPanel(abn[r]);
35024                    
35025                 }
35026             }
35027         }
35028         return ret;
35029         
35030     },
35031     
35032     
35033 // private
35034     factory : function(cfg)
35035     {
35036         
35037         var validRegions = Roo.bootstrap.layout.Border.regions;
35038
35039         var target = cfg.region;
35040         cfg.mgr = this;
35041         
35042         var r = Roo.bootstrap.layout;
35043         Roo.log(target);
35044         switch(target){
35045             case "north":
35046                 return new r.North(cfg);
35047             case "south":
35048                 return new r.South(cfg);
35049             case "east":
35050                 return new r.East(cfg);
35051             case "west":
35052                 return new r.West(cfg);
35053             case "center":
35054                 return new r.Center(cfg);
35055         }
35056         throw 'Layout region "'+target+'" not supported.';
35057     }
35058     
35059     
35060 });
35061  /*
35062  * Based on:
35063  * Ext JS Library 1.1.1
35064  * Copyright(c) 2006-2007, Ext JS, LLC.
35065  *
35066  * Originally Released Under LGPL - original licence link has changed is not relivant.
35067  *
35068  * Fork - LGPL
35069  * <script type="text/javascript">
35070  */
35071  
35072 /**
35073  * @class Roo.bootstrap.layout.Basic
35074  * @extends Roo.util.Observable
35075  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35076  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35077  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35078  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35079  * @cfg {string}   region  the region that it inhabits..
35080  * @cfg {bool}   skipConfig skip config?
35081  * 
35082
35083  */
35084 Roo.bootstrap.layout.Basic = function(config){
35085     
35086     this.mgr = config.mgr;
35087     
35088     this.position = config.region;
35089     
35090     var skipConfig = config.skipConfig;
35091     
35092     this.events = {
35093         /**
35094          * @scope Roo.BasicLayoutRegion
35095          */
35096         
35097         /**
35098          * @event beforeremove
35099          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35100          * @param {Roo.LayoutRegion} this
35101          * @param {Roo.ContentPanel} panel The panel
35102          * @param {Object} e The cancel event object
35103          */
35104         "beforeremove" : true,
35105         /**
35106          * @event invalidated
35107          * Fires when the layout for this region is changed.
35108          * @param {Roo.LayoutRegion} this
35109          */
35110         "invalidated" : true,
35111         /**
35112          * @event visibilitychange
35113          * Fires when this region is shown or hidden 
35114          * @param {Roo.LayoutRegion} this
35115          * @param {Boolean} visibility true or false
35116          */
35117         "visibilitychange" : true,
35118         /**
35119          * @event paneladded
35120          * Fires when a panel is added. 
35121          * @param {Roo.LayoutRegion} this
35122          * @param {Roo.ContentPanel} panel The panel
35123          */
35124         "paneladded" : true,
35125         /**
35126          * @event panelremoved
35127          * Fires when a panel is removed. 
35128          * @param {Roo.LayoutRegion} this
35129          * @param {Roo.ContentPanel} panel The panel
35130          */
35131         "panelremoved" : true,
35132         /**
35133          * @event beforecollapse
35134          * Fires when this region before collapse.
35135          * @param {Roo.LayoutRegion} this
35136          */
35137         "beforecollapse" : true,
35138         /**
35139          * @event collapsed
35140          * Fires when this region is collapsed.
35141          * @param {Roo.LayoutRegion} this
35142          */
35143         "collapsed" : true,
35144         /**
35145          * @event expanded
35146          * Fires when this region is expanded.
35147          * @param {Roo.LayoutRegion} this
35148          */
35149         "expanded" : true,
35150         /**
35151          * @event slideshow
35152          * Fires when this region is slid into view.
35153          * @param {Roo.LayoutRegion} this
35154          */
35155         "slideshow" : true,
35156         /**
35157          * @event slidehide
35158          * Fires when this region slides out of view. 
35159          * @param {Roo.LayoutRegion} this
35160          */
35161         "slidehide" : true,
35162         /**
35163          * @event panelactivated
35164          * Fires when a panel is activated. 
35165          * @param {Roo.LayoutRegion} this
35166          * @param {Roo.ContentPanel} panel The activated panel
35167          */
35168         "panelactivated" : true,
35169         /**
35170          * @event resized
35171          * Fires when the user resizes this region. 
35172          * @param {Roo.LayoutRegion} this
35173          * @param {Number} newSize The new size (width for east/west, height for north/south)
35174          */
35175         "resized" : true
35176     };
35177     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35178     this.panels = new Roo.util.MixedCollection();
35179     this.panels.getKey = this.getPanelId.createDelegate(this);
35180     this.box = null;
35181     this.activePanel = null;
35182     // ensure listeners are added...
35183     
35184     if (config.listeners || config.events) {
35185         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35186             listeners : config.listeners || {},
35187             events : config.events || {}
35188         });
35189     }
35190     
35191     if(skipConfig !== true){
35192         this.applyConfig(config);
35193     }
35194 };
35195
35196 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35197 {
35198     getPanelId : function(p){
35199         return p.getId();
35200     },
35201     
35202     applyConfig : function(config){
35203         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35204         this.config = config;
35205         
35206     },
35207     
35208     /**
35209      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35210      * the width, for horizontal (north, south) the height.
35211      * @param {Number} newSize The new width or height
35212      */
35213     resizeTo : function(newSize){
35214         var el = this.el ? this.el :
35215                  (this.activePanel ? this.activePanel.getEl() : null);
35216         if(el){
35217             switch(this.position){
35218                 case "east":
35219                 case "west":
35220                     el.setWidth(newSize);
35221                     this.fireEvent("resized", this, newSize);
35222                 break;
35223                 case "north":
35224                 case "south":
35225                     el.setHeight(newSize);
35226                     this.fireEvent("resized", this, newSize);
35227                 break;                
35228             }
35229         }
35230     },
35231     
35232     getBox : function(){
35233         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35234     },
35235     
35236     getMargins : function(){
35237         return this.margins;
35238     },
35239     
35240     updateBox : function(box){
35241         this.box = box;
35242         var el = this.activePanel.getEl();
35243         el.dom.style.left = box.x + "px";
35244         el.dom.style.top = box.y + "px";
35245         this.activePanel.setSize(box.width, box.height);
35246     },
35247     
35248     /**
35249      * Returns the container element for this region.
35250      * @return {Roo.Element}
35251      */
35252     getEl : function(){
35253         return this.activePanel;
35254     },
35255     
35256     /**
35257      * Returns true if this region is currently visible.
35258      * @return {Boolean}
35259      */
35260     isVisible : function(){
35261         return this.activePanel ? true : false;
35262     },
35263     
35264     setActivePanel : function(panel){
35265         panel = this.getPanel(panel);
35266         if(this.activePanel && this.activePanel != panel){
35267             this.activePanel.setActiveState(false);
35268             this.activePanel.getEl().setLeftTop(-10000,-10000);
35269         }
35270         this.activePanel = panel;
35271         panel.setActiveState(true);
35272         if(this.box){
35273             panel.setSize(this.box.width, this.box.height);
35274         }
35275         this.fireEvent("panelactivated", this, panel);
35276         this.fireEvent("invalidated");
35277     },
35278     
35279     /**
35280      * Show the specified panel.
35281      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35282      * @return {Roo.ContentPanel} The shown panel or null
35283      */
35284     showPanel : function(panel){
35285         panel = this.getPanel(panel);
35286         if(panel){
35287             this.setActivePanel(panel);
35288         }
35289         return panel;
35290     },
35291     
35292     /**
35293      * Get the active panel for this region.
35294      * @return {Roo.ContentPanel} The active panel or null
35295      */
35296     getActivePanel : function(){
35297         return this.activePanel;
35298     },
35299     
35300     /**
35301      * Add the passed ContentPanel(s)
35302      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35303      * @return {Roo.ContentPanel} The panel added (if only one was added)
35304      */
35305     add : function(panel){
35306         if(arguments.length > 1){
35307             for(var i = 0, len = arguments.length; i < len; i++) {
35308                 this.add(arguments[i]);
35309             }
35310             return null;
35311         }
35312         if(this.hasPanel(panel)){
35313             this.showPanel(panel);
35314             return panel;
35315         }
35316         var el = panel.getEl();
35317         if(el.dom.parentNode != this.mgr.el.dom){
35318             this.mgr.el.dom.appendChild(el.dom);
35319         }
35320         if(panel.setRegion){
35321             panel.setRegion(this);
35322         }
35323         this.panels.add(panel);
35324         el.setStyle("position", "absolute");
35325         if(!panel.background){
35326             this.setActivePanel(panel);
35327             if(this.config.initialSize && this.panels.getCount()==1){
35328                 this.resizeTo(this.config.initialSize);
35329             }
35330         }
35331         this.fireEvent("paneladded", this, panel);
35332         return panel;
35333     },
35334     
35335     /**
35336      * Returns true if the panel is in this region.
35337      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35338      * @return {Boolean}
35339      */
35340     hasPanel : function(panel){
35341         if(typeof panel == "object"){ // must be panel obj
35342             panel = panel.getId();
35343         }
35344         return this.getPanel(panel) ? true : false;
35345     },
35346     
35347     /**
35348      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35349      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35350      * @param {Boolean} preservePanel Overrides the config preservePanel option
35351      * @return {Roo.ContentPanel} The panel that was removed
35352      */
35353     remove : function(panel, preservePanel){
35354         panel = this.getPanel(panel);
35355         if(!panel){
35356             return null;
35357         }
35358         var e = {};
35359         this.fireEvent("beforeremove", this, panel, e);
35360         if(e.cancel === true){
35361             return null;
35362         }
35363         var panelId = panel.getId();
35364         this.panels.removeKey(panelId);
35365         return panel;
35366     },
35367     
35368     /**
35369      * Returns the panel specified or null if it's not in this region.
35370      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35371      * @return {Roo.ContentPanel}
35372      */
35373     getPanel : function(id){
35374         if(typeof id == "object"){ // must be panel obj
35375             return id;
35376         }
35377         return this.panels.get(id);
35378     },
35379     
35380     /**
35381      * Returns this regions position (north/south/east/west/center).
35382      * @return {String} 
35383      */
35384     getPosition: function(){
35385         return this.position;    
35386     }
35387 });/*
35388  * Based on:
35389  * Ext JS Library 1.1.1
35390  * Copyright(c) 2006-2007, Ext JS, LLC.
35391  *
35392  * Originally Released Under LGPL - original licence link has changed is not relivant.
35393  *
35394  * Fork - LGPL
35395  * <script type="text/javascript">
35396  */
35397  
35398 /**
35399  * @class Roo.bootstrap.layout.Region
35400  * @extends Roo.bootstrap.layout.Basic
35401  * This class represents a region in a layout manager.
35402  
35403  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35404  * @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})
35405  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35406  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35407  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35408  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35409  * @cfg {String}    title           The title for the region (overrides panel titles)
35410  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35411  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35412  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35413  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35414  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35415  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35416  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35417  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35418  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35419  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35420
35421  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35422  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35423  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35424  * @cfg {Number}    width           For East/West panels
35425  * @cfg {Number}    height          For North/South panels
35426  * @cfg {Boolean}   split           To show the splitter
35427  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35428  * 
35429  * @cfg {string}   cls             Extra CSS classes to add to region
35430  * 
35431  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35432  * @cfg {string}   region  the region that it inhabits..
35433  *
35434
35435  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35436  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35437
35438  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35439  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35440  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35441  */
35442 Roo.bootstrap.layout.Region = function(config)
35443 {
35444     this.applyConfig(config);
35445
35446     var mgr = config.mgr;
35447     var pos = config.region;
35448     config.skipConfig = true;
35449     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35450     
35451     if (mgr.el) {
35452         this.onRender(mgr.el);   
35453     }
35454      
35455     this.visible = true;
35456     this.collapsed = false;
35457     this.unrendered_panels = [];
35458 };
35459
35460 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35461
35462     position: '', // set by wrapper (eg. north/south etc..)
35463     unrendered_panels : null,  // unrendered panels.
35464     createBody : function(){
35465         /** This region's body element 
35466         * @type Roo.Element */
35467         this.bodyEl = this.el.createChild({
35468                 tag: "div",
35469                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35470         });
35471     },
35472
35473     onRender: function(ctr, pos)
35474     {
35475         var dh = Roo.DomHelper;
35476         /** This region's container element 
35477         * @type Roo.Element */
35478         this.el = dh.append(ctr.dom, {
35479                 tag: "div",
35480                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35481             }, true);
35482         /** This region's title element 
35483         * @type Roo.Element */
35484     
35485         this.titleEl = dh.append(this.el.dom,
35486             {
35487                     tag: "div",
35488                     unselectable: "on",
35489                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35490                     children:[
35491                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35492                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35493                     ]}, true);
35494         
35495         this.titleEl.enableDisplayMode();
35496         /** This region's title text element 
35497         * @type HTMLElement */
35498         this.titleTextEl = this.titleEl.dom.firstChild;
35499         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35500         /*
35501         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35502         this.closeBtn.enableDisplayMode();
35503         this.closeBtn.on("click", this.closeClicked, this);
35504         this.closeBtn.hide();
35505     */
35506         this.createBody(this.config);
35507         if(this.config.hideWhenEmpty){
35508             this.hide();
35509             this.on("paneladded", this.validateVisibility, this);
35510             this.on("panelremoved", this.validateVisibility, this);
35511         }
35512         if(this.autoScroll){
35513             this.bodyEl.setStyle("overflow", "auto");
35514         }else{
35515             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35516         }
35517         //if(c.titlebar !== false){
35518             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35519                 this.titleEl.hide();
35520             }else{
35521                 this.titleEl.show();
35522                 if(this.config.title){
35523                     this.titleTextEl.innerHTML = this.config.title;
35524                 }
35525             }
35526         //}
35527         if(this.config.collapsed){
35528             this.collapse(true);
35529         }
35530         if(this.config.hidden){
35531             this.hide();
35532         }
35533         
35534         if (this.unrendered_panels && this.unrendered_panels.length) {
35535             for (var i =0;i< this.unrendered_panels.length; i++) {
35536                 this.add(this.unrendered_panels[i]);
35537             }
35538             this.unrendered_panels = null;
35539             
35540         }
35541         
35542     },
35543     
35544     applyConfig : function(c)
35545     {
35546         /*
35547          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35548             var dh = Roo.DomHelper;
35549             if(c.titlebar !== false){
35550                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35551                 this.collapseBtn.on("click", this.collapse, this);
35552                 this.collapseBtn.enableDisplayMode();
35553                 /*
35554                 if(c.showPin === true || this.showPin){
35555                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35556                     this.stickBtn.enableDisplayMode();
35557                     this.stickBtn.on("click", this.expand, this);
35558                     this.stickBtn.hide();
35559                 }
35560                 
35561             }
35562             */
35563             /** This region's collapsed element
35564             * @type Roo.Element */
35565             /*
35566              *
35567             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35568                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35569             ]}, true);
35570             
35571             if(c.floatable !== false){
35572                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35573                this.collapsedEl.on("click", this.collapseClick, this);
35574             }
35575
35576             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35577                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35578                    id: "message", unselectable: "on", style:{"float":"left"}});
35579                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35580              }
35581             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35582             this.expandBtn.on("click", this.expand, this);
35583             
35584         }
35585         
35586         if(this.collapseBtn){
35587             this.collapseBtn.setVisible(c.collapsible == true);
35588         }
35589         
35590         this.cmargins = c.cmargins || this.cmargins ||
35591                          (this.position == "west" || this.position == "east" ?
35592                              {top: 0, left: 2, right:2, bottom: 0} :
35593                              {top: 2, left: 0, right:0, bottom: 2});
35594         */
35595         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35596         
35597         
35598         this.bottomTabs = c.tabPosition != "top";
35599         
35600         this.autoScroll = c.autoScroll || false;
35601         
35602         
35603        
35604         
35605         this.duration = c.duration || .30;
35606         this.slideDuration = c.slideDuration || .45;
35607         this.config = c;
35608        
35609     },
35610     /**
35611      * Returns true if this region is currently visible.
35612      * @return {Boolean}
35613      */
35614     isVisible : function(){
35615         return this.visible;
35616     },
35617
35618     /**
35619      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35620      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35621      */
35622     //setCollapsedTitle : function(title){
35623     //    title = title || "&#160;";
35624      //   if(this.collapsedTitleTextEl){
35625       //      this.collapsedTitleTextEl.innerHTML = title;
35626        // }
35627     //},
35628
35629     getBox : function(){
35630         var b;
35631       //  if(!this.collapsed){
35632             b = this.el.getBox(false, true);
35633        // }else{
35634           //  b = this.collapsedEl.getBox(false, true);
35635         //}
35636         return b;
35637     },
35638
35639     getMargins : function(){
35640         return this.margins;
35641         //return this.collapsed ? this.cmargins : this.margins;
35642     },
35643 /*
35644     highlight : function(){
35645         this.el.addClass("x-layout-panel-dragover");
35646     },
35647
35648     unhighlight : function(){
35649         this.el.removeClass("x-layout-panel-dragover");
35650     },
35651 */
35652     updateBox : function(box)
35653     {
35654         if (!this.bodyEl) {
35655             return; // not rendered yet..
35656         }
35657         
35658         this.box = box;
35659         if(!this.collapsed){
35660             this.el.dom.style.left = box.x + "px";
35661             this.el.dom.style.top = box.y + "px";
35662             this.updateBody(box.width, box.height);
35663         }else{
35664             this.collapsedEl.dom.style.left = box.x + "px";
35665             this.collapsedEl.dom.style.top = box.y + "px";
35666             this.collapsedEl.setSize(box.width, box.height);
35667         }
35668         if(this.tabs){
35669             this.tabs.autoSizeTabs();
35670         }
35671     },
35672
35673     updateBody : function(w, h)
35674     {
35675         if(w !== null){
35676             this.el.setWidth(w);
35677             w -= this.el.getBorderWidth("rl");
35678             if(this.config.adjustments){
35679                 w += this.config.adjustments[0];
35680             }
35681         }
35682         if(h !== null && h > 0){
35683             this.el.setHeight(h);
35684             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35685             h -= this.el.getBorderWidth("tb");
35686             if(this.config.adjustments){
35687                 h += this.config.adjustments[1];
35688             }
35689             this.bodyEl.setHeight(h);
35690             if(this.tabs){
35691                 h = this.tabs.syncHeight(h);
35692             }
35693         }
35694         if(this.panelSize){
35695             w = w !== null ? w : this.panelSize.width;
35696             h = h !== null ? h : this.panelSize.height;
35697         }
35698         if(this.activePanel){
35699             var el = this.activePanel.getEl();
35700             w = w !== null ? w : el.getWidth();
35701             h = h !== null ? h : el.getHeight();
35702             this.panelSize = {width: w, height: h};
35703             this.activePanel.setSize(w, h);
35704         }
35705         if(Roo.isIE && this.tabs){
35706             this.tabs.el.repaint();
35707         }
35708     },
35709
35710     /**
35711      * Returns the container element for this region.
35712      * @return {Roo.Element}
35713      */
35714     getEl : function(){
35715         return this.el;
35716     },
35717
35718     /**
35719      * Hides this region.
35720      */
35721     hide : function(){
35722         //if(!this.collapsed){
35723             this.el.dom.style.left = "-2000px";
35724             this.el.hide();
35725         //}else{
35726          //   this.collapsedEl.dom.style.left = "-2000px";
35727          //   this.collapsedEl.hide();
35728        // }
35729         this.visible = false;
35730         this.fireEvent("visibilitychange", this, false);
35731     },
35732
35733     /**
35734      * Shows this region if it was previously hidden.
35735      */
35736     show : function(){
35737         //if(!this.collapsed){
35738             this.el.show();
35739         //}else{
35740         //    this.collapsedEl.show();
35741        // }
35742         this.visible = true;
35743         this.fireEvent("visibilitychange", this, true);
35744     },
35745 /*
35746     closeClicked : function(){
35747         if(this.activePanel){
35748             this.remove(this.activePanel);
35749         }
35750     },
35751
35752     collapseClick : function(e){
35753         if(this.isSlid){
35754            e.stopPropagation();
35755            this.slideIn();
35756         }else{
35757            e.stopPropagation();
35758            this.slideOut();
35759         }
35760     },
35761 */
35762     /**
35763      * Collapses this region.
35764      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35765      */
35766     /*
35767     collapse : function(skipAnim, skipCheck = false){
35768         if(this.collapsed) {
35769             return;
35770         }
35771         
35772         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35773             
35774             this.collapsed = true;
35775             if(this.split){
35776                 this.split.el.hide();
35777             }
35778             if(this.config.animate && skipAnim !== true){
35779                 this.fireEvent("invalidated", this);
35780                 this.animateCollapse();
35781             }else{
35782                 this.el.setLocation(-20000,-20000);
35783                 this.el.hide();
35784                 this.collapsedEl.show();
35785                 this.fireEvent("collapsed", this);
35786                 this.fireEvent("invalidated", this);
35787             }
35788         }
35789         
35790     },
35791 */
35792     animateCollapse : function(){
35793         // overridden
35794     },
35795
35796     /**
35797      * Expands this region if it was previously collapsed.
35798      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35799      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35800      */
35801     /*
35802     expand : function(e, skipAnim){
35803         if(e) {
35804             e.stopPropagation();
35805         }
35806         if(!this.collapsed || this.el.hasActiveFx()) {
35807             return;
35808         }
35809         if(this.isSlid){
35810             this.afterSlideIn();
35811             skipAnim = true;
35812         }
35813         this.collapsed = false;
35814         if(this.config.animate && skipAnim !== true){
35815             this.animateExpand();
35816         }else{
35817             this.el.show();
35818             if(this.split){
35819                 this.split.el.show();
35820             }
35821             this.collapsedEl.setLocation(-2000,-2000);
35822             this.collapsedEl.hide();
35823             this.fireEvent("invalidated", this);
35824             this.fireEvent("expanded", this);
35825         }
35826     },
35827 */
35828     animateExpand : function(){
35829         // overridden
35830     },
35831
35832     initTabs : function()
35833     {
35834         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35835         
35836         var ts = new Roo.bootstrap.panel.Tabs({
35837                 el: this.bodyEl.dom,
35838                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35839                 disableTooltips: this.config.disableTabTips,
35840                 toolbar : this.config.toolbar
35841             });
35842         
35843         if(this.config.hideTabs){
35844             ts.stripWrap.setDisplayed(false);
35845         }
35846         this.tabs = ts;
35847         ts.resizeTabs = this.config.resizeTabs === true;
35848         ts.minTabWidth = this.config.minTabWidth || 40;
35849         ts.maxTabWidth = this.config.maxTabWidth || 250;
35850         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35851         ts.monitorResize = false;
35852         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35853         ts.bodyEl.addClass('roo-layout-tabs-body');
35854         this.panels.each(this.initPanelAsTab, this);
35855     },
35856
35857     initPanelAsTab : function(panel){
35858         var ti = this.tabs.addTab(
35859             panel.getEl().id,
35860             panel.getTitle(),
35861             null,
35862             this.config.closeOnTab && panel.isClosable(),
35863             panel.tpl
35864         );
35865         if(panel.tabTip !== undefined){
35866             ti.setTooltip(panel.tabTip);
35867         }
35868         ti.on("activate", function(){
35869               this.setActivePanel(panel);
35870         }, this);
35871         
35872         if(this.config.closeOnTab){
35873             ti.on("beforeclose", function(t, e){
35874                 e.cancel = true;
35875                 this.remove(panel);
35876             }, this);
35877         }
35878         
35879         panel.tabItem = ti;
35880         
35881         return ti;
35882     },
35883
35884     updatePanelTitle : function(panel, title)
35885     {
35886         if(this.activePanel == panel){
35887             this.updateTitle(title);
35888         }
35889         if(this.tabs){
35890             var ti = this.tabs.getTab(panel.getEl().id);
35891             ti.setText(title);
35892             if(panel.tabTip !== undefined){
35893                 ti.setTooltip(panel.tabTip);
35894             }
35895         }
35896     },
35897
35898     updateTitle : function(title){
35899         if(this.titleTextEl && !this.config.title){
35900             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35901         }
35902     },
35903
35904     setActivePanel : function(panel)
35905     {
35906         panel = this.getPanel(panel);
35907         if(this.activePanel && this.activePanel != panel){
35908             if(this.activePanel.setActiveState(false) === false){
35909                 return;
35910             }
35911         }
35912         this.activePanel = panel;
35913         panel.setActiveState(true);
35914         if(this.panelSize){
35915             panel.setSize(this.panelSize.width, this.panelSize.height);
35916         }
35917         if(this.closeBtn){
35918             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35919         }
35920         this.updateTitle(panel.getTitle());
35921         if(this.tabs){
35922             this.fireEvent("invalidated", this);
35923         }
35924         this.fireEvent("panelactivated", this, panel);
35925     },
35926
35927     /**
35928      * Shows the specified panel.
35929      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35930      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35931      */
35932     showPanel : function(panel)
35933     {
35934         panel = this.getPanel(panel);
35935         if(panel){
35936             if(this.tabs){
35937                 var tab = this.tabs.getTab(panel.getEl().id);
35938                 if(tab.isHidden()){
35939                     this.tabs.unhideTab(tab.id);
35940                 }
35941                 tab.activate();
35942             }else{
35943                 this.setActivePanel(panel);
35944             }
35945         }
35946         return panel;
35947     },
35948
35949     /**
35950      * Get the active panel for this region.
35951      * @return {Roo.ContentPanel} The active panel or null
35952      */
35953     getActivePanel : function(){
35954         return this.activePanel;
35955     },
35956
35957     validateVisibility : function(){
35958         if(this.panels.getCount() < 1){
35959             this.updateTitle("&#160;");
35960             this.closeBtn.hide();
35961             this.hide();
35962         }else{
35963             if(!this.isVisible()){
35964                 this.show();
35965             }
35966         }
35967     },
35968
35969     /**
35970      * Adds the passed ContentPanel(s) to this region.
35971      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35972      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35973      */
35974     add : function(panel)
35975     {
35976         if(arguments.length > 1){
35977             for(var i = 0, len = arguments.length; i < len; i++) {
35978                 this.add(arguments[i]);
35979             }
35980             return null;
35981         }
35982         
35983         // if we have not been rendered yet, then we can not really do much of this..
35984         if (!this.bodyEl) {
35985             this.unrendered_panels.push(panel);
35986             return panel;
35987         }
35988         
35989         
35990         
35991         
35992         if(this.hasPanel(panel)){
35993             this.showPanel(panel);
35994             return panel;
35995         }
35996         panel.setRegion(this);
35997         this.panels.add(panel);
35998        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35999             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36000             // and hide them... ???
36001             this.bodyEl.dom.appendChild(panel.getEl().dom);
36002             if(panel.background !== true){
36003                 this.setActivePanel(panel);
36004             }
36005             this.fireEvent("paneladded", this, panel);
36006             return panel;
36007         }
36008         */
36009         if(!this.tabs){
36010             this.initTabs();
36011         }else{
36012             this.initPanelAsTab(panel);
36013         }
36014         
36015         
36016         if(panel.background !== true){
36017             this.tabs.activate(panel.getEl().id);
36018         }
36019         this.fireEvent("paneladded", this, panel);
36020         return panel;
36021     },
36022
36023     /**
36024      * Hides the tab for the specified panel.
36025      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36026      */
36027     hidePanel : function(panel){
36028         if(this.tabs && (panel = this.getPanel(panel))){
36029             this.tabs.hideTab(panel.getEl().id);
36030         }
36031     },
36032
36033     /**
36034      * Unhides the tab for a previously hidden panel.
36035      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36036      */
36037     unhidePanel : function(panel){
36038         if(this.tabs && (panel = this.getPanel(panel))){
36039             this.tabs.unhideTab(panel.getEl().id);
36040         }
36041     },
36042
36043     clearPanels : function(){
36044         while(this.panels.getCount() > 0){
36045              this.remove(this.panels.first());
36046         }
36047     },
36048
36049     /**
36050      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36051      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36052      * @param {Boolean} preservePanel Overrides the config preservePanel option
36053      * @return {Roo.ContentPanel} The panel that was removed
36054      */
36055     remove : function(panel, preservePanel)
36056     {
36057         panel = this.getPanel(panel);
36058         if(!panel){
36059             return null;
36060         }
36061         var e = {};
36062         this.fireEvent("beforeremove", this, panel, e);
36063         if(e.cancel === true){
36064             return null;
36065         }
36066         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36067         var panelId = panel.getId();
36068         this.panels.removeKey(panelId);
36069         if(preservePanel){
36070             document.body.appendChild(panel.getEl().dom);
36071         }
36072         if(this.tabs){
36073             this.tabs.removeTab(panel.getEl().id);
36074         }else if (!preservePanel){
36075             this.bodyEl.dom.removeChild(panel.getEl().dom);
36076         }
36077         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36078             var p = this.panels.first();
36079             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36080             tempEl.appendChild(p.getEl().dom);
36081             this.bodyEl.update("");
36082             this.bodyEl.dom.appendChild(p.getEl().dom);
36083             tempEl = null;
36084             this.updateTitle(p.getTitle());
36085             this.tabs = null;
36086             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36087             this.setActivePanel(p);
36088         }
36089         panel.setRegion(null);
36090         if(this.activePanel == panel){
36091             this.activePanel = null;
36092         }
36093         if(this.config.autoDestroy !== false && preservePanel !== true){
36094             try{panel.destroy();}catch(e){}
36095         }
36096         this.fireEvent("panelremoved", this, panel);
36097         return panel;
36098     },
36099
36100     /**
36101      * Returns the TabPanel component used by this region
36102      * @return {Roo.TabPanel}
36103      */
36104     getTabs : function(){
36105         return this.tabs;
36106     },
36107
36108     createTool : function(parentEl, className){
36109         var btn = Roo.DomHelper.append(parentEl, {
36110             tag: "div",
36111             cls: "x-layout-tools-button",
36112             children: [ {
36113                 tag: "div",
36114                 cls: "roo-layout-tools-button-inner " + className,
36115                 html: "&#160;"
36116             }]
36117         }, true);
36118         btn.addClassOnOver("roo-layout-tools-button-over");
36119         return btn;
36120     }
36121 });/*
36122  * Based on:
36123  * Ext JS Library 1.1.1
36124  * Copyright(c) 2006-2007, Ext JS, LLC.
36125  *
36126  * Originally Released Under LGPL - original licence link has changed is not relivant.
36127  *
36128  * Fork - LGPL
36129  * <script type="text/javascript">
36130  */
36131  
36132
36133
36134 /**
36135  * @class Roo.SplitLayoutRegion
36136  * @extends Roo.LayoutRegion
36137  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36138  */
36139 Roo.bootstrap.layout.Split = function(config){
36140     this.cursor = config.cursor;
36141     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36142 };
36143
36144 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36145 {
36146     splitTip : "Drag to resize.",
36147     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36148     useSplitTips : false,
36149
36150     applyConfig : function(config){
36151         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36152     },
36153     
36154     onRender : function(ctr,pos) {
36155         
36156         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36157         if(!this.config.split){
36158             return;
36159         }
36160         if(!this.split){
36161             
36162             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36163                             tag: "div",
36164                             id: this.el.id + "-split",
36165                             cls: "roo-layout-split roo-layout-split-"+this.position,
36166                             html: "&#160;"
36167             });
36168             /** The SplitBar for this region 
36169             * @type Roo.SplitBar */
36170             // does not exist yet...
36171             Roo.log([this.position, this.orientation]);
36172             
36173             this.split = new Roo.bootstrap.SplitBar({
36174                 dragElement : splitEl,
36175                 resizingElement: this.el,
36176                 orientation : this.orientation
36177             });
36178             
36179             this.split.on("moved", this.onSplitMove, this);
36180             this.split.useShim = this.config.useShim === true;
36181             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36182             if(this.useSplitTips){
36183                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36184             }
36185             //if(config.collapsible){
36186             //    this.split.el.on("dblclick", this.collapse,  this);
36187             //}
36188         }
36189         if(typeof this.config.minSize != "undefined"){
36190             this.split.minSize = this.config.minSize;
36191         }
36192         if(typeof this.config.maxSize != "undefined"){
36193             this.split.maxSize = this.config.maxSize;
36194         }
36195         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36196             this.hideSplitter();
36197         }
36198         
36199     },
36200
36201     getHMaxSize : function(){
36202          var cmax = this.config.maxSize || 10000;
36203          var center = this.mgr.getRegion("center");
36204          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36205     },
36206
36207     getVMaxSize : function(){
36208          var cmax = this.config.maxSize || 10000;
36209          var center = this.mgr.getRegion("center");
36210          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36211     },
36212
36213     onSplitMove : function(split, newSize){
36214         this.fireEvent("resized", this, newSize);
36215     },
36216     
36217     /** 
36218      * Returns the {@link Roo.SplitBar} for this region.
36219      * @return {Roo.SplitBar}
36220      */
36221     getSplitBar : function(){
36222         return this.split;
36223     },
36224     
36225     hide : function(){
36226         this.hideSplitter();
36227         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36228     },
36229
36230     hideSplitter : function(){
36231         if(this.split){
36232             this.split.el.setLocation(-2000,-2000);
36233             this.split.el.hide();
36234         }
36235     },
36236
36237     show : function(){
36238         if(this.split){
36239             this.split.el.show();
36240         }
36241         Roo.bootstrap.layout.Split.superclass.show.call(this);
36242     },
36243     
36244     beforeSlide: function(){
36245         if(Roo.isGecko){// firefox overflow auto bug workaround
36246             this.bodyEl.clip();
36247             if(this.tabs) {
36248                 this.tabs.bodyEl.clip();
36249             }
36250             if(this.activePanel){
36251                 this.activePanel.getEl().clip();
36252                 
36253                 if(this.activePanel.beforeSlide){
36254                     this.activePanel.beforeSlide();
36255                 }
36256             }
36257         }
36258     },
36259     
36260     afterSlide : function(){
36261         if(Roo.isGecko){// firefox overflow auto bug workaround
36262             this.bodyEl.unclip();
36263             if(this.tabs) {
36264                 this.tabs.bodyEl.unclip();
36265             }
36266             if(this.activePanel){
36267                 this.activePanel.getEl().unclip();
36268                 if(this.activePanel.afterSlide){
36269                     this.activePanel.afterSlide();
36270                 }
36271             }
36272         }
36273     },
36274
36275     initAutoHide : function(){
36276         if(this.autoHide !== false){
36277             if(!this.autoHideHd){
36278                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36279                 this.autoHideHd = {
36280                     "mouseout": function(e){
36281                         if(!e.within(this.el, true)){
36282                             st.delay(500);
36283                         }
36284                     },
36285                     "mouseover" : function(e){
36286                         st.cancel();
36287                     },
36288                     scope : this
36289                 };
36290             }
36291             this.el.on(this.autoHideHd);
36292         }
36293     },
36294
36295     clearAutoHide : function(){
36296         if(this.autoHide !== false){
36297             this.el.un("mouseout", this.autoHideHd.mouseout);
36298             this.el.un("mouseover", this.autoHideHd.mouseover);
36299         }
36300     },
36301
36302     clearMonitor : function(){
36303         Roo.get(document).un("click", this.slideInIf, this);
36304     },
36305
36306     // these names are backwards but not changed for compat
36307     slideOut : function(){
36308         if(this.isSlid || this.el.hasActiveFx()){
36309             return;
36310         }
36311         this.isSlid = true;
36312         if(this.collapseBtn){
36313             this.collapseBtn.hide();
36314         }
36315         this.closeBtnState = this.closeBtn.getStyle('display');
36316         this.closeBtn.hide();
36317         if(this.stickBtn){
36318             this.stickBtn.show();
36319         }
36320         this.el.show();
36321         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36322         this.beforeSlide();
36323         this.el.setStyle("z-index", 10001);
36324         this.el.slideIn(this.getSlideAnchor(), {
36325             callback: function(){
36326                 this.afterSlide();
36327                 this.initAutoHide();
36328                 Roo.get(document).on("click", this.slideInIf, this);
36329                 this.fireEvent("slideshow", this);
36330             },
36331             scope: this,
36332             block: true
36333         });
36334     },
36335
36336     afterSlideIn : function(){
36337         this.clearAutoHide();
36338         this.isSlid = false;
36339         this.clearMonitor();
36340         this.el.setStyle("z-index", "");
36341         if(this.collapseBtn){
36342             this.collapseBtn.show();
36343         }
36344         this.closeBtn.setStyle('display', this.closeBtnState);
36345         if(this.stickBtn){
36346             this.stickBtn.hide();
36347         }
36348         this.fireEvent("slidehide", this);
36349     },
36350
36351     slideIn : function(cb){
36352         if(!this.isSlid || this.el.hasActiveFx()){
36353             Roo.callback(cb);
36354             return;
36355         }
36356         this.isSlid = false;
36357         this.beforeSlide();
36358         this.el.slideOut(this.getSlideAnchor(), {
36359             callback: function(){
36360                 this.el.setLeftTop(-10000, -10000);
36361                 this.afterSlide();
36362                 this.afterSlideIn();
36363                 Roo.callback(cb);
36364             },
36365             scope: this,
36366             block: true
36367         });
36368     },
36369     
36370     slideInIf : function(e){
36371         if(!e.within(this.el)){
36372             this.slideIn();
36373         }
36374     },
36375
36376     animateCollapse : function(){
36377         this.beforeSlide();
36378         this.el.setStyle("z-index", 20000);
36379         var anchor = this.getSlideAnchor();
36380         this.el.slideOut(anchor, {
36381             callback : function(){
36382                 this.el.setStyle("z-index", "");
36383                 this.collapsedEl.slideIn(anchor, {duration:.3});
36384                 this.afterSlide();
36385                 this.el.setLocation(-10000,-10000);
36386                 this.el.hide();
36387                 this.fireEvent("collapsed", this);
36388             },
36389             scope: this,
36390             block: true
36391         });
36392     },
36393
36394     animateExpand : function(){
36395         this.beforeSlide();
36396         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36397         this.el.setStyle("z-index", 20000);
36398         this.collapsedEl.hide({
36399             duration:.1
36400         });
36401         this.el.slideIn(this.getSlideAnchor(), {
36402             callback : function(){
36403                 this.el.setStyle("z-index", "");
36404                 this.afterSlide();
36405                 if(this.split){
36406                     this.split.el.show();
36407                 }
36408                 this.fireEvent("invalidated", this);
36409                 this.fireEvent("expanded", this);
36410             },
36411             scope: this,
36412             block: true
36413         });
36414     },
36415
36416     anchors : {
36417         "west" : "left",
36418         "east" : "right",
36419         "north" : "top",
36420         "south" : "bottom"
36421     },
36422
36423     sanchors : {
36424         "west" : "l",
36425         "east" : "r",
36426         "north" : "t",
36427         "south" : "b"
36428     },
36429
36430     canchors : {
36431         "west" : "tl-tr",
36432         "east" : "tr-tl",
36433         "north" : "tl-bl",
36434         "south" : "bl-tl"
36435     },
36436
36437     getAnchor : function(){
36438         return this.anchors[this.position];
36439     },
36440
36441     getCollapseAnchor : function(){
36442         return this.canchors[this.position];
36443     },
36444
36445     getSlideAnchor : function(){
36446         return this.sanchors[this.position];
36447     },
36448
36449     getAlignAdj : function(){
36450         var cm = this.cmargins;
36451         switch(this.position){
36452             case "west":
36453                 return [0, 0];
36454             break;
36455             case "east":
36456                 return [0, 0];
36457             break;
36458             case "north":
36459                 return [0, 0];
36460             break;
36461             case "south":
36462                 return [0, 0];
36463             break;
36464         }
36465     },
36466
36467     getExpandAdj : function(){
36468         var c = this.collapsedEl, cm = this.cmargins;
36469         switch(this.position){
36470             case "west":
36471                 return [-(cm.right+c.getWidth()+cm.left), 0];
36472             break;
36473             case "east":
36474                 return [cm.right+c.getWidth()+cm.left, 0];
36475             break;
36476             case "north":
36477                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36478             break;
36479             case "south":
36480                 return [0, cm.top+cm.bottom+c.getHeight()];
36481             break;
36482         }
36483     }
36484 });/*
36485  * Based on:
36486  * Ext JS Library 1.1.1
36487  * Copyright(c) 2006-2007, Ext JS, LLC.
36488  *
36489  * Originally Released Under LGPL - original licence link has changed is not relivant.
36490  *
36491  * Fork - LGPL
36492  * <script type="text/javascript">
36493  */
36494 /*
36495  * These classes are private internal classes
36496  */
36497 Roo.bootstrap.layout.Center = function(config){
36498     config.region = "center";
36499     Roo.bootstrap.layout.Region.call(this, config);
36500     this.visible = true;
36501     this.minWidth = config.minWidth || 20;
36502     this.minHeight = config.minHeight || 20;
36503 };
36504
36505 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36506     hide : function(){
36507         // center panel can't be hidden
36508     },
36509     
36510     show : function(){
36511         // center panel can't be hidden
36512     },
36513     
36514     getMinWidth: function(){
36515         return this.minWidth;
36516     },
36517     
36518     getMinHeight: function(){
36519         return this.minHeight;
36520     }
36521 });
36522
36523
36524
36525
36526  
36527
36528
36529
36530
36531
36532 Roo.bootstrap.layout.North = function(config)
36533 {
36534     config.region = 'north';
36535     config.cursor = 'n-resize';
36536     
36537     Roo.bootstrap.layout.Split.call(this, config);
36538     
36539     
36540     if(this.split){
36541         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36542         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36543         this.split.el.addClass("roo-layout-split-v");
36544     }
36545     var size = config.initialSize || config.height;
36546     if(typeof size != "undefined"){
36547         this.el.setHeight(size);
36548     }
36549 };
36550 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36551 {
36552     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36553     
36554     
36555     
36556     getBox : function(){
36557         if(this.collapsed){
36558             return this.collapsedEl.getBox();
36559         }
36560         var box = this.el.getBox();
36561         if(this.split){
36562             box.height += this.split.el.getHeight();
36563         }
36564         return box;
36565     },
36566     
36567     updateBox : function(box){
36568         if(this.split && !this.collapsed){
36569             box.height -= this.split.el.getHeight();
36570             this.split.el.setLeft(box.x);
36571             this.split.el.setTop(box.y+box.height);
36572             this.split.el.setWidth(box.width);
36573         }
36574         if(this.collapsed){
36575             this.updateBody(box.width, null);
36576         }
36577         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36578     }
36579 });
36580
36581
36582
36583
36584
36585 Roo.bootstrap.layout.South = function(config){
36586     config.region = 'south';
36587     config.cursor = 's-resize';
36588     Roo.bootstrap.layout.Split.call(this, config);
36589     if(this.split){
36590         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36591         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36592         this.split.el.addClass("roo-layout-split-v");
36593     }
36594     var size = config.initialSize || config.height;
36595     if(typeof size != "undefined"){
36596         this.el.setHeight(size);
36597     }
36598 };
36599
36600 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36601     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36602     getBox : function(){
36603         if(this.collapsed){
36604             return this.collapsedEl.getBox();
36605         }
36606         var box = this.el.getBox();
36607         if(this.split){
36608             var sh = this.split.el.getHeight();
36609             box.height += sh;
36610             box.y -= sh;
36611         }
36612         return box;
36613     },
36614     
36615     updateBox : function(box){
36616         if(this.split && !this.collapsed){
36617             var sh = this.split.el.getHeight();
36618             box.height -= sh;
36619             box.y += sh;
36620             this.split.el.setLeft(box.x);
36621             this.split.el.setTop(box.y-sh);
36622             this.split.el.setWidth(box.width);
36623         }
36624         if(this.collapsed){
36625             this.updateBody(box.width, null);
36626         }
36627         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36628     }
36629 });
36630
36631 Roo.bootstrap.layout.East = function(config){
36632     config.region = "east";
36633     config.cursor = "e-resize";
36634     Roo.bootstrap.layout.Split.call(this, config);
36635     if(this.split){
36636         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36637         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36638         this.split.el.addClass("roo-layout-split-h");
36639     }
36640     var size = config.initialSize || config.width;
36641     if(typeof size != "undefined"){
36642         this.el.setWidth(size);
36643     }
36644 };
36645 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36646     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36647     getBox : function(){
36648         if(this.collapsed){
36649             return this.collapsedEl.getBox();
36650         }
36651         var box = this.el.getBox();
36652         if(this.split){
36653             var sw = this.split.el.getWidth();
36654             box.width += sw;
36655             box.x -= sw;
36656         }
36657         return box;
36658     },
36659
36660     updateBox : function(box){
36661         if(this.split && !this.collapsed){
36662             var sw = this.split.el.getWidth();
36663             box.width -= sw;
36664             this.split.el.setLeft(box.x);
36665             this.split.el.setTop(box.y);
36666             this.split.el.setHeight(box.height);
36667             box.x += sw;
36668         }
36669         if(this.collapsed){
36670             this.updateBody(null, box.height);
36671         }
36672         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36673     }
36674 });
36675
36676 Roo.bootstrap.layout.West = function(config){
36677     config.region = "west";
36678     config.cursor = "w-resize";
36679     
36680     Roo.bootstrap.layout.Split.call(this, config);
36681     if(this.split){
36682         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36683         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36684         this.split.el.addClass("roo-layout-split-h");
36685     }
36686     
36687 };
36688 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36689     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36690     
36691     onRender: function(ctr, pos)
36692     {
36693         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36694         var size = this.config.initialSize || this.config.width;
36695         if(typeof size != "undefined"){
36696             this.el.setWidth(size);
36697         }
36698     },
36699     
36700     getBox : function(){
36701         if(this.collapsed){
36702             return this.collapsedEl.getBox();
36703         }
36704         var box = this.el.getBox();
36705         if(this.split){
36706             box.width += this.split.el.getWidth();
36707         }
36708         return box;
36709     },
36710     
36711     updateBox : function(box){
36712         if(this.split && !this.collapsed){
36713             var sw = this.split.el.getWidth();
36714             box.width -= sw;
36715             this.split.el.setLeft(box.x+box.width);
36716             this.split.el.setTop(box.y);
36717             this.split.el.setHeight(box.height);
36718         }
36719         if(this.collapsed){
36720             this.updateBody(null, box.height);
36721         }
36722         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36723     }
36724 });
36725 Roo.namespace("Roo.bootstrap.panel");/*
36726  * Based on:
36727  * Ext JS Library 1.1.1
36728  * Copyright(c) 2006-2007, Ext JS, LLC.
36729  *
36730  * Originally Released Under LGPL - original licence link has changed is not relivant.
36731  *
36732  * Fork - LGPL
36733  * <script type="text/javascript">
36734  */
36735 /**
36736  * @class Roo.ContentPanel
36737  * @extends Roo.util.Observable
36738  * A basic ContentPanel element.
36739  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36740  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36741  * @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
36742  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36743  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36744  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36745  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36746  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36747  * @cfg {String} title          The title for this panel
36748  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36749  * @cfg {String} url            Calls {@link #setUrl} with this value
36750  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36751  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36752  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36753  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36754  * @cfg {Boolean} badges render the badges
36755
36756  * @constructor
36757  * Create a new ContentPanel.
36758  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36759  * @param {String/Object} config A string to set only the title or a config object
36760  * @param {String} content (optional) Set the HTML content for this panel
36761  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36762  */
36763 Roo.bootstrap.panel.Content = function( config){
36764     
36765     this.tpl = config.tpl || false;
36766     
36767     var el = config.el;
36768     var content = config.content;
36769
36770     if(config.autoCreate){ // xtype is available if this is called from factory
36771         el = Roo.id();
36772     }
36773     this.el = Roo.get(el);
36774     if(!this.el && config && config.autoCreate){
36775         if(typeof config.autoCreate == "object"){
36776             if(!config.autoCreate.id){
36777                 config.autoCreate.id = config.id||el;
36778             }
36779             this.el = Roo.DomHelper.append(document.body,
36780                         config.autoCreate, true);
36781         }else{
36782             var elcfg =  {   tag: "div",
36783                             cls: "roo-layout-inactive-content",
36784                             id: config.id||el
36785                             };
36786             if (config.html) {
36787                 elcfg.html = config.html;
36788                 
36789             }
36790                         
36791             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36792         }
36793     } 
36794     this.closable = false;
36795     this.loaded = false;
36796     this.active = false;
36797    
36798       
36799     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36800         
36801         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36802         
36803         this.wrapEl = this.el; //this.el.wrap();
36804         var ti = [];
36805         if (config.toolbar.items) {
36806             ti = config.toolbar.items ;
36807             delete config.toolbar.items ;
36808         }
36809         
36810         var nitems = [];
36811         this.toolbar.render(this.wrapEl, 'before');
36812         for(var i =0;i < ti.length;i++) {
36813           //  Roo.log(['add child', items[i]]);
36814             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36815         }
36816         this.toolbar.items = nitems;
36817         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36818         delete config.toolbar;
36819         
36820     }
36821     /*
36822     // xtype created footer. - not sure if will work as we normally have to render first..
36823     if (this.footer && !this.footer.el && this.footer.xtype) {
36824         if (!this.wrapEl) {
36825             this.wrapEl = this.el.wrap();
36826         }
36827     
36828         this.footer.container = this.wrapEl.createChild();
36829          
36830         this.footer = Roo.factory(this.footer, Roo);
36831         
36832     }
36833     */
36834     
36835      if(typeof config == "string"){
36836         this.title = config;
36837     }else{
36838         Roo.apply(this, config);
36839     }
36840     
36841     if(this.resizeEl){
36842         this.resizeEl = Roo.get(this.resizeEl, true);
36843     }else{
36844         this.resizeEl = this.el;
36845     }
36846     // handle view.xtype
36847     
36848  
36849     
36850     
36851     this.addEvents({
36852         /**
36853          * @event activate
36854          * Fires when this panel is activated. 
36855          * @param {Roo.ContentPanel} this
36856          */
36857         "activate" : true,
36858         /**
36859          * @event deactivate
36860          * Fires when this panel is activated. 
36861          * @param {Roo.ContentPanel} this
36862          */
36863         "deactivate" : true,
36864
36865         /**
36866          * @event resize
36867          * Fires when this panel is resized if fitToFrame is true.
36868          * @param {Roo.ContentPanel} this
36869          * @param {Number} width The width after any component adjustments
36870          * @param {Number} height The height after any component adjustments
36871          */
36872         "resize" : true,
36873         
36874          /**
36875          * @event render
36876          * Fires when this tab is created
36877          * @param {Roo.ContentPanel} this
36878          */
36879         "render" : true
36880         
36881         
36882         
36883     });
36884     
36885
36886     
36887     
36888     if(this.autoScroll){
36889         this.resizeEl.setStyle("overflow", "auto");
36890     } else {
36891         // fix randome scrolling
36892         //this.el.on('scroll', function() {
36893         //    Roo.log('fix random scolling');
36894         //    this.scrollTo('top',0); 
36895         //});
36896     }
36897     content = content || this.content;
36898     if(content){
36899         this.setContent(content);
36900     }
36901     if(config && config.url){
36902         this.setUrl(this.url, this.params, this.loadOnce);
36903     }
36904     
36905     
36906     
36907     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36908     
36909     if (this.view && typeof(this.view.xtype) != 'undefined') {
36910         this.view.el = this.el.appendChild(document.createElement("div"));
36911         this.view = Roo.factory(this.view); 
36912         this.view.render  &&  this.view.render(false, '');  
36913     }
36914     
36915     
36916     this.fireEvent('render', this);
36917 };
36918
36919 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36920     
36921     tabTip : '',
36922     
36923     setRegion : function(region){
36924         this.region = region;
36925         this.setActiveClass(region && !this.background);
36926     },
36927     
36928     
36929     setActiveClass: function(state)
36930     {
36931         if(state){
36932            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36933            this.el.setStyle('position','relative');
36934         }else{
36935            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36936            this.el.setStyle('position', 'absolute');
36937         } 
36938     },
36939     
36940     /**
36941      * Returns the toolbar for this Panel if one was configured. 
36942      * @return {Roo.Toolbar} 
36943      */
36944     getToolbar : function(){
36945         return this.toolbar;
36946     },
36947     
36948     setActiveState : function(active)
36949     {
36950         this.active = active;
36951         this.setActiveClass(active);
36952         if(!active){
36953             if(this.fireEvent("deactivate", this) === false){
36954                 return false;
36955             }
36956             return true;
36957         }
36958         this.fireEvent("activate", this);
36959         return true;
36960     },
36961     /**
36962      * Updates this panel's element
36963      * @param {String} content The new content
36964      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36965     */
36966     setContent : function(content, loadScripts){
36967         this.el.update(content, loadScripts);
36968     },
36969
36970     ignoreResize : function(w, h){
36971         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36972             return true;
36973         }else{
36974             this.lastSize = {width: w, height: h};
36975             return false;
36976         }
36977     },
36978     /**
36979      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36980      * @return {Roo.UpdateManager} The UpdateManager
36981      */
36982     getUpdateManager : function(){
36983         return this.el.getUpdateManager();
36984     },
36985      /**
36986      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36987      * @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:
36988 <pre><code>
36989 panel.load({
36990     url: "your-url.php",
36991     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36992     callback: yourFunction,
36993     scope: yourObject, //(optional scope)
36994     discardUrl: false,
36995     nocache: false,
36996     text: "Loading...",
36997     timeout: 30,
36998     scripts: false
36999 });
37000 </code></pre>
37001      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37002      * 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.
37003      * @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}
37004      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37005      * @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.
37006      * @return {Roo.ContentPanel} this
37007      */
37008     load : function(){
37009         var um = this.el.getUpdateManager();
37010         um.update.apply(um, arguments);
37011         return this;
37012     },
37013
37014
37015     /**
37016      * 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.
37017      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37018      * @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)
37019      * @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)
37020      * @return {Roo.UpdateManager} The UpdateManager
37021      */
37022     setUrl : function(url, params, loadOnce){
37023         if(this.refreshDelegate){
37024             this.removeListener("activate", this.refreshDelegate);
37025         }
37026         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37027         this.on("activate", this.refreshDelegate);
37028         return this.el.getUpdateManager();
37029     },
37030     
37031     _handleRefresh : function(url, params, loadOnce){
37032         if(!loadOnce || !this.loaded){
37033             var updater = this.el.getUpdateManager();
37034             updater.update(url, params, this._setLoaded.createDelegate(this));
37035         }
37036     },
37037     
37038     _setLoaded : function(){
37039         this.loaded = true;
37040     }, 
37041     
37042     /**
37043      * Returns this panel's id
37044      * @return {String} 
37045      */
37046     getId : function(){
37047         return this.el.id;
37048     },
37049     
37050     /** 
37051      * Returns this panel's element - used by regiosn to add.
37052      * @return {Roo.Element} 
37053      */
37054     getEl : function(){
37055         return this.wrapEl || this.el;
37056     },
37057     
37058    
37059     
37060     adjustForComponents : function(width, height)
37061     {
37062         //Roo.log('adjustForComponents ');
37063         if(this.resizeEl != this.el){
37064             width -= this.el.getFrameWidth('lr');
37065             height -= this.el.getFrameWidth('tb');
37066         }
37067         if(this.toolbar){
37068             var te = this.toolbar.getEl();
37069             te.setWidth(width);
37070             height -= te.getHeight();
37071         }
37072         if(this.footer){
37073             var te = this.footer.getEl();
37074             te.setWidth(width);
37075             height -= te.getHeight();
37076         }
37077         
37078         
37079         if(this.adjustments){
37080             width += this.adjustments[0];
37081             height += this.adjustments[1];
37082         }
37083         return {"width": width, "height": height};
37084     },
37085     
37086     setSize : function(width, height){
37087         if(this.fitToFrame && !this.ignoreResize(width, height)){
37088             if(this.fitContainer && this.resizeEl != this.el){
37089                 this.el.setSize(width, height);
37090             }
37091             var size = this.adjustForComponents(width, height);
37092             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37093             this.fireEvent('resize', this, size.width, size.height);
37094         }
37095     },
37096     
37097     /**
37098      * Returns this panel's title
37099      * @return {String} 
37100      */
37101     getTitle : function(){
37102         
37103         if (typeof(this.title) != 'object') {
37104             return this.title;
37105         }
37106         
37107         var t = '';
37108         for (var k in this.title) {
37109             if (!this.title.hasOwnProperty(k)) {
37110                 continue;
37111             }
37112             
37113             if (k.indexOf('-') >= 0) {
37114                 var s = k.split('-');
37115                 for (var i = 0; i<s.length; i++) {
37116                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37117                 }
37118             } else {
37119                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37120             }
37121         }
37122         return t;
37123     },
37124     
37125     /**
37126      * Set this panel's title
37127      * @param {String} title
37128      */
37129     setTitle : function(title){
37130         this.title = title;
37131         if(this.region){
37132             this.region.updatePanelTitle(this, title);
37133         }
37134     },
37135     
37136     /**
37137      * Returns true is this panel was configured to be closable
37138      * @return {Boolean} 
37139      */
37140     isClosable : function(){
37141         return this.closable;
37142     },
37143     
37144     beforeSlide : function(){
37145         this.el.clip();
37146         this.resizeEl.clip();
37147     },
37148     
37149     afterSlide : function(){
37150         this.el.unclip();
37151         this.resizeEl.unclip();
37152     },
37153     
37154     /**
37155      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37156      *   Will fail silently if the {@link #setUrl} method has not been called.
37157      *   This does not activate the panel, just updates its content.
37158      */
37159     refresh : function(){
37160         if(this.refreshDelegate){
37161            this.loaded = false;
37162            this.refreshDelegate();
37163         }
37164     },
37165     
37166     /**
37167      * Destroys this panel
37168      */
37169     destroy : function(){
37170         this.el.removeAllListeners();
37171         var tempEl = document.createElement("span");
37172         tempEl.appendChild(this.el.dom);
37173         tempEl.innerHTML = "";
37174         this.el.remove();
37175         this.el = null;
37176     },
37177     
37178     /**
37179      * form - if the content panel contains a form - this is a reference to it.
37180      * @type {Roo.form.Form}
37181      */
37182     form : false,
37183     /**
37184      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37185      *    This contains a reference to it.
37186      * @type {Roo.View}
37187      */
37188     view : false,
37189     
37190       /**
37191      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37192      * <pre><code>
37193
37194 layout.addxtype({
37195        xtype : 'Form',
37196        items: [ .... ]
37197    }
37198 );
37199
37200 </code></pre>
37201      * @param {Object} cfg Xtype definition of item to add.
37202      */
37203     
37204     
37205     getChildContainer: function () {
37206         return this.getEl();
37207     }
37208     
37209     
37210     /*
37211         var  ret = new Roo.factory(cfg);
37212         return ret;
37213         
37214         
37215         // add form..
37216         if (cfg.xtype.match(/^Form$/)) {
37217             
37218             var el;
37219             //if (this.footer) {
37220             //    el = this.footer.container.insertSibling(false, 'before');
37221             //} else {
37222                 el = this.el.createChild();
37223             //}
37224
37225             this.form = new  Roo.form.Form(cfg);
37226             
37227             
37228             if ( this.form.allItems.length) {
37229                 this.form.render(el.dom);
37230             }
37231             return this.form;
37232         }
37233         // should only have one of theses..
37234         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37235             // views.. should not be just added - used named prop 'view''
37236             
37237             cfg.el = this.el.appendChild(document.createElement("div"));
37238             // factory?
37239             
37240             var ret = new Roo.factory(cfg);
37241              
37242              ret.render && ret.render(false, ''); // render blank..
37243             this.view = ret;
37244             return ret;
37245         }
37246         return false;
37247     }
37248     \*/
37249 });
37250  
37251 /**
37252  * @class Roo.bootstrap.panel.Grid
37253  * @extends Roo.bootstrap.panel.Content
37254  * @constructor
37255  * Create a new GridPanel.
37256  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37257  * @param {Object} config A the config object
37258   
37259  */
37260
37261
37262
37263 Roo.bootstrap.panel.Grid = function(config)
37264 {
37265     
37266       
37267     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37268         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37269
37270     config.el = this.wrapper;
37271     //this.el = this.wrapper;
37272     
37273       if (config.container) {
37274         // ctor'ed from a Border/panel.grid
37275         
37276         
37277         this.wrapper.setStyle("overflow", "hidden");
37278         this.wrapper.addClass('roo-grid-container');
37279
37280     }
37281     
37282     
37283     if(config.toolbar){
37284         var tool_el = this.wrapper.createChild();    
37285         this.toolbar = Roo.factory(config.toolbar);
37286         var ti = [];
37287         if (config.toolbar.items) {
37288             ti = config.toolbar.items ;
37289             delete config.toolbar.items ;
37290         }
37291         
37292         var nitems = [];
37293         this.toolbar.render(tool_el);
37294         for(var i =0;i < ti.length;i++) {
37295           //  Roo.log(['add child', items[i]]);
37296             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37297         }
37298         this.toolbar.items = nitems;
37299         
37300         delete config.toolbar;
37301     }
37302     
37303     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37304     config.grid.scrollBody = true;;
37305     config.grid.monitorWindowResize = false; // turn off autosizing
37306     config.grid.autoHeight = false;
37307     config.grid.autoWidth = false;
37308     
37309     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37310     
37311     if (config.background) {
37312         // render grid on panel activation (if panel background)
37313         this.on('activate', function(gp) {
37314             if (!gp.grid.rendered) {
37315                 gp.grid.render(this.wrapper);
37316                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37317             }
37318         });
37319             
37320     } else {
37321         this.grid.render(this.wrapper);
37322         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37323
37324     }
37325     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37326     // ??? needed ??? config.el = this.wrapper;
37327     
37328     
37329     
37330   
37331     // xtype created footer. - not sure if will work as we normally have to render first..
37332     if (this.footer && !this.footer.el && this.footer.xtype) {
37333         
37334         var ctr = this.grid.getView().getFooterPanel(true);
37335         this.footer.dataSource = this.grid.dataSource;
37336         this.footer = Roo.factory(this.footer, Roo);
37337         this.footer.render(ctr);
37338         
37339     }
37340     
37341     
37342     
37343     
37344      
37345 };
37346
37347 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37348     getId : function(){
37349         return this.grid.id;
37350     },
37351     
37352     /**
37353      * Returns the grid for this panel
37354      * @return {Roo.bootstrap.Table} 
37355      */
37356     getGrid : function(){
37357         return this.grid;    
37358     },
37359     
37360     setSize : function(width, height){
37361         if(!this.ignoreResize(width, height)){
37362             var grid = this.grid;
37363             var size = this.adjustForComponents(width, height);
37364             var gridel = grid.getGridEl();
37365             gridel.setSize(size.width, size.height);
37366             /*
37367             var thd = grid.getGridEl().select('thead',true).first();
37368             var tbd = grid.getGridEl().select('tbody', true).first();
37369             if (tbd) {
37370                 tbd.setSize(width, height - thd.getHeight());
37371             }
37372             */
37373             grid.autoSize();
37374         }
37375     },
37376      
37377     
37378     
37379     beforeSlide : function(){
37380         this.grid.getView().scroller.clip();
37381     },
37382     
37383     afterSlide : function(){
37384         this.grid.getView().scroller.unclip();
37385     },
37386     
37387     destroy : function(){
37388         this.grid.destroy();
37389         delete this.grid;
37390         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37391     }
37392 });
37393
37394 /**
37395  * @class Roo.bootstrap.panel.Nest
37396  * @extends Roo.bootstrap.panel.Content
37397  * @constructor
37398  * Create a new Panel, that can contain a layout.Border.
37399  * 
37400  * 
37401  * @param {Roo.BorderLayout} layout The layout for this panel
37402  * @param {String/Object} config A string to set only the title or a config object
37403  */
37404 Roo.bootstrap.panel.Nest = function(config)
37405 {
37406     // construct with only one argument..
37407     /* FIXME - implement nicer consturctors
37408     if (layout.layout) {
37409         config = layout;
37410         layout = config.layout;
37411         delete config.layout;
37412     }
37413     if (layout.xtype && !layout.getEl) {
37414         // then layout needs constructing..
37415         layout = Roo.factory(layout, Roo);
37416     }
37417     */
37418     
37419     config.el =  config.layout.getEl();
37420     
37421     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37422     
37423     config.layout.monitorWindowResize = false; // turn off autosizing
37424     this.layout = config.layout;
37425     this.layout.getEl().addClass("roo-layout-nested-layout");
37426     
37427     
37428     
37429     
37430 };
37431
37432 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37433
37434     setSize : function(width, height){
37435         if(!this.ignoreResize(width, height)){
37436             var size = this.adjustForComponents(width, height);
37437             var el = this.layout.getEl();
37438             if (size.height < 1) {
37439                 el.setWidth(size.width);   
37440             } else {
37441                 el.setSize(size.width, size.height);
37442             }
37443             var touch = el.dom.offsetWidth;
37444             this.layout.layout();
37445             // ie requires a double layout on the first pass
37446             if(Roo.isIE && !this.initialized){
37447                 this.initialized = true;
37448                 this.layout.layout();
37449             }
37450         }
37451     },
37452     
37453     // activate all subpanels if not currently active..
37454     
37455     setActiveState : function(active){
37456         this.active = active;
37457         this.setActiveClass(active);
37458         
37459         if(!active){
37460             this.fireEvent("deactivate", this);
37461             return;
37462         }
37463         
37464         this.fireEvent("activate", this);
37465         // not sure if this should happen before or after..
37466         if (!this.layout) {
37467             return; // should not happen..
37468         }
37469         var reg = false;
37470         for (var r in this.layout.regions) {
37471             reg = this.layout.getRegion(r);
37472             if (reg.getActivePanel()) {
37473                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37474                 reg.setActivePanel(reg.getActivePanel());
37475                 continue;
37476             }
37477             if (!reg.panels.length) {
37478                 continue;
37479             }
37480             reg.showPanel(reg.getPanel(0));
37481         }
37482         
37483         
37484         
37485         
37486     },
37487     
37488     /**
37489      * Returns the nested BorderLayout for this panel
37490      * @return {Roo.BorderLayout} 
37491      */
37492     getLayout : function(){
37493         return this.layout;
37494     },
37495     
37496      /**
37497      * Adds a xtype elements to the layout of the nested panel
37498      * <pre><code>
37499
37500 panel.addxtype({
37501        xtype : 'ContentPanel',
37502        region: 'west',
37503        items: [ .... ]
37504    }
37505 );
37506
37507 panel.addxtype({
37508         xtype : 'NestedLayoutPanel',
37509         region: 'west',
37510         layout: {
37511            center: { },
37512            west: { }   
37513         },
37514         items : [ ... list of content panels or nested layout panels.. ]
37515    }
37516 );
37517 </code></pre>
37518      * @param {Object} cfg Xtype definition of item to add.
37519      */
37520     addxtype : function(cfg) {
37521         return this.layout.addxtype(cfg);
37522     
37523     }
37524 });        /*
37525  * Based on:
37526  * Ext JS Library 1.1.1
37527  * Copyright(c) 2006-2007, Ext JS, LLC.
37528  *
37529  * Originally Released Under LGPL - original licence link has changed is not relivant.
37530  *
37531  * Fork - LGPL
37532  * <script type="text/javascript">
37533  */
37534 /**
37535  * @class Roo.TabPanel
37536  * @extends Roo.util.Observable
37537  * A lightweight tab container.
37538  * <br><br>
37539  * Usage:
37540  * <pre><code>
37541 // basic tabs 1, built from existing content
37542 var tabs = new Roo.TabPanel("tabs1");
37543 tabs.addTab("script", "View Script");
37544 tabs.addTab("markup", "View Markup");
37545 tabs.activate("script");
37546
37547 // more advanced tabs, built from javascript
37548 var jtabs = new Roo.TabPanel("jtabs");
37549 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37550
37551 // set up the UpdateManager
37552 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37553 var updater = tab2.getUpdateManager();
37554 updater.setDefaultUrl("ajax1.htm");
37555 tab2.on('activate', updater.refresh, updater, true);
37556
37557 // Use setUrl for Ajax loading
37558 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37559 tab3.setUrl("ajax2.htm", null, true);
37560
37561 // Disabled tab
37562 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37563 tab4.disable();
37564
37565 jtabs.activate("jtabs-1");
37566  * </code></pre>
37567  * @constructor
37568  * Create a new TabPanel.
37569  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37570  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37571  */
37572 Roo.bootstrap.panel.Tabs = function(config){
37573     /**
37574     * The container element for this TabPanel.
37575     * @type Roo.Element
37576     */
37577     this.el = Roo.get(config.el);
37578     delete config.el;
37579     if(config){
37580         if(typeof config == "boolean"){
37581             this.tabPosition = config ? "bottom" : "top";
37582         }else{
37583             Roo.apply(this, config);
37584         }
37585     }
37586     
37587     if(this.tabPosition == "bottom"){
37588         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37589         this.el.addClass("roo-tabs-bottom");
37590     }
37591     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37592     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37593     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37594     if(Roo.isIE){
37595         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37596     }
37597     if(this.tabPosition != "bottom"){
37598         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37599          * @type Roo.Element
37600          */
37601         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37602         this.el.addClass("roo-tabs-top");
37603     }
37604     this.items = [];
37605
37606     this.bodyEl.setStyle("position", "relative");
37607
37608     this.active = null;
37609     this.activateDelegate = this.activate.createDelegate(this);
37610
37611     this.addEvents({
37612         /**
37613          * @event tabchange
37614          * Fires when the active tab changes
37615          * @param {Roo.TabPanel} this
37616          * @param {Roo.TabPanelItem} activePanel The new active tab
37617          */
37618         "tabchange": true,
37619         /**
37620          * @event beforetabchange
37621          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37622          * @param {Roo.TabPanel} this
37623          * @param {Object} e Set cancel to true on this object to cancel the tab change
37624          * @param {Roo.TabPanelItem} tab The tab being changed to
37625          */
37626         "beforetabchange" : true
37627     });
37628
37629     Roo.EventManager.onWindowResize(this.onResize, this);
37630     this.cpad = this.el.getPadding("lr");
37631     this.hiddenCount = 0;
37632
37633
37634     // toolbar on the tabbar support...
37635     if (this.toolbar) {
37636         alert("no toolbar support yet");
37637         this.toolbar  = false;
37638         /*
37639         var tcfg = this.toolbar;
37640         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37641         this.toolbar = new Roo.Toolbar(tcfg);
37642         if (Roo.isSafari) {
37643             var tbl = tcfg.container.child('table', true);
37644             tbl.setAttribute('width', '100%');
37645         }
37646         */
37647         
37648     }
37649    
37650
37651
37652     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37653 };
37654
37655 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37656     /*
37657      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37658      */
37659     tabPosition : "top",
37660     /*
37661      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37662      */
37663     currentTabWidth : 0,
37664     /*
37665      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37666      */
37667     minTabWidth : 40,
37668     /*
37669      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37670      */
37671     maxTabWidth : 250,
37672     /*
37673      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37674      */
37675     preferredTabWidth : 175,
37676     /*
37677      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37678      */
37679     resizeTabs : false,
37680     /*
37681      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37682      */
37683     monitorResize : true,
37684     /*
37685      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37686      */
37687     toolbar : false,
37688
37689     /**
37690      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37691      * @param {String} id The id of the div to use <b>or create</b>
37692      * @param {String} text The text for the tab
37693      * @param {String} content (optional) Content to put in the TabPanelItem body
37694      * @param {Boolean} closable (optional) True to create a close icon on the tab
37695      * @return {Roo.TabPanelItem} The created TabPanelItem
37696      */
37697     addTab : function(id, text, content, closable, tpl)
37698     {
37699         var item = new Roo.bootstrap.panel.TabItem({
37700             panel: this,
37701             id : id,
37702             text : text,
37703             closable : closable,
37704             tpl : tpl
37705         });
37706         this.addTabItem(item);
37707         if(content){
37708             item.setContent(content);
37709         }
37710         return item;
37711     },
37712
37713     /**
37714      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37715      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37716      * @return {Roo.TabPanelItem}
37717      */
37718     getTab : function(id){
37719         return this.items[id];
37720     },
37721
37722     /**
37723      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37724      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37725      */
37726     hideTab : function(id){
37727         var t = this.items[id];
37728         if(!t.isHidden()){
37729            t.setHidden(true);
37730            this.hiddenCount++;
37731            this.autoSizeTabs();
37732         }
37733     },
37734
37735     /**
37736      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37737      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37738      */
37739     unhideTab : function(id){
37740         var t = this.items[id];
37741         if(t.isHidden()){
37742            t.setHidden(false);
37743            this.hiddenCount--;
37744            this.autoSizeTabs();
37745         }
37746     },
37747
37748     /**
37749      * Adds an existing {@link Roo.TabPanelItem}.
37750      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37751      */
37752     addTabItem : function(item){
37753         this.items[item.id] = item;
37754         this.items.push(item);
37755       //  if(this.resizeTabs){
37756     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37757   //         this.autoSizeTabs();
37758 //        }else{
37759 //            item.autoSize();
37760        // }
37761     },
37762
37763     /**
37764      * Removes a {@link Roo.TabPanelItem}.
37765      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37766      */
37767     removeTab : function(id){
37768         var items = this.items;
37769         var tab = items[id];
37770         if(!tab) { return; }
37771         var index = items.indexOf(tab);
37772         if(this.active == tab && items.length > 1){
37773             var newTab = this.getNextAvailable(index);
37774             if(newTab) {
37775                 newTab.activate();
37776             }
37777         }
37778         this.stripEl.dom.removeChild(tab.pnode.dom);
37779         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37780             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37781         }
37782         items.splice(index, 1);
37783         delete this.items[tab.id];
37784         tab.fireEvent("close", tab);
37785         tab.purgeListeners();
37786         this.autoSizeTabs();
37787     },
37788
37789     getNextAvailable : function(start){
37790         var items = this.items;
37791         var index = start;
37792         // look for a next tab that will slide over to
37793         // replace the one being removed
37794         while(index < items.length){
37795             var item = items[++index];
37796             if(item && !item.isHidden()){
37797                 return item;
37798             }
37799         }
37800         // if one isn't found select the previous tab (on the left)
37801         index = start;
37802         while(index >= 0){
37803             var item = items[--index];
37804             if(item && !item.isHidden()){
37805                 return item;
37806             }
37807         }
37808         return null;
37809     },
37810
37811     /**
37812      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37813      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37814      */
37815     disableTab : function(id){
37816         var tab = this.items[id];
37817         if(tab && this.active != tab){
37818             tab.disable();
37819         }
37820     },
37821
37822     /**
37823      * Enables a {@link Roo.TabPanelItem} that is disabled.
37824      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37825      */
37826     enableTab : function(id){
37827         var tab = this.items[id];
37828         tab.enable();
37829     },
37830
37831     /**
37832      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37833      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37834      * @return {Roo.TabPanelItem} The TabPanelItem.
37835      */
37836     activate : function(id){
37837         var tab = this.items[id];
37838         if(!tab){
37839             return null;
37840         }
37841         if(tab == this.active || tab.disabled){
37842             return tab;
37843         }
37844         var e = {};
37845         this.fireEvent("beforetabchange", this, e, tab);
37846         if(e.cancel !== true && !tab.disabled){
37847             if(this.active){
37848                 this.active.hide();
37849             }
37850             this.active = this.items[id];
37851             this.active.show();
37852             this.fireEvent("tabchange", this, this.active);
37853         }
37854         return tab;
37855     },
37856
37857     /**
37858      * Gets the active {@link Roo.TabPanelItem}.
37859      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37860      */
37861     getActiveTab : function(){
37862         return this.active;
37863     },
37864
37865     /**
37866      * Updates the tab body element to fit the height of the container element
37867      * for overflow scrolling
37868      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37869      */
37870     syncHeight : function(targetHeight){
37871         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37872         var bm = this.bodyEl.getMargins();
37873         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37874         this.bodyEl.setHeight(newHeight);
37875         return newHeight;
37876     },
37877
37878     onResize : function(){
37879         if(this.monitorResize){
37880             this.autoSizeTabs();
37881         }
37882     },
37883
37884     /**
37885      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37886      */
37887     beginUpdate : function(){
37888         this.updating = true;
37889     },
37890
37891     /**
37892      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37893      */
37894     endUpdate : function(){
37895         this.updating = false;
37896         this.autoSizeTabs();
37897     },
37898
37899     /**
37900      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37901      */
37902     autoSizeTabs : function(){
37903         var count = this.items.length;
37904         var vcount = count - this.hiddenCount;
37905         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37906             return;
37907         }
37908         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37909         var availWidth = Math.floor(w / vcount);
37910         var b = this.stripBody;
37911         if(b.getWidth() > w){
37912             var tabs = this.items;
37913             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37914             if(availWidth < this.minTabWidth){
37915                 /*if(!this.sleft){    // incomplete scrolling code
37916                     this.createScrollButtons();
37917                 }
37918                 this.showScroll();
37919                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37920             }
37921         }else{
37922             if(this.currentTabWidth < this.preferredTabWidth){
37923                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37924             }
37925         }
37926     },
37927
37928     /**
37929      * Returns the number of tabs in this TabPanel.
37930      * @return {Number}
37931      */
37932      getCount : function(){
37933          return this.items.length;
37934      },
37935
37936     /**
37937      * Resizes all the tabs to the passed width
37938      * @param {Number} The new width
37939      */
37940     setTabWidth : function(width){
37941         this.currentTabWidth = width;
37942         for(var i = 0, len = this.items.length; i < len; i++) {
37943                 if(!this.items[i].isHidden()) {
37944                 this.items[i].setWidth(width);
37945             }
37946         }
37947     },
37948
37949     /**
37950      * Destroys this TabPanel
37951      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37952      */
37953     destroy : function(removeEl){
37954         Roo.EventManager.removeResizeListener(this.onResize, this);
37955         for(var i = 0, len = this.items.length; i < len; i++){
37956             this.items[i].purgeListeners();
37957         }
37958         if(removeEl === true){
37959             this.el.update("");
37960             this.el.remove();
37961         }
37962     },
37963     
37964     createStrip : function(container)
37965     {
37966         var strip = document.createElement("nav");
37967         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37968         container.appendChild(strip);
37969         return strip;
37970     },
37971     
37972     createStripList : function(strip)
37973     {
37974         // div wrapper for retard IE
37975         // returns the "tr" element.
37976         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37977         //'<div class="x-tabs-strip-wrap">'+
37978           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37979           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37980         return strip.firstChild; //.firstChild.firstChild.firstChild;
37981     },
37982     createBody : function(container)
37983     {
37984         var body = document.createElement("div");
37985         Roo.id(body, "tab-body");
37986         //Roo.fly(body).addClass("x-tabs-body");
37987         Roo.fly(body).addClass("tab-content");
37988         container.appendChild(body);
37989         return body;
37990     },
37991     createItemBody :function(bodyEl, id){
37992         var body = Roo.getDom(id);
37993         if(!body){
37994             body = document.createElement("div");
37995             body.id = id;
37996         }
37997         //Roo.fly(body).addClass("x-tabs-item-body");
37998         Roo.fly(body).addClass("tab-pane");
37999          bodyEl.insertBefore(body, bodyEl.firstChild);
38000         return body;
38001     },
38002     /** @private */
38003     createStripElements :  function(stripEl, text, closable, tpl)
38004     {
38005         var td = document.createElement("li"); // was td..
38006         
38007         
38008         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38009         
38010         
38011         stripEl.appendChild(td);
38012         /*if(closable){
38013             td.className = "x-tabs-closable";
38014             if(!this.closeTpl){
38015                 this.closeTpl = new Roo.Template(
38016                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38017                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38018                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38019                 );
38020             }
38021             var el = this.closeTpl.overwrite(td, {"text": text});
38022             var close = el.getElementsByTagName("div")[0];
38023             var inner = el.getElementsByTagName("em")[0];
38024             return {"el": el, "close": close, "inner": inner};
38025         } else {
38026         */
38027         // not sure what this is..
38028 //            if(!this.tabTpl){
38029                 //this.tabTpl = new Roo.Template(
38030                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38031                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38032                 //);
38033 //                this.tabTpl = new Roo.Template(
38034 //                   '<a href="#">' +
38035 //                   '<span unselectable="on"' +
38036 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38037 //                            ' >{text}</span></a>'
38038 //                );
38039 //                
38040 //            }
38041
38042
38043             var template = tpl || this.tabTpl || false;
38044             
38045             if(!template){
38046                 
38047                 template = new Roo.Template(
38048                    '<a href="#">' +
38049                    '<span unselectable="on"' +
38050                             (this.disableTooltips ? '' : ' title="{text}"') +
38051                             ' >{text}</span></a>'
38052                 );
38053             }
38054             
38055             switch (typeof(template)) {
38056                 case 'object' :
38057                     break;
38058                 case 'string' :
38059                     template = new Roo.Template(template);
38060                     break;
38061                 default :
38062                     break;
38063             }
38064             
38065             var el = template.overwrite(td, {"text": text});
38066             
38067             var inner = el.getElementsByTagName("span")[0];
38068             
38069             return {"el": el, "inner": inner};
38070             
38071     }
38072         
38073     
38074 });
38075
38076 /**
38077  * @class Roo.TabPanelItem
38078  * @extends Roo.util.Observable
38079  * Represents an individual item (tab plus body) in a TabPanel.
38080  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38081  * @param {String} id The id of this TabPanelItem
38082  * @param {String} text The text for the tab of this TabPanelItem
38083  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38084  */
38085 Roo.bootstrap.panel.TabItem = function(config){
38086     /**
38087      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38088      * @type Roo.TabPanel
38089      */
38090     this.tabPanel = config.panel;
38091     /**
38092      * The id for this TabPanelItem
38093      * @type String
38094      */
38095     this.id = config.id;
38096     /** @private */
38097     this.disabled = false;
38098     /** @private */
38099     this.text = config.text;
38100     /** @private */
38101     this.loaded = false;
38102     this.closable = config.closable;
38103
38104     /**
38105      * The body element for this TabPanelItem.
38106      * @type Roo.Element
38107      */
38108     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38109     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38110     this.bodyEl.setStyle("display", "block");
38111     this.bodyEl.setStyle("zoom", "1");
38112     //this.hideAction();
38113
38114     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38115     /** @private */
38116     this.el = Roo.get(els.el);
38117     this.inner = Roo.get(els.inner, true);
38118     this.textEl = Roo.get(this.el.dom.firstChild, true);
38119     this.pnode = Roo.get(els.el.parentNode, true);
38120 //    this.el.on("mousedown", this.onTabMouseDown, this);
38121     this.el.on("click", this.onTabClick, this);
38122     /** @private */
38123     if(config.closable){
38124         var c = Roo.get(els.close, true);
38125         c.dom.title = this.closeText;
38126         c.addClassOnOver("close-over");
38127         c.on("click", this.closeClick, this);
38128      }
38129
38130     this.addEvents({
38131          /**
38132          * @event activate
38133          * Fires when this tab becomes the active tab.
38134          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38135          * @param {Roo.TabPanelItem} this
38136          */
38137         "activate": true,
38138         /**
38139          * @event beforeclose
38140          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38141          * @param {Roo.TabPanelItem} this
38142          * @param {Object} e Set cancel to true on this object to cancel the close.
38143          */
38144         "beforeclose": true,
38145         /**
38146          * @event close
38147          * Fires when this tab is closed.
38148          * @param {Roo.TabPanelItem} this
38149          */
38150          "close": true,
38151         /**
38152          * @event deactivate
38153          * Fires when this tab is no longer the active tab.
38154          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38155          * @param {Roo.TabPanelItem} this
38156          */
38157          "deactivate" : true
38158     });
38159     this.hidden = false;
38160
38161     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38162 };
38163
38164 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38165            {
38166     purgeListeners : function(){
38167        Roo.util.Observable.prototype.purgeListeners.call(this);
38168        this.el.removeAllListeners();
38169     },
38170     /**
38171      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38172      */
38173     show : function(){
38174         this.pnode.addClass("active");
38175         this.showAction();
38176         if(Roo.isOpera){
38177             this.tabPanel.stripWrap.repaint();
38178         }
38179         this.fireEvent("activate", this.tabPanel, this);
38180     },
38181
38182     /**
38183      * Returns true if this tab is the active tab.
38184      * @return {Boolean}
38185      */
38186     isActive : function(){
38187         return this.tabPanel.getActiveTab() == this;
38188     },
38189
38190     /**
38191      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38192      */
38193     hide : function(){
38194         this.pnode.removeClass("active");
38195         this.hideAction();
38196         this.fireEvent("deactivate", this.tabPanel, this);
38197     },
38198
38199     hideAction : function(){
38200         this.bodyEl.hide();
38201         this.bodyEl.setStyle("position", "absolute");
38202         this.bodyEl.setLeft("-20000px");
38203         this.bodyEl.setTop("-20000px");
38204     },
38205
38206     showAction : function(){
38207         this.bodyEl.setStyle("position", "relative");
38208         this.bodyEl.setTop("");
38209         this.bodyEl.setLeft("");
38210         this.bodyEl.show();
38211     },
38212
38213     /**
38214      * Set the tooltip for the tab.
38215      * @param {String} tooltip The tab's tooltip
38216      */
38217     setTooltip : function(text){
38218         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38219             this.textEl.dom.qtip = text;
38220             this.textEl.dom.removeAttribute('title');
38221         }else{
38222             this.textEl.dom.title = text;
38223         }
38224     },
38225
38226     onTabClick : function(e){
38227         e.preventDefault();
38228         this.tabPanel.activate(this.id);
38229     },
38230
38231     onTabMouseDown : function(e){
38232         e.preventDefault();
38233         this.tabPanel.activate(this.id);
38234     },
38235 /*
38236     getWidth : function(){
38237         return this.inner.getWidth();
38238     },
38239
38240     setWidth : function(width){
38241         var iwidth = width - this.pnode.getPadding("lr");
38242         this.inner.setWidth(iwidth);
38243         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38244         this.pnode.setWidth(width);
38245     },
38246 */
38247     /**
38248      * Show or hide the tab
38249      * @param {Boolean} hidden True to hide or false to show.
38250      */
38251     setHidden : function(hidden){
38252         this.hidden = hidden;
38253         this.pnode.setStyle("display", hidden ? "none" : "");
38254     },
38255
38256     /**
38257      * Returns true if this tab is "hidden"
38258      * @return {Boolean}
38259      */
38260     isHidden : function(){
38261         return this.hidden;
38262     },
38263
38264     /**
38265      * Returns the text for this tab
38266      * @return {String}
38267      */
38268     getText : function(){
38269         return this.text;
38270     },
38271     /*
38272     autoSize : function(){
38273         //this.el.beginMeasure();
38274         this.textEl.setWidth(1);
38275         /*
38276          *  #2804 [new] Tabs in Roojs
38277          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38278          */
38279         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38280         //this.el.endMeasure();
38281     //},
38282
38283     /**
38284      * Sets the text for the tab (Note: this also sets the tooltip text)
38285      * @param {String} text The tab's text and tooltip
38286      */
38287     setText : function(text){
38288         this.text = text;
38289         this.textEl.update(text);
38290         this.setTooltip(text);
38291         //if(!this.tabPanel.resizeTabs){
38292         //    this.autoSize();
38293         //}
38294     },
38295     /**
38296      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38297      */
38298     activate : function(){
38299         this.tabPanel.activate(this.id);
38300     },
38301
38302     /**
38303      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38304      */
38305     disable : function(){
38306         if(this.tabPanel.active != this){
38307             this.disabled = true;
38308             this.pnode.addClass("disabled");
38309         }
38310     },
38311
38312     /**
38313      * Enables this TabPanelItem if it was previously disabled.
38314      */
38315     enable : function(){
38316         this.disabled = false;
38317         this.pnode.removeClass("disabled");
38318     },
38319
38320     /**
38321      * Sets the content for this TabPanelItem.
38322      * @param {String} content The content
38323      * @param {Boolean} loadScripts true to look for and load scripts
38324      */
38325     setContent : function(content, loadScripts){
38326         this.bodyEl.update(content, loadScripts);
38327     },
38328
38329     /**
38330      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38331      * @return {Roo.UpdateManager} The UpdateManager
38332      */
38333     getUpdateManager : function(){
38334         return this.bodyEl.getUpdateManager();
38335     },
38336
38337     /**
38338      * Set a URL to be used to load the content for this TabPanelItem.
38339      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38340      * @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)
38341      * @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)
38342      * @return {Roo.UpdateManager} The UpdateManager
38343      */
38344     setUrl : function(url, params, loadOnce){
38345         if(this.refreshDelegate){
38346             this.un('activate', this.refreshDelegate);
38347         }
38348         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38349         this.on("activate", this.refreshDelegate);
38350         return this.bodyEl.getUpdateManager();
38351     },
38352
38353     /** @private */
38354     _handleRefresh : function(url, params, loadOnce){
38355         if(!loadOnce || !this.loaded){
38356             var updater = this.bodyEl.getUpdateManager();
38357             updater.update(url, params, this._setLoaded.createDelegate(this));
38358         }
38359     },
38360
38361     /**
38362      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38363      *   Will fail silently if the setUrl method has not been called.
38364      *   This does not activate the panel, just updates its content.
38365      */
38366     refresh : function(){
38367         if(this.refreshDelegate){
38368            this.loaded = false;
38369            this.refreshDelegate();
38370         }
38371     },
38372
38373     /** @private */
38374     _setLoaded : function(){
38375         this.loaded = true;
38376     },
38377
38378     /** @private */
38379     closeClick : function(e){
38380         var o = {};
38381         e.stopEvent();
38382         this.fireEvent("beforeclose", this, o);
38383         if(o.cancel !== true){
38384             this.tabPanel.removeTab(this.id);
38385         }
38386     },
38387     /**
38388      * The text displayed in the tooltip for the close icon.
38389      * @type String
38390      */
38391     closeText : "Close this tab"
38392 });
38393 /**
38394 *    This script refer to:
38395 *    Title: International Telephone Input
38396 *    Author: Jack O'Connor
38397 *    Code version:  v12.1.12
38398 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38399 **/
38400
38401 Roo.bootstrap.PhoneInputData = function() {
38402     var d = [
38403       [
38404         "Afghanistan (‫افغانستان‬‎)",
38405         "af",
38406         "93"
38407       ],
38408       [
38409         "Albania (Shqipëri)",
38410         "al",
38411         "355"
38412       ],
38413       [
38414         "Algeria (‫الجزائر‬‎)",
38415         "dz",
38416         "213"
38417       ],
38418       [
38419         "American Samoa",
38420         "as",
38421         "1684"
38422       ],
38423       [
38424         "Andorra",
38425         "ad",
38426         "376"
38427       ],
38428       [
38429         "Angola",
38430         "ao",
38431         "244"
38432       ],
38433       [
38434         "Anguilla",
38435         "ai",
38436         "1264"
38437       ],
38438       [
38439         "Antigua and Barbuda",
38440         "ag",
38441         "1268"
38442       ],
38443       [
38444         "Argentina",
38445         "ar",
38446         "54"
38447       ],
38448       [
38449         "Armenia (Հայաստան)",
38450         "am",
38451         "374"
38452       ],
38453       [
38454         "Aruba",
38455         "aw",
38456         "297"
38457       ],
38458       [
38459         "Australia",
38460         "au",
38461         "61",
38462         0
38463       ],
38464       [
38465         "Austria (Österreich)",
38466         "at",
38467         "43"
38468       ],
38469       [
38470         "Azerbaijan (Azərbaycan)",
38471         "az",
38472         "994"
38473       ],
38474       [
38475         "Bahamas",
38476         "bs",
38477         "1242"
38478       ],
38479       [
38480         "Bahrain (‫البحرين‬‎)",
38481         "bh",
38482         "973"
38483       ],
38484       [
38485         "Bangladesh (বাংলাদেশ)",
38486         "bd",
38487         "880"
38488       ],
38489       [
38490         "Barbados",
38491         "bb",
38492         "1246"
38493       ],
38494       [
38495         "Belarus (Беларусь)",
38496         "by",
38497         "375"
38498       ],
38499       [
38500         "Belgium (België)",
38501         "be",
38502         "32"
38503       ],
38504       [
38505         "Belize",
38506         "bz",
38507         "501"
38508       ],
38509       [
38510         "Benin (Bénin)",
38511         "bj",
38512         "229"
38513       ],
38514       [
38515         "Bermuda",
38516         "bm",
38517         "1441"
38518       ],
38519       [
38520         "Bhutan (འབྲུག)",
38521         "bt",
38522         "975"
38523       ],
38524       [
38525         "Bolivia",
38526         "bo",
38527         "591"
38528       ],
38529       [
38530         "Bosnia and Herzegovina (Босна и Херцеговина)",
38531         "ba",
38532         "387"
38533       ],
38534       [
38535         "Botswana",
38536         "bw",
38537         "267"
38538       ],
38539       [
38540         "Brazil (Brasil)",
38541         "br",
38542         "55"
38543       ],
38544       [
38545         "British Indian Ocean Territory",
38546         "io",
38547         "246"
38548       ],
38549       [
38550         "British Virgin Islands",
38551         "vg",
38552         "1284"
38553       ],
38554       [
38555         "Brunei",
38556         "bn",
38557         "673"
38558       ],
38559       [
38560         "Bulgaria (България)",
38561         "bg",
38562         "359"
38563       ],
38564       [
38565         "Burkina Faso",
38566         "bf",
38567         "226"
38568       ],
38569       [
38570         "Burundi (Uburundi)",
38571         "bi",
38572         "257"
38573       ],
38574       [
38575         "Cambodia (កម្ពុជា)",
38576         "kh",
38577         "855"
38578       ],
38579       [
38580         "Cameroon (Cameroun)",
38581         "cm",
38582         "237"
38583       ],
38584       [
38585         "Canada",
38586         "ca",
38587         "1",
38588         1,
38589         ["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"]
38590       ],
38591       [
38592         "Cape Verde (Kabu Verdi)",
38593         "cv",
38594         "238"
38595       ],
38596       [
38597         "Caribbean Netherlands",
38598         "bq",
38599         "599",
38600         1
38601       ],
38602       [
38603         "Cayman Islands",
38604         "ky",
38605         "1345"
38606       ],
38607       [
38608         "Central African Republic (République centrafricaine)",
38609         "cf",
38610         "236"
38611       ],
38612       [
38613         "Chad (Tchad)",
38614         "td",
38615         "235"
38616       ],
38617       [
38618         "Chile",
38619         "cl",
38620         "56"
38621       ],
38622       [
38623         "China (中国)",
38624         "cn",
38625         "86"
38626       ],
38627       [
38628         "Christmas Island",
38629         "cx",
38630         "61",
38631         2
38632       ],
38633       [
38634         "Cocos (Keeling) Islands",
38635         "cc",
38636         "61",
38637         1
38638       ],
38639       [
38640         "Colombia",
38641         "co",
38642         "57"
38643       ],
38644       [
38645         "Comoros (‫جزر القمر‬‎)",
38646         "km",
38647         "269"
38648       ],
38649       [
38650         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38651         "cd",
38652         "243"
38653       ],
38654       [
38655         "Congo (Republic) (Congo-Brazzaville)",
38656         "cg",
38657         "242"
38658       ],
38659       [
38660         "Cook Islands",
38661         "ck",
38662         "682"
38663       ],
38664       [
38665         "Costa Rica",
38666         "cr",
38667         "506"
38668       ],
38669       [
38670         "Côte d’Ivoire",
38671         "ci",
38672         "225"
38673       ],
38674       [
38675         "Croatia (Hrvatska)",
38676         "hr",
38677         "385"
38678       ],
38679       [
38680         "Cuba",
38681         "cu",
38682         "53"
38683       ],
38684       [
38685         "Curaçao",
38686         "cw",
38687         "599",
38688         0
38689       ],
38690       [
38691         "Cyprus (Κύπρος)",
38692         "cy",
38693         "357"
38694       ],
38695       [
38696         "Czech Republic (Česká republika)",
38697         "cz",
38698         "420"
38699       ],
38700       [
38701         "Denmark (Danmark)",
38702         "dk",
38703         "45"
38704       ],
38705       [
38706         "Djibouti",
38707         "dj",
38708         "253"
38709       ],
38710       [
38711         "Dominica",
38712         "dm",
38713         "1767"
38714       ],
38715       [
38716         "Dominican Republic (República Dominicana)",
38717         "do",
38718         "1",
38719         2,
38720         ["809", "829", "849"]
38721       ],
38722       [
38723         "Ecuador",
38724         "ec",
38725         "593"
38726       ],
38727       [
38728         "Egypt (‫مصر‬‎)",
38729         "eg",
38730         "20"
38731       ],
38732       [
38733         "El Salvador",
38734         "sv",
38735         "503"
38736       ],
38737       [
38738         "Equatorial Guinea (Guinea Ecuatorial)",
38739         "gq",
38740         "240"
38741       ],
38742       [
38743         "Eritrea",
38744         "er",
38745         "291"
38746       ],
38747       [
38748         "Estonia (Eesti)",
38749         "ee",
38750         "372"
38751       ],
38752       [
38753         "Ethiopia",
38754         "et",
38755         "251"
38756       ],
38757       [
38758         "Falkland Islands (Islas Malvinas)",
38759         "fk",
38760         "500"
38761       ],
38762       [
38763         "Faroe Islands (Føroyar)",
38764         "fo",
38765         "298"
38766       ],
38767       [
38768         "Fiji",
38769         "fj",
38770         "679"
38771       ],
38772       [
38773         "Finland (Suomi)",
38774         "fi",
38775         "358",
38776         0
38777       ],
38778       [
38779         "France",
38780         "fr",
38781         "33"
38782       ],
38783       [
38784         "French Guiana (Guyane française)",
38785         "gf",
38786         "594"
38787       ],
38788       [
38789         "French Polynesia (Polynésie française)",
38790         "pf",
38791         "689"
38792       ],
38793       [
38794         "Gabon",
38795         "ga",
38796         "241"
38797       ],
38798       [
38799         "Gambia",
38800         "gm",
38801         "220"
38802       ],
38803       [
38804         "Georgia (საქართველო)",
38805         "ge",
38806         "995"
38807       ],
38808       [
38809         "Germany (Deutschland)",
38810         "de",
38811         "49"
38812       ],
38813       [
38814         "Ghana (Gaana)",
38815         "gh",
38816         "233"
38817       ],
38818       [
38819         "Gibraltar",
38820         "gi",
38821         "350"
38822       ],
38823       [
38824         "Greece (Ελλάδα)",
38825         "gr",
38826         "30"
38827       ],
38828       [
38829         "Greenland (Kalaallit Nunaat)",
38830         "gl",
38831         "299"
38832       ],
38833       [
38834         "Grenada",
38835         "gd",
38836         "1473"
38837       ],
38838       [
38839         "Guadeloupe",
38840         "gp",
38841         "590",
38842         0
38843       ],
38844       [
38845         "Guam",
38846         "gu",
38847         "1671"
38848       ],
38849       [
38850         "Guatemala",
38851         "gt",
38852         "502"
38853       ],
38854       [
38855         "Guernsey",
38856         "gg",
38857         "44",
38858         1
38859       ],
38860       [
38861         "Guinea (Guinée)",
38862         "gn",
38863         "224"
38864       ],
38865       [
38866         "Guinea-Bissau (Guiné Bissau)",
38867         "gw",
38868         "245"
38869       ],
38870       [
38871         "Guyana",
38872         "gy",
38873         "592"
38874       ],
38875       [
38876         "Haiti",
38877         "ht",
38878         "509"
38879       ],
38880       [
38881         "Honduras",
38882         "hn",
38883         "504"
38884       ],
38885       [
38886         "Hong Kong (香港)",
38887         "hk",
38888         "852"
38889       ],
38890       [
38891         "Hungary (Magyarország)",
38892         "hu",
38893         "36"
38894       ],
38895       [
38896         "Iceland (Ísland)",
38897         "is",
38898         "354"
38899       ],
38900       [
38901         "India (भारत)",
38902         "in",
38903         "91"
38904       ],
38905       [
38906         "Indonesia",
38907         "id",
38908         "62"
38909       ],
38910       [
38911         "Iran (‫ایران‬‎)",
38912         "ir",
38913         "98"
38914       ],
38915       [
38916         "Iraq (‫العراق‬‎)",
38917         "iq",
38918         "964"
38919       ],
38920       [
38921         "Ireland",
38922         "ie",
38923         "353"
38924       ],
38925       [
38926         "Isle of Man",
38927         "im",
38928         "44",
38929         2
38930       ],
38931       [
38932         "Israel (‫ישראל‬‎)",
38933         "il",
38934         "972"
38935       ],
38936       [
38937         "Italy (Italia)",
38938         "it",
38939         "39",
38940         0
38941       ],
38942       [
38943         "Jamaica",
38944         "jm",
38945         "1876"
38946       ],
38947       [
38948         "Japan (日本)",
38949         "jp",
38950         "81"
38951       ],
38952       [
38953         "Jersey",
38954         "je",
38955         "44",
38956         3
38957       ],
38958       [
38959         "Jordan (‫الأردن‬‎)",
38960         "jo",
38961         "962"
38962       ],
38963       [
38964         "Kazakhstan (Казахстан)",
38965         "kz",
38966         "7",
38967         1
38968       ],
38969       [
38970         "Kenya",
38971         "ke",
38972         "254"
38973       ],
38974       [
38975         "Kiribati",
38976         "ki",
38977         "686"
38978       ],
38979       [
38980         "Kosovo",
38981         "xk",
38982         "383"
38983       ],
38984       [
38985         "Kuwait (‫الكويت‬‎)",
38986         "kw",
38987         "965"
38988       ],
38989       [
38990         "Kyrgyzstan (Кыргызстан)",
38991         "kg",
38992         "996"
38993       ],
38994       [
38995         "Laos (ລາວ)",
38996         "la",
38997         "856"
38998       ],
38999       [
39000         "Latvia (Latvija)",
39001         "lv",
39002         "371"
39003       ],
39004       [
39005         "Lebanon (‫لبنان‬‎)",
39006         "lb",
39007         "961"
39008       ],
39009       [
39010         "Lesotho",
39011         "ls",
39012         "266"
39013       ],
39014       [
39015         "Liberia",
39016         "lr",
39017         "231"
39018       ],
39019       [
39020         "Libya (‫ليبيا‬‎)",
39021         "ly",
39022         "218"
39023       ],
39024       [
39025         "Liechtenstein",
39026         "li",
39027         "423"
39028       ],
39029       [
39030         "Lithuania (Lietuva)",
39031         "lt",
39032         "370"
39033       ],
39034       [
39035         "Luxembourg",
39036         "lu",
39037         "352"
39038       ],
39039       [
39040         "Macau (澳門)",
39041         "mo",
39042         "853"
39043       ],
39044       [
39045         "Macedonia (FYROM) (Македонија)",
39046         "mk",
39047         "389"
39048       ],
39049       [
39050         "Madagascar (Madagasikara)",
39051         "mg",
39052         "261"
39053       ],
39054       [
39055         "Malawi",
39056         "mw",
39057         "265"
39058       ],
39059       [
39060         "Malaysia",
39061         "my",
39062         "60"
39063       ],
39064       [
39065         "Maldives",
39066         "mv",
39067         "960"
39068       ],
39069       [
39070         "Mali",
39071         "ml",
39072         "223"
39073       ],
39074       [
39075         "Malta",
39076         "mt",
39077         "356"
39078       ],
39079       [
39080         "Marshall Islands",
39081         "mh",
39082         "692"
39083       ],
39084       [
39085         "Martinique",
39086         "mq",
39087         "596"
39088       ],
39089       [
39090         "Mauritania (‫موريتانيا‬‎)",
39091         "mr",
39092         "222"
39093       ],
39094       [
39095         "Mauritius (Moris)",
39096         "mu",
39097         "230"
39098       ],
39099       [
39100         "Mayotte",
39101         "yt",
39102         "262",
39103         1
39104       ],
39105       [
39106         "Mexico (México)",
39107         "mx",
39108         "52"
39109       ],
39110       [
39111         "Micronesia",
39112         "fm",
39113         "691"
39114       ],
39115       [
39116         "Moldova (Republica Moldova)",
39117         "md",
39118         "373"
39119       ],
39120       [
39121         "Monaco",
39122         "mc",
39123         "377"
39124       ],
39125       [
39126         "Mongolia (Монгол)",
39127         "mn",
39128         "976"
39129       ],
39130       [
39131         "Montenegro (Crna Gora)",
39132         "me",
39133         "382"
39134       ],
39135       [
39136         "Montserrat",
39137         "ms",
39138         "1664"
39139       ],
39140       [
39141         "Morocco (‫المغرب‬‎)",
39142         "ma",
39143         "212",
39144         0
39145       ],
39146       [
39147         "Mozambique (Moçambique)",
39148         "mz",
39149         "258"
39150       ],
39151       [
39152         "Myanmar (Burma) (မြန်မာ)",
39153         "mm",
39154         "95"
39155       ],
39156       [
39157         "Namibia (Namibië)",
39158         "na",
39159         "264"
39160       ],
39161       [
39162         "Nauru",
39163         "nr",
39164         "674"
39165       ],
39166       [
39167         "Nepal (नेपाल)",
39168         "np",
39169         "977"
39170       ],
39171       [
39172         "Netherlands (Nederland)",
39173         "nl",
39174         "31"
39175       ],
39176       [
39177         "New Caledonia (Nouvelle-Calédonie)",
39178         "nc",
39179         "687"
39180       ],
39181       [
39182         "New Zealand",
39183         "nz",
39184         "64"
39185       ],
39186       [
39187         "Nicaragua",
39188         "ni",
39189         "505"
39190       ],
39191       [
39192         "Niger (Nijar)",
39193         "ne",
39194         "227"
39195       ],
39196       [
39197         "Nigeria",
39198         "ng",
39199         "234"
39200       ],
39201       [
39202         "Niue",
39203         "nu",
39204         "683"
39205       ],
39206       [
39207         "Norfolk Island",
39208         "nf",
39209         "672"
39210       ],
39211       [
39212         "North Korea (조선 민주주의 인민 공화국)",
39213         "kp",
39214         "850"
39215       ],
39216       [
39217         "Northern Mariana Islands",
39218         "mp",
39219         "1670"
39220       ],
39221       [
39222         "Norway (Norge)",
39223         "no",
39224         "47",
39225         0
39226       ],
39227       [
39228         "Oman (‫عُمان‬‎)",
39229         "om",
39230         "968"
39231       ],
39232       [
39233         "Pakistan (‫پاکستان‬‎)",
39234         "pk",
39235         "92"
39236       ],
39237       [
39238         "Palau",
39239         "pw",
39240         "680"
39241       ],
39242       [
39243         "Palestine (‫فلسطين‬‎)",
39244         "ps",
39245         "970"
39246       ],
39247       [
39248         "Panama (Panamá)",
39249         "pa",
39250         "507"
39251       ],
39252       [
39253         "Papua New Guinea",
39254         "pg",
39255         "675"
39256       ],
39257       [
39258         "Paraguay",
39259         "py",
39260         "595"
39261       ],
39262       [
39263         "Peru (Perú)",
39264         "pe",
39265         "51"
39266       ],
39267       [
39268         "Philippines",
39269         "ph",
39270         "63"
39271       ],
39272       [
39273         "Poland (Polska)",
39274         "pl",
39275         "48"
39276       ],
39277       [
39278         "Portugal",
39279         "pt",
39280         "351"
39281       ],
39282       [
39283         "Puerto Rico",
39284         "pr",
39285         "1",
39286         3,
39287         ["787", "939"]
39288       ],
39289       [
39290         "Qatar (‫قطر‬‎)",
39291         "qa",
39292         "974"
39293       ],
39294       [
39295         "Réunion (La Réunion)",
39296         "re",
39297         "262",
39298         0
39299       ],
39300       [
39301         "Romania (România)",
39302         "ro",
39303         "40"
39304       ],
39305       [
39306         "Russia (Россия)",
39307         "ru",
39308         "7",
39309         0
39310       ],
39311       [
39312         "Rwanda",
39313         "rw",
39314         "250"
39315       ],
39316       [
39317         "Saint Barthélemy",
39318         "bl",
39319         "590",
39320         1
39321       ],
39322       [
39323         "Saint Helena",
39324         "sh",
39325         "290"
39326       ],
39327       [
39328         "Saint Kitts and Nevis",
39329         "kn",
39330         "1869"
39331       ],
39332       [
39333         "Saint Lucia",
39334         "lc",
39335         "1758"
39336       ],
39337       [
39338         "Saint Martin (Saint-Martin (partie française))",
39339         "mf",
39340         "590",
39341         2
39342       ],
39343       [
39344         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39345         "pm",
39346         "508"
39347       ],
39348       [
39349         "Saint Vincent and the Grenadines",
39350         "vc",
39351         "1784"
39352       ],
39353       [
39354         "Samoa",
39355         "ws",
39356         "685"
39357       ],
39358       [
39359         "San Marino",
39360         "sm",
39361         "378"
39362       ],
39363       [
39364         "São Tomé and Príncipe (São Tomé e Príncipe)",
39365         "st",
39366         "239"
39367       ],
39368       [
39369         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39370         "sa",
39371         "966"
39372       ],
39373       [
39374         "Senegal (Sénégal)",
39375         "sn",
39376         "221"
39377       ],
39378       [
39379         "Serbia (Србија)",
39380         "rs",
39381         "381"
39382       ],
39383       [
39384         "Seychelles",
39385         "sc",
39386         "248"
39387       ],
39388       [
39389         "Sierra Leone",
39390         "sl",
39391         "232"
39392       ],
39393       [
39394         "Singapore",
39395         "sg",
39396         "65"
39397       ],
39398       [
39399         "Sint Maarten",
39400         "sx",
39401         "1721"
39402       ],
39403       [
39404         "Slovakia (Slovensko)",
39405         "sk",
39406         "421"
39407       ],
39408       [
39409         "Slovenia (Slovenija)",
39410         "si",
39411         "386"
39412       ],
39413       [
39414         "Solomon Islands",
39415         "sb",
39416         "677"
39417       ],
39418       [
39419         "Somalia (Soomaaliya)",
39420         "so",
39421         "252"
39422       ],
39423       [
39424         "South Africa",
39425         "za",
39426         "27"
39427       ],
39428       [
39429         "South Korea (대한민국)",
39430         "kr",
39431         "82"
39432       ],
39433       [
39434         "South Sudan (‫جنوب السودان‬‎)",
39435         "ss",
39436         "211"
39437       ],
39438       [
39439         "Spain (España)",
39440         "es",
39441         "34"
39442       ],
39443       [
39444         "Sri Lanka (ශ්‍රී ලංකාව)",
39445         "lk",
39446         "94"
39447       ],
39448       [
39449         "Sudan (‫السودان‬‎)",
39450         "sd",
39451         "249"
39452       ],
39453       [
39454         "Suriname",
39455         "sr",
39456         "597"
39457       ],
39458       [
39459         "Svalbard and Jan Mayen",
39460         "sj",
39461         "47",
39462         1
39463       ],
39464       [
39465         "Swaziland",
39466         "sz",
39467         "268"
39468       ],
39469       [
39470         "Sweden (Sverige)",
39471         "se",
39472         "46"
39473       ],
39474       [
39475         "Switzerland (Schweiz)",
39476         "ch",
39477         "41"
39478       ],
39479       [
39480         "Syria (‫سوريا‬‎)",
39481         "sy",
39482         "963"
39483       ],
39484       [
39485         "Taiwan (台灣)",
39486         "tw",
39487         "886"
39488       ],
39489       [
39490         "Tajikistan",
39491         "tj",
39492         "992"
39493       ],
39494       [
39495         "Tanzania",
39496         "tz",
39497         "255"
39498       ],
39499       [
39500         "Thailand (ไทย)",
39501         "th",
39502         "66"
39503       ],
39504       [
39505         "Timor-Leste",
39506         "tl",
39507         "670"
39508       ],
39509       [
39510         "Togo",
39511         "tg",
39512         "228"
39513       ],
39514       [
39515         "Tokelau",
39516         "tk",
39517         "690"
39518       ],
39519       [
39520         "Tonga",
39521         "to",
39522         "676"
39523       ],
39524       [
39525         "Trinidad and Tobago",
39526         "tt",
39527         "1868"
39528       ],
39529       [
39530         "Tunisia (‫تونس‬‎)",
39531         "tn",
39532         "216"
39533       ],
39534       [
39535         "Turkey (Türkiye)",
39536         "tr",
39537         "90"
39538       ],
39539       [
39540         "Turkmenistan",
39541         "tm",
39542         "993"
39543       ],
39544       [
39545         "Turks and Caicos Islands",
39546         "tc",
39547         "1649"
39548       ],
39549       [
39550         "Tuvalu",
39551         "tv",
39552         "688"
39553       ],
39554       [
39555         "U.S. Virgin Islands",
39556         "vi",
39557         "1340"
39558       ],
39559       [
39560         "Uganda",
39561         "ug",
39562         "256"
39563       ],
39564       [
39565         "Ukraine (Україна)",
39566         "ua",
39567         "380"
39568       ],
39569       [
39570         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39571         "ae",
39572         "971"
39573       ],
39574       [
39575         "United Kingdom",
39576         "gb",
39577         "44",
39578         0
39579       ],
39580       [
39581         "United States",
39582         "us",
39583         "1",
39584         0
39585       ],
39586       [
39587         "Uruguay",
39588         "uy",
39589         "598"
39590       ],
39591       [
39592         "Uzbekistan (Oʻzbekiston)",
39593         "uz",
39594         "998"
39595       ],
39596       [
39597         "Vanuatu",
39598         "vu",
39599         "678"
39600       ],
39601       [
39602         "Vatican City (Città del Vaticano)",
39603         "va",
39604         "39",
39605         1
39606       ],
39607       [
39608         "Venezuela",
39609         "ve",
39610         "58"
39611       ],
39612       [
39613         "Vietnam (Việt Nam)",
39614         "vn",
39615         "84"
39616       ],
39617       [
39618         "Wallis and Futuna (Wallis-et-Futuna)",
39619         "wf",
39620         "681"
39621       ],
39622       [
39623         "Western Sahara (‫الصحراء الغربية‬‎)",
39624         "eh",
39625         "212",
39626         1
39627       ],
39628       [
39629         "Yemen (‫اليمن‬‎)",
39630         "ye",
39631         "967"
39632       ],
39633       [
39634         "Zambia",
39635         "zm",
39636         "260"
39637       ],
39638       [
39639         "Zimbabwe",
39640         "zw",
39641         "263"
39642       ],
39643       [
39644         "Åland Islands",
39645         "ax",
39646         "358",
39647         1
39648       ]
39649   ];
39650   
39651   return d;
39652 }/**
39653 *    This script refer to:
39654 *    Title: International Telephone Input
39655 *    Author: Jack O'Connor
39656 *    Code version:  v12.1.12
39657 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39658 **/
39659
39660 /**
39661  * @class Roo.bootstrap.PhoneInput
39662  * @extends Roo.bootstrap.TriggerField
39663  * An input with International dial-code selection
39664  
39665  * @cfg {String} defaultDialCode default '+852'
39666  * @cfg {Array} preferedCountries default []
39667   
39668  * @constructor
39669  * Create a new PhoneInput.
39670  * @param {Object} config Configuration options
39671  */
39672
39673 Roo.bootstrap.PhoneInput = function(config) {
39674     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39675 };
39676
39677 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39678         
39679         listWidth: undefined,
39680         
39681         selectedClass: 'active',
39682         
39683         invalidClass : "has-warning",
39684         
39685         validClass: 'has-success',
39686         
39687         allowed: '0123456789',
39688         
39689         /**
39690          * @cfg {String} defaultDialCode The default dial code when initializing the input
39691          */
39692         defaultDialCode: '+852',
39693         
39694         /**
39695          * @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
39696          */
39697         preferedCountries: false,
39698         
39699         getAutoCreate : function()
39700         {
39701             var data = Roo.bootstrap.PhoneInputData();
39702             var align = this.labelAlign || this.parentLabelAlign();
39703             var id = Roo.id();
39704             
39705             this.allCountries = [];
39706             this.dialCodeMapping = [];
39707             
39708             for (var i = 0; i < data.length; i++) {
39709               var c = data[i];
39710               this.allCountries[i] = {
39711                 name: c[0],
39712                 iso2: c[1],
39713                 dialCode: c[2],
39714                 priority: c[3] || 0,
39715                 areaCodes: c[4] || null
39716               };
39717               this.dialCodeMapping[c[2]] = {
39718                   name: c[0],
39719                   iso2: c[1],
39720                   priority: c[3] || 0,
39721                   areaCodes: c[4] || null
39722               };
39723             }
39724             
39725             var cfg = {
39726                 cls: 'form-group',
39727                 cn: []
39728             };
39729             
39730             var input =  {
39731                 tag: 'input',
39732                 id : id,
39733                 cls : 'form-control tel-input',
39734                 autocomplete: 'new-password'
39735             };
39736             
39737             var hiddenInput = {
39738                 tag: 'input',
39739                 type: 'hidden',
39740                 cls: 'hidden-tel-input'
39741             };
39742             
39743             if (this.name) {
39744                 hiddenInput.name = this.name;
39745             }
39746             
39747             if (this.disabled) {
39748                 input.disabled = true;
39749             }
39750             
39751             var flag_container = {
39752                 tag: 'div',
39753                 cls: 'flag-box',
39754                 cn: [
39755                     {
39756                         tag: 'div',
39757                         cls: 'flag'
39758                     },
39759                     {
39760                         tag: 'div',
39761                         cls: 'caret'
39762                     }
39763                 ]
39764             };
39765             
39766             var box = {
39767                 tag: 'div',
39768                 cls: this.hasFeedback ? 'has-feedback' : '',
39769                 cn: [
39770                     hiddenInput,
39771                     input,
39772                     {
39773                         tag: 'input',
39774                         cls: 'dial-code-holder',
39775                         disabled: true
39776                     }
39777                 ]
39778             };
39779             
39780             var container = {
39781                 cls: 'roo-select2-container input-group',
39782                 cn: [
39783                     flag_container,
39784                     box
39785                 ]
39786             };
39787             
39788             if (this.fieldLabel.length) {
39789                 var indicator = {
39790                     tag: 'i',
39791                     tooltip: 'This field is required'
39792                 };
39793                 
39794                 var label = {
39795                     tag: 'label',
39796                     'for':  id,
39797                     cls: 'control-label',
39798                     cn: []
39799                 };
39800                 
39801                 var label_text = {
39802                     tag: 'span',
39803                     html: this.fieldLabel
39804                 };
39805                 
39806                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39807                 label.cn = [
39808                     indicator,
39809                     label_text
39810                 ];
39811                 
39812                 if(this.indicatorpos == 'right') {
39813                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39814                     label.cn = [
39815                         label_text,
39816                         indicator
39817                     ];
39818                 }
39819                 
39820                 if(align == 'left') {
39821                     container = {
39822                         tag: 'div',
39823                         cn: [
39824                             container
39825                         ]
39826                     };
39827                     
39828                     if(this.labelWidth > 12){
39829                         label.style = "width: " + this.labelWidth + 'px';
39830                     }
39831                     if(this.labelWidth < 13 && this.labelmd == 0){
39832                         this.labelmd = this.labelWidth;
39833                     }
39834                     if(this.labellg > 0){
39835                         label.cls += ' col-lg-' + this.labellg;
39836                         input.cls += ' col-lg-' + (12 - this.labellg);
39837                     }
39838                     if(this.labelmd > 0){
39839                         label.cls += ' col-md-' + this.labelmd;
39840                         container.cls += ' col-md-' + (12 - this.labelmd);
39841                     }
39842                     if(this.labelsm > 0){
39843                         label.cls += ' col-sm-' + this.labelsm;
39844                         container.cls += ' col-sm-' + (12 - this.labelsm);
39845                     }
39846                     if(this.labelxs > 0){
39847                         label.cls += ' col-xs-' + this.labelxs;
39848                         container.cls += ' col-xs-' + (12 - this.labelxs);
39849                     }
39850                 }
39851             }
39852             
39853             cfg.cn = [
39854                 label,
39855                 container
39856             ];
39857             
39858             var settings = this;
39859             
39860             ['xs','sm','md','lg'].map(function(size){
39861                 if (settings[size]) {
39862                     cfg.cls += ' col-' + size + '-' + settings[size];
39863                 }
39864             });
39865             
39866             this.store = new Roo.data.Store({
39867                 proxy : new Roo.data.MemoryProxy({}),
39868                 reader : new Roo.data.JsonReader({
39869                     fields : [
39870                         {
39871                             'name' : 'name',
39872                             'type' : 'string'
39873                         },
39874                         {
39875                             'name' : 'iso2',
39876                             'type' : 'string'
39877                         },
39878                         {
39879                             'name' : 'dialCode',
39880                             'type' : 'string'
39881                         },
39882                         {
39883                             'name' : 'priority',
39884                             'type' : 'string'
39885                         },
39886                         {
39887                             'name' : 'areaCodes',
39888                             'type' : 'string'
39889                         }
39890                     ]
39891                 })
39892             });
39893             
39894             if(!this.preferedCountries) {
39895                 this.preferedCountries = [
39896                     'hk',
39897                     'gb',
39898                     'us'
39899                 ];
39900             }
39901             
39902             var p = this.preferedCountries.reverse();
39903             
39904             if(p) {
39905                 for (var i = 0; i < p.length; i++) {
39906                     for (var j = 0; j < this.allCountries.length; j++) {
39907                         if(this.allCountries[j].iso2 == p[i]) {
39908                             var t = this.allCountries[j];
39909                             this.allCountries.splice(j,1);
39910                             this.allCountries.unshift(t);
39911                         }
39912                     } 
39913                 }
39914             }
39915             
39916             this.store.proxy.data = {
39917                 success: true,
39918                 data: this.allCountries
39919             };
39920             
39921             return cfg;
39922         },
39923         
39924         initEvents : function()
39925         {
39926             this.createList();
39927             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39928             
39929             this.indicator = this.indicatorEl();
39930             this.flag = this.flagEl();
39931             this.dialCodeHolder = this.dialCodeHolderEl();
39932             
39933             this.trigger = this.el.select('div.flag-box',true).first();
39934             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39935             
39936             var _this = this;
39937             
39938             (function(){
39939                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39940                 _this.list.setWidth(lw);
39941             }).defer(100);
39942             
39943             this.list.on('mouseover', this.onViewOver, this);
39944             this.list.on('mousemove', this.onViewMove, this);
39945             this.inputEl().on("keyup", this.onKeyUp, this);
39946             
39947             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39948
39949             this.view = new Roo.View(this.list, this.tpl, {
39950                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39951             });
39952             
39953             this.view.on('click', this.onViewClick, this);
39954             this.setValue(this.defaultDialCode);
39955         },
39956         
39957         onTriggerClick : function(e)
39958         {
39959             Roo.log('trigger click');
39960             if(this.disabled){
39961                 return;
39962             }
39963             
39964             if(this.isExpanded()){
39965                 this.collapse();
39966                 this.hasFocus = false;
39967             }else {
39968                 this.store.load({});
39969                 this.hasFocus = true;
39970                 this.expand();
39971             }
39972         },
39973         
39974         isExpanded : function()
39975         {
39976             return this.list.isVisible();
39977         },
39978         
39979         collapse : function()
39980         {
39981             if(!this.isExpanded()){
39982                 return;
39983             }
39984             this.list.hide();
39985             Roo.get(document).un('mousedown', this.collapseIf, this);
39986             Roo.get(document).un('mousewheel', this.collapseIf, this);
39987             this.fireEvent('collapse', this);
39988             this.validate();
39989         },
39990         
39991         expand : function()
39992         {
39993             Roo.log('expand');
39994
39995             if(this.isExpanded() || !this.hasFocus){
39996                 return;
39997             }
39998             
39999             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40000             this.list.setWidth(lw);
40001             
40002             this.list.show();
40003             this.restrictHeight();
40004             
40005             Roo.get(document).on('mousedown', this.collapseIf, this);
40006             Roo.get(document).on('mousewheel', this.collapseIf, this);
40007             
40008             this.fireEvent('expand', this);
40009         },
40010         
40011         restrictHeight : function()
40012         {
40013             this.list.alignTo(this.inputEl(), this.listAlign);
40014             this.list.alignTo(this.inputEl(), this.listAlign);
40015         },
40016         
40017         onViewOver : function(e, t)
40018         {
40019             if(this.inKeyMode){
40020                 return;
40021             }
40022             var item = this.view.findItemFromChild(t);
40023             
40024             if(item){
40025                 var index = this.view.indexOf(item);
40026                 this.select(index, false);
40027             }
40028         },
40029
40030         // private
40031         onViewClick : function(view, doFocus, el, e)
40032         {
40033             var index = this.view.getSelectedIndexes()[0];
40034             
40035             var r = this.store.getAt(index);
40036             
40037             if(r){
40038                 this.onSelect(r, index);
40039             }
40040             if(doFocus !== false && !this.blockFocus){
40041                 this.inputEl().focus();
40042             }
40043         },
40044         
40045         onViewMove : function(e, t)
40046         {
40047             this.inKeyMode = false;
40048         },
40049         
40050         select : function(index, scrollIntoView)
40051         {
40052             this.selectedIndex = index;
40053             this.view.select(index);
40054             if(scrollIntoView !== false){
40055                 var el = this.view.getNode(index);
40056                 if(el){
40057                     this.list.scrollChildIntoView(el, false);
40058                 }
40059             }
40060         },
40061         
40062         createList : function()
40063         {
40064             this.list = Roo.get(document.body).createChild({
40065                 tag: 'ul',
40066                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40067                 style: 'display:none'
40068             });
40069             
40070             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40071         },
40072         
40073         collapseIf : function(e)
40074         {
40075             var in_combo  = e.within(this.el);
40076             var in_list =  e.within(this.list);
40077             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40078             
40079             if (in_combo || in_list || is_list) {
40080                 return;
40081             }
40082             this.collapse();
40083         },
40084         
40085         onSelect : function(record, index)
40086         {
40087             if(this.fireEvent('beforeselect', this, record, index) !== false){
40088                 
40089                 this.setFlagClass(record.data.iso2);
40090                 this.setDialCode(record.data.dialCode);
40091                 this.hasFocus = false;
40092                 this.collapse();
40093                 this.fireEvent('select', this, record, index);
40094             }
40095         },
40096         
40097         flagEl : function()
40098         {
40099             var flag = this.el.select('div.flag',true).first();
40100             if(!flag){
40101                 return false;
40102             }
40103             return flag;
40104         },
40105         
40106         dialCodeHolderEl : function()
40107         {
40108             var d = this.el.select('input.dial-code-holder',true).first();
40109             if(!d){
40110                 return false;
40111             }
40112             return d;
40113         },
40114         
40115         setDialCode : function(v)
40116         {
40117             this.dialCodeHolder.dom.value = '+'+v;
40118         },
40119         
40120         setFlagClass : function(n)
40121         {
40122             this.flag.dom.className = 'flag '+n;
40123         },
40124         
40125         getValue : function()
40126         {
40127             var v = this.inputEl().getValue();
40128             if(this.dialCodeHolder) {
40129                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40130             }
40131             return v;
40132         },
40133         
40134         setValue : function(v)
40135         {
40136             var d = this.getDialCode(v);
40137             
40138             //invalid dial code
40139             if(v.length == 0 || !d || d.length == 0) {
40140                 if(this.rendered){
40141                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40142                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40143                 }
40144                 return;
40145             }
40146             
40147             //valid dial code
40148             this.setFlagClass(this.dialCodeMapping[d].iso2);
40149             this.setDialCode(d);
40150             this.inputEl().dom.value = v.replace('+'+d,'');
40151             this.hiddenEl().dom.value = this.getValue();
40152             
40153             this.validate();
40154         },
40155         
40156         getDialCode : function(v)
40157         {
40158             v = v ||  '';
40159             
40160             if (v.length == 0) {
40161                 return this.dialCodeHolder.dom.value;
40162             }
40163             
40164             var dialCode = "";
40165             if (v.charAt(0) != "+") {
40166                 return false;
40167             }
40168             var numericChars = "";
40169             for (var i = 1; i < v.length; i++) {
40170               var c = v.charAt(i);
40171               if (!isNaN(c)) {
40172                 numericChars += c;
40173                 if (this.dialCodeMapping[numericChars]) {
40174                   dialCode = v.substr(1, i);
40175                 }
40176                 if (numericChars.length == 4) {
40177                   break;
40178                 }
40179               }
40180             }
40181             return dialCode;
40182         },
40183         
40184         reset : function()
40185         {
40186             this.setValue(this.defaultDialCode);
40187             this.validate();
40188         },
40189         
40190         hiddenEl : function()
40191         {
40192             return this.el.select('input.hidden-tel-input',true).first();
40193         },
40194         
40195         onKeyUp : function(e){
40196             
40197             var k = e.getKey();
40198             var c = e.getCharCode();
40199             
40200             if(
40201                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40202                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40203             ){
40204                 e.stopEvent();
40205             }
40206             
40207             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40208             //     return;
40209             // }
40210             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40211                 e.stopEvent();
40212             }
40213             
40214             this.setValue(this.getValue());
40215         }
40216         
40217 });
40218 /**
40219  * @class Roo.bootstrap.MoneyField
40220  * @extends Roo.bootstrap.ComboBox
40221  * Bootstrap MoneyField class
40222  * 
40223  * @constructor
40224  * Create a new MoneyField.
40225  * @param {Object} config Configuration options
40226  */
40227
40228 Roo.bootstrap.MoneyField = function(config) {
40229     
40230     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40231     
40232 };
40233
40234 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40235     
40236     /**
40237      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40238      */
40239     allowDecimals : true,
40240     /**
40241      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40242      */
40243     decimalSeparator : ".",
40244     /**
40245      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40246      */
40247     decimalPrecision : 0,
40248     /**
40249      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40250      */
40251     allowNegative : true,
40252     /**
40253      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40254      */
40255     allowZero: true,
40256     /**
40257      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40258      */
40259     minValue : Number.NEGATIVE_INFINITY,
40260     /**
40261      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40262      */
40263     maxValue : Number.MAX_VALUE,
40264     /**
40265      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40266      */
40267     minText : "The minimum value for this field is {0}",
40268     /**
40269      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40270      */
40271     maxText : "The maximum value for this field is {0}",
40272     /**
40273      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40274      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40275      */
40276     nanText : "{0} is not a valid number",
40277     /**
40278      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40279      */
40280     castInt : true,
40281     /**
40282      * @cfg {String} defaults currency of the MoneyField
40283      * value should be in lkey
40284      */
40285     defaultCurrency : false,
40286     /**
40287      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40288      */
40289     thousandsDelimiter : false,
40290     
40291     
40292     inputlg : 9,
40293     inputmd : 9,
40294     inputsm : 9,
40295     inputxs : 6,
40296     
40297     store : false,
40298     
40299     getAutoCreate : function()
40300     {
40301         var align = this.labelAlign || this.parentLabelAlign();
40302         
40303         var id = Roo.id();
40304
40305         var cfg = {
40306             cls: 'form-group',
40307             cn: []
40308         };
40309
40310         var input =  {
40311             tag: 'input',
40312             id : id,
40313             cls : 'form-control roo-money-amount-input',
40314             autocomplete: 'new-password'
40315         };
40316         
40317         var hiddenInput = {
40318             tag: 'input',
40319             type: 'hidden',
40320             id: Roo.id(),
40321             cls: 'hidden-number-input'
40322         };
40323         
40324         if (this.name) {
40325             hiddenInput.name = this.name;
40326         }
40327
40328         if (this.disabled) {
40329             input.disabled = true;
40330         }
40331
40332         var clg = 12 - this.inputlg;
40333         var cmd = 12 - this.inputmd;
40334         var csm = 12 - this.inputsm;
40335         var cxs = 12 - this.inputxs;
40336         
40337         var container = {
40338             tag : 'div',
40339             cls : 'row roo-money-field',
40340             cn : [
40341                 {
40342                     tag : 'div',
40343                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40344                     cn : [
40345                         {
40346                             tag : 'div',
40347                             cls: 'roo-select2-container input-group',
40348                             cn: [
40349                                 {
40350                                     tag : 'input',
40351                                     cls : 'form-control roo-money-currency-input',
40352                                     autocomplete: 'new-password',
40353                                     readOnly : 1,
40354                                     name : this.currencyName
40355                                 },
40356                                 {
40357                                     tag :'span',
40358                                     cls : 'input-group-addon',
40359                                     cn : [
40360                                         {
40361                                             tag: 'span',
40362                                             cls: 'caret'
40363                                         }
40364                                     ]
40365                                 }
40366                             ]
40367                         }
40368                     ]
40369                 },
40370                 {
40371                     tag : 'div',
40372                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40373                     cn : [
40374                         {
40375                             tag: 'div',
40376                             cls: this.hasFeedback ? 'has-feedback' : '',
40377                             cn: [
40378                                 input
40379                             ]
40380                         }
40381                     ]
40382                 }
40383             ]
40384             
40385         };
40386         
40387         if (this.fieldLabel.length) {
40388             var indicator = {
40389                 tag: 'i',
40390                 tooltip: 'This field is required'
40391             };
40392
40393             var label = {
40394                 tag: 'label',
40395                 'for':  id,
40396                 cls: 'control-label',
40397                 cn: []
40398             };
40399
40400             var label_text = {
40401                 tag: 'span',
40402                 html: this.fieldLabel
40403             };
40404
40405             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40406             label.cn = [
40407                 indicator,
40408                 label_text
40409             ];
40410
40411             if(this.indicatorpos == 'right') {
40412                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40413                 label.cn = [
40414                     label_text,
40415                     indicator
40416                 ];
40417             }
40418
40419             if(align == 'left') {
40420                 container = {
40421                     tag: 'div',
40422                     cn: [
40423                         container
40424                     ]
40425                 };
40426
40427                 if(this.labelWidth > 12){
40428                     label.style = "width: " + this.labelWidth + 'px';
40429                 }
40430                 if(this.labelWidth < 13 && this.labelmd == 0){
40431                     this.labelmd = this.labelWidth;
40432                 }
40433                 if(this.labellg > 0){
40434                     label.cls += ' col-lg-' + this.labellg;
40435                     input.cls += ' col-lg-' + (12 - this.labellg);
40436                 }
40437                 if(this.labelmd > 0){
40438                     label.cls += ' col-md-' + this.labelmd;
40439                     container.cls += ' col-md-' + (12 - this.labelmd);
40440                 }
40441                 if(this.labelsm > 0){
40442                     label.cls += ' col-sm-' + this.labelsm;
40443                     container.cls += ' col-sm-' + (12 - this.labelsm);
40444                 }
40445                 if(this.labelxs > 0){
40446                     label.cls += ' col-xs-' + this.labelxs;
40447                     container.cls += ' col-xs-' + (12 - this.labelxs);
40448                 }
40449             }
40450         }
40451
40452         cfg.cn = [
40453             label,
40454             container,
40455             hiddenInput
40456         ];
40457         
40458         var settings = this;
40459
40460         ['xs','sm','md','lg'].map(function(size){
40461             if (settings[size]) {
40462                 cfg.cls += ' col-' + size + '-' + settings[size];
40463             }
40464         });
40465         
40466         return cfg;
40467     },
40468     
40469     initEvents : function()
40470     {
40471         this.indicator = this.indicatorEl();
40472         
40473         this.initCurrencyEvent();
40474         
40475         this.initNumberEvent();
40476     },
40477     
40478     initCurrencyEvent : function()
40479     {
40480         if (!this.store) {
40481             throw "can not find store for combo";
40482         }
40483         
40484         this.store = Roo.factory(this.store, Roo.data);
40485         this.store.parent = this;
40486         
40487         this.createList();
40488         
40489         this.triggerEl = this.el.select('.input-group-addon', true).first();
40490         
40491         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40492         
40493         var _this = this;
40494         
40495         (function(){
40496             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40497             _this.list.setWidth(lw);
40498         }).defer(100);
40499         
40500         this.list.on('mouseover', this.onViewOver, this);
40501         this.list.on('mousemove', this.onViewMove, this);
40502         this.list.on('scroll', this.onViewScroll, this);
40503         
40504         if(!this.tpl){
40505             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40506         }
40507         
40508         this.view = new Roo.View(this.list, this.tpl, {
40509             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40510         });
40511         
40512         this.view.on('click', this.onViewClick, this);
40513         
40514         this.store.on('beforeload', this.onBeforeLoad, this);
40515         this.store.on('load', this.onLoad, this);
40516         this.store.on('loadexception', this.onLoadException, this);
40517         
40518         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40519             "up" : function(e){
40520                 this.inKeyMode = true;
40521                 this.selectPrev();
40522             },
40523
40524             "down" : function(e){
40525                 if(!this.isExpanded()){
40526                     this.onTriggerClick();
40527                 }else{
40528                     this.inKeyMode = true;
40529                     this.selectNext();
40530                 }
40531             },
40532
40533             "enter" : function(e){
40534                 this.collapse();
40535                 
40536                 if(this.fireEvent("specialkey", this, e)){
40537                     this.onViewClick(false);
40538                 }
40539                 
40540                 return true;
40541             },
40542
40543             "esc" : function(e){
40544                 this.collapse();
40545             },
40546
40547             "tab" : function(e){
40548                 this.collapse();
40549                 
40550                 if(this.fireEvent("specialkey", this, e)){
40551                     this.onViewClick(false);
40552                 }
40553                 
40554                 return true;
40555             },
40556
40557             scope : this,
40558
40559             doRelay : function(foo, bar, hname){
40560                 if(hname == 'down' || this.scope.isExpanded()){
40561                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40562                 }
40563                 return true;
40564             },
40565
40566             forceKeyDown: true
40567         });
40568         
40569         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40570         
40571     },
40572     
40573     initNumberEvent : function(e)
40574     {
40575         this.inputEl().on("keydown" , this.fireKey,  this);
40576         this.inputEl().on("focus", this.onFocus,  this);
40577         this.inputEl().on("blur", this.onBlur,  this);
40578         
40579         this.inputEl().relayEvent('keyup', this);
40580         
40581         if(this.indicator){
40582             this.indicator.addClass('invisible');
40583         }
40584  
40585         this.originalValue = this.getValue();
40586         
40587         if(this.validationEvent == 'keyup'){
40588             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40589             this.inputEl().on('keyup', this.filterValidation, this);
40590         }
40591         else if(this.validationEvent !== false){
40592             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40593         }
40594         
40595         if(this.selectOnFocus){
40596             this.on("focus", this.preFocus, this);
40597             
40598         }
40599         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40600             this.inputEl().on("keypress", this.filterKeys, this);
40601         } else {
40602             this.inputEl().relayEvent('keypress', this);
40603         }
40604         
40605         var allowed = "0123456789";
40606         
40607         if(this.allowDecimals){
40608             allowed += this.decimalSeparator;
40609         }
40610         
40611         if(this.allowNegative){
40612             allowed += "-";
40613         }
40614         
40615         if(this.thousandsDelimiter) {
40616             allowed += ",";
40617         }
40618         
40619         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40620         
40621         var keyPress = function(e){
40622             
40623             var k = e.getKey();
40624             
40625             var c = e.getCharCode();
40626             
40627             if(
40628                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40629                     allowed.indexOf(String.fromCharCode(c)) === -1
40630             ){
40631                 e.stopEvent();
40632                 return;
40633             }
40634             
40635             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40636                 return;
40637             }
40638             
40639             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40640                 e.stopEvent();
40641             }
40642         };
40643         
40644         this.inputEl().on("keypress", keyPress, this);
40645         
40646     },
40647     
40648     onTriggerClick : function(e)
40649     {   
40650         if(this.disabled){
40651             return;
40652         }
40653         
40654         this.page = 0;
40655         this.loadNext = false;
40656         
40657         if(this.isExpanded()){
40658             this.collapse();
40659             return;
40660         }
40661         
40662         this.hasFocus = true;
40663         
40664         if(this.triggerAction == 'all') {
40665             this.doQuery(this.allQuery, true);
40666             return;
40667         }
40668         
40669         this.doQuery(this.getRawValue());
40670     },
40671     
40672     getCurrency : function()
40673     {   
40674         var v = this.currencyEl().getValue();
40675         
40676         return v;
40677     },
40678     
40679     restrictHeight : function()
40680     {
40681         this.list.alignTo(this.currencyEl(), this.listAlign);
40682         this.list.alignTo(this.currencyEl(), this.listAlign);
40683     },
40684     
40685     onViewClick : function(view, doFocus, el, e)
40686     {
40687         var index = this.view.getSelectedIndexes()[0];
40688         
40689         var r = this.store.getAt(index);
40690         
40691         if(r){
40692             this.onSelect(r, index);
40693         }
40694     },
40695     
40696     onSelect : function(record, index){
40697         
40698         if(this.fireEvent('beforeselect', this, record, index) !== false){
40699         
40700             this.setFromCurrencyData(index > -1 ? record.data : false);
40701             
40702             this.collapse();
40703             
40704             this.fireEvent('select', this, record, index);
40705         }
40706     },
40707     
40708     setFromCurrencyData : function(o)
40709     {
40710         var currency = '';
40711         
40712         this.lastCurrency = o;
40713         
40714         if (this.currencyField) {
40715             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40716         } else {
40717             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40718         }
40719         
40720         this.lastSelectionText = currency;
40721         
40722         //setting default currency
40723         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40724             this.setCurrency(this.defaultCurrency);
40725             return;
40726         }
40727         
40728         this.setCurrency(currency);
40729     },
40730     
40731     setFromData : function(o)
40732     {
40733         var c = {};
40734         
40735         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40736         
40737         this.setFromCurrencyData(c);
40738         
40739         var value = '';
40740         
40741         if (this.name) {
40742             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40743         } else {
40744             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40745         }
40746         
40747         this.setValue(value);
40748         
40749     },
40750     
40751     setCurrency : function(v)
40752     {   
40753         this.currencyValue = v;
40754         
40755         if(this.rendered){
40756             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40757             this.validate();
40758         }
40759     },
40760     
40761     setValue : function(v)
40762     {
40763         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40764         
40765         this.value = v;
40766         
40767         if(this.rendered){
40768             
40769             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40770             
40771             this.inputEl().dom.value = (v == '') ? '' :
40772                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40773             
40774             if(!this.allowZero && v === '0') {
40775                 this.hiddenEl().dom.value = '';
40776                 this.inputEl().dom.value = '';
40777             }
40778             
40779             this.validate();
40780         }
40781     },
40782     
40783     getRawValue : function()
40784     {
40785         var v = this.inputEl().getValue();
40786         
40787         return v;
40788     },
40789     
40790     getValue : function()
40791     {
40792         return this.fixPrecision(this.parseValue(this.getRawValue()));
40793     },
40794     
40795     parseValue : function(value)
40796     {
40797         if(this.thousandsDelimiter) {
40798             value += "";
40799             r = new RegExp(",", "g");
40800             value = value.replace(r, "");
40801         }
40802         
40803         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40804         return isNaN(value) ? '' : value;
40805         
40806     },
40807     
40808     fixPrecision : function(value)
40809     {
40810         if(this.thousandsDelimiter) {
40811             value += "";
40812             r = new RegExp(",", "g");
40813             value = value.replace(r, "");
40814         }
40815         
40816         var nan = isNaN(value);
40817         
40818         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40819             return nan ? '' : value;
40820         }
40821         return parseFloat(value).toFixed(this.decimalPrecision);
40822     },
40823     
40824     decimalPrecisionFcn : function(v)
40825     {
40826         return Math.floor(v);
40827     },
40828     
40829     validateValue : function(value)
40830     {
40831         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40832             return false;
40833         }
40834         
40835         var num = this.parseValue(value);
40836         
40837         if(isNaN(num)){
40838             this.markInvalid(String.format(this.nanText, value));
40839             return false;
40840         }
40841         
40842         if(num < this.minValue){
40843             this.markInvalid(String.format(this.minText, this.minValue));
40844             return false;
40845         }
40846         
40847         if(num > this.maxValue){
40848             this.markInvalid(String.format(this.maxText, this.maxValue));
40849             return false;
40850         }
40851         
40852         return true;
40853     },
40854     
40855     validate : function()
40856     {
40857         if(this.disabled || this.allowBlank){
40858             this.markValid();
40859             return true;
40860         }
40861         
40862         var currency = this.getCurrency();
40863         
40864         if(this.validateValue(this.getRawValue()) && currency.length){
40865             this.markValid();
40866             return true;
40867         }
40868         
40869         this.markInvalid();
40870         return false;
40871     },
40872     
40873     getName: function()
40874     {
40875         return this.name;
40876     },
40877     
40878     beforeBlur : function()
40879     {
40880         if(!this.castInt){
40881             return;
40882         }
40883         
40884         var v = this.parseValue(this.getRawValue());
40885         
40886         if(v || v == 0){
40887             this.setValue(v);
40888         }
40889     },
40890     
40891     onBlur : function()
40892     {
40893         this.beforeBlur();
40894         
40895         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40896             //this.el.removeClass(this.focusClass);
40897         }
40898         
40899         this.hasFocus = false;
40900         
40901         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40902             this.validate();
40903         }
40904         
40905         var v = this.getValue();
40906         
40907         if(String(v) !== String(this.startValue)){
40908             this.fireEvent('change', this, v, this.startValue);
40909         }
40910         
40911         this.fireEvent("blur", this);
40912     },
40913     
40914     inputEl : function()
40915     {
40916         return this.el.select('.roo-money-amount-input', true).first();
40917     },
40918     
40919     currencyEl : function()
40920     {
40921         return this.el.select('.roo-money-currency-input', true).first();
40922     },
40923     
40924     hiddenEl : function()
40925     {
40926         return this.el.select('input.hidden-number-input',true).first();
40927     }
40928     
40929 });