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         // reassign x when hitting right
2250         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2251             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2252         }
2253         
2254         // reassign y when hitting bottom
2255         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2256             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2257         }
2258         
2259         // but the list may align on trigger left or trigger top... should it be a properity?
2260         
2261         Roo.log(this.el.getWidth());
2262         
2263         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2264             this.el.setXY(xy);
2265         }
2266         
2267         Roo.log(this.el.getWidth());
2268         
2269         this.focus();
2270         this.fireEvent("show", this);
2271     },
2272     
2273     focus : function(){
2274         return;
2275         if(!this.hidden){
2276             this.doFocus.defer(50, this);
2277         }
2278     },
2279
2280     doFocus : function(){
2281         if(!this.hidden){
2282             this.focusEl.focus();
2283         }
2284     },
2285
2286     /**
2287      * Hides this menu and optionally all parent menus
2288      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2289      */
2290     hide : function(deep)
2291     {
2292         
2293         this.hideMenuItems();
2294         if(this.el && this.isVisible()){
2295             this.fireEvent("beforehide", this);
2296             if(this.activeItem){
2297                 this.activeItem.deactivate();
2298                 this.activeItem = null;
2299             }
2300             this.triggerEl.removeClass('open');;
2301             this.hidden = true;
2302             this.fireEvent("hide", this);
2303         }
2304         if(deep === true && this.parentMenu){
2305             this.parentMenu.hide(true);
2306         }
2307     },
2308     
2309     onTriggerClick : function(e)
2310     {
2311         Roo.log('trigger click');
2312         
2313         var target = e.getTarget();
2314         
2315         Roo.log(target.nodeName.toLowerCase());
2316         
2317         if(target.nodeName.toLowerCase() === 'i'){
2318             e.preventDefault();
2319         }
2320         
2321     },
2322     
2323     onTriggerPress  : function(e)
2324     {
2325         Roo.log('trigger press');
2326         //Roo.log(e.getTarget());
2327        // Roo.log(this.triggerEl.dom);
2328        
2329         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330         var pel = Roo.get(e.getTarget());
2331         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332             Roo.log('is treeview or dropdown?');
2333             return;
2334         }
2335         
2336         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2337             return;
2338         }
2339         
2340         if (this.isVisible()) {
2341             Roo.log('hide');
2342             this.hide();
2343         } else {
2344             Roo.log('show');
2345             this.show(this.triggerEl, false, false);
2346         }
2347         
2348         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2349             e.stopEvent();
2350         }
2351         
2352     },
2353        
2354     
2355     hideMenuItems : function()
2356     {
2357         Roo.log("hide Menu Items");
2358         if (!this.el) { 
2359             return;
2360         }
2361         //$(backdrop).remove()
2362         this.el.select('.open',true).each(function(aa) {
2363             
2364             aa.removeClass('open');
2365           //var parent = getParent($(this))
2366           //var relatedTarget = { relatedTarget: this }
2367           
2368            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369           //if (e.isDefaultPrevented()) return
2370            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2371         });
2372     },
2373     addxtypeChild : function (tree, cntr) {
2374         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2375           
2376         this.menuitems.add(comp);
2377         return comp;
2378
2379     },
2380     getEl : function()
2381     {
2382         Roo.log(this.el);
2383         return this.el;
2384     },
2385     
2386     clear : function()
2387     {
2388         this.getEl().dom.innerHTML = '';
2389         this.menuitems.clear();
2390     }
2391 });
2392
2393  
2394  /*
2395  * - LGPL
2396  *
2397  * menu item
2398  * 
2399  */
2400
2401
2402 /**
2403  * @class Roo.bootstrap.MenuItem
2404  * @extends Roo.bootstrap.Component
2405  * Bootstrap MenuItem class
2406  * @cfg {String} html the menu label
2407  * @cfg {String} href the link
2408  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2411  * @cfg {String} fa favicon to show on left of menu item.
2412  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2413  * 
2414  * 
2415  * @constructor
2416  * Create a new MenuItem
2417  * @param {Object} config The config object
2418  */
2419
2420
2421 Roo.bootstrap.MenuItem = function(config){
2422     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2423     this.addEvents({
2424         // raw events
2425         /**
2426          * @event click
2427          * The raw click event for the entire grid.
2428          * @param {Roo.bootstrap.MenuItem} this
2429          * @param {Roo.EventObject} e
2430          */
2431         "click" : true
2432     });
2433 };
2434
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2436     
2437     href : false,
2438     html : false,
2439     preventDefault: false,
2440     isContainer : false,
2441     active : false,
2442     fa: false,
2443     
2444     getAutoCreate : function(){
2445         
2446         if(this.isContainer){
2447             return {
2448                 tag: 'li',
2449                 cls: 'dropdown-menu-item'
2450             };
2451         }
2452         var ctag = {
2453             tag: 'span',
2454             html: 'Link'
2455         };
2456         
2457         var anc = {
2458             tag : 'a',
2459             href : '#',
2460             cn : [  ]
2461         };
2462         
2463         if (this.fa !== false) {
2464             anc.cn.push({
2465                 tag : 'i',
2466                 cls : 'fa fa-' + this.fa
2467             });
2468         }
2469         
2470         anc.cn.push(ctag);
2471         
2472         
2473         var cfg= {
2474             tag: 'li',
2475             cls: 'dropdown-menu-item',
2476             cn: [ anc ]
2477         };
2478         if (this.parent().type == 'treeview') {
2479             cfg.cls = 'treeview-menu';
2480         }
2481         if (this.active) {
2482             cfg.cls += ' active';
2483         }
2484         
2485         
2486         
2487         anc.href = this.href || cfg.cn[0].href ;
2488         ctag.html = this.html || cfg.cn[0].html ;
2489         return cfg;
2490     },
2491     
2492     initEvents: function()
2493     {
2494         if (this.parent().type == 'treeview') {
2495             this.el.select('a').on('click', this.onClick, this);
2496         }
2497         
2498         if (this.menu) {
2499             this.menu.parentType = this.xtype;
2500             this.menu.triggerEl = this.el;
2501             this.menu = this.addxtype(Roo.apply({}, this.menu));
2502         }
2503         
2504     },
2505     onClick : function(e)
2506     {
2507         Roo.log('item on click ');
2508         
2509         if(this.preventDefault){
2510             e.preventDefault();
2511         }
2512         //this.parent().hideMenuItems();
2513         
2514         this.fireEvent('click', this, e);
2515     },
2516     getEl : function()
2517     {
2518         return this.el;
2519     } 
2520 });
2521
2522  
2523
2524  /*
2525  * - LGPL
2526  *
2527  * menu separator
2528  * 
2529  */
2530
2531
2532 /**
2533  * @class Roo.bootstrap.MenuSeparator
2534  * @extends Roo.bootstrap.Component
2535  * Bootstrap MenuSeparator class
2536  * 
2537  * @constructor
2538  * Create a new MenuItem
2539  * @param {Object} config The config object
2540  */
2541
2542
2543 Roo.bootstrap.MenuSeparator = function(config){
2544     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2545 };
2546
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2548     
2549     getAutoCreate : function(){
2550         var cfg = {
2551             cls: 'divider',
2552             tag : 'li'
2553         };
2554         
2555         return cfg;
2556     }
2557    
2558 });
2559
2560  
2561
2562  
2563 /*
2564 * Licence: LGPL
2565 */
2566
2567 /**
2568  * @class Roo.bootstrap.Modal
2569  * @extends Roo.bootstrap.Component
2570  * Bootstrap Modal class
2571  * @cfg {String} title Title of dialog
2572  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2574  * @cfg {Boolean} specificTitle default false
2575  * @cfg {Array} buttons Array of buttons or standard button set..
2576  * @cfg {String} buttonPosition (left|right|center) default right
2577  * @cfg {Boolean} animate default true
2578  * @cfg {Boolean} allow_close default true
2579  * @cfg {Boolean} fitwindow default false
2580  * @cfg {String} size (sm|lg) default empty
2581  *
2582  *
2583  * @constructor
2584  * Create a new Modal Dialog
2585  * @param {Object} config The config object
2586  */
2587
2588 Roo.bootstrap.Modal = function(config){
2589     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2590     this.addEvents({
2591         // raw events
2592         /**
2593          * @event btnclick
2594          * The raw btnclick event for the button
2595          * @param {Roo.EventObject} e
2596          */
2597         "btnclick" : true,
2598         /**
2599          * @event resize
2600          * Fire when dialog resize
2601          * @param {Roo.bootstrap.Modal} this
2602          * @param {Roo.EventObject} e
2603          */
2604         "resize" : true
2605     });
2606     this.buttons = this.buttons || [];
2607
2608     if (this.tmpl) {
2609         this.tmpl = Roo.factory(this.tmpl);
2610     }
2611
2612 };
2613
2614 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2615
2616     title : 'test dialog',
2617
2618     buttons : false,
2619
2620     // set on load...
2621
2622     html: false,
2623
2624     tmp: false,
2625
2626     specificTitle: false,
2627
2628     buttonPosition: 'right',
2629
2630     allow_close : true,
2631
2632     animate : true,
2633
2634     fitwindow: false,
2635
2636
2637      // private
2638     dialogEl: false,
2639     bodyEl:  false,
2640     footerEl:  false,
2641     titleEl:  false,
2642     closeEl:  false,
2643
2644     size: '',
2645
2646
2647     onRender : function(ct, position)
2648     {
2649         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2650
2651         if(!this.el){
2652             var cfg = Roo.apply({},  this.getAutoCreate());
2653             cfg.id = Roo.id();
2654             //if(!cfg.name){
2655             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2656             //}
2657             //if (!cfg.name.length) {
2658             //    delete cfg.name;
2659            // }
2660             if (this.cls) {
2661                 cfg.cls += ' ' + this.cls;
2662             }
2663             if (this.style) {
2664                 cfg.style = this.style;
2665             }
2666             this.el = Roo.get(document.body).createChild(cfg, position);
2667         }
2668         //var type = this.el.dom.type;
2669
2670
2671         if(this.tabIndex !== undefined){
2672             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2673         }
2674
2675         this.dialogEl = this.el.select('.modal-dialog',true).first();
2676         this.bodyEl = this.el.select('.modal-body',true).first();
2677         this.closeEl = this.el.select('.modal-header .close', true).first();
2678         this.headerEl = this.el.select('.modal-header',true).first();
2679         this.titleEl = this.el.select('.modal-title',true).first();
2680         this.footerEl = this.el.select('.modal-footer',true).first();
2681
2682         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2683         
2684         //this.el.addClass("x-dlg-modal");
2685
2686         if (this.buttons.length) {
2687             Roo.each(this.buttons, function(bb) {
2688                 var b = Roo.apply({}, bb);
2689                 b.xns = b.xns || Roo.bootstrap;
2690                 b.xtype = b.xtype || 'Button';
2691                 if (typeof(b.listeners) == 'undefined') {
2692                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2693                 }
2694
2695                 var btn = Roo.factory(b);
2696
2697                 btn.render(this.el.select('.modal-footer div').first());
2698
2699             },this);
2700         }
2701         // render the children.
2702         var nitems = [];
2703
2704         if(typeof(this.items) != 'undefined'){
2705             var items = this.items;
2706             delete this.items;
2707
2708             for(var i =0;i < items.length;i++) {
2709                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2710             }
2711         }
2712
2713         this.items = nitems;
2714
2715         // where are these used - they used to be body/close/footer
2716
2717
2718         this.initEvents();
2719         //this.el.addClass([this.fieldClass, this.cls]);
2720
2721     },
2722
2723     getAutoCreate : function(){
2724
2725
2726         var bdy = {
2727                 cls : 'modal-body',
2728                 html : this.html || ''
2729         };
2730
2731         var title = {
2732             tag: 'h4',
2733             cls : 'modal-title',
2734             html : this.title
2735         };
2736
2737         if(this.specificTitle){
2738             title = this.title;
2739
2740         };
2741
2742         var header = [];
2743         if (this.allow_close) {
2744             header.push({
2745                 tag: 'button',
2746                 cls : 'close',
2747                 html : '&times'
2748             });
2749         }
2750
2751         header.push(title);
2752
2753         var size = '';
2754
2755         if(this.size.length){
2756             size = 'modal-' + this.size;
2757         }
2758
2759         var modal = {
2760             cls: "modal",
2761              cn : [
2762                 {
2763                     cls: "modal-dialog " + size,
2764                     cn : [
2765                         {
2766                             cls : "modal-content",
2767                             cn : [
2768                                 {
2769                                     cls : 'modal-header',
2770                                     cn : header
2771                                 },
2772                                 bdy,
2773                                 {
2774                                     cls : 'modal-footer',
2775                                     cn : [
2776                                         {
2777                                             tag: 'div',
2778                                             cls: 'btn-' + this.buttonPosition
2779                                         }
2780                                     ]
2781
2782                                 }
2783
2784
2785                             ]
2786
2787                         }
2788                     ]
2789
2790                 }
2791             ]
2792         };
2793
2794         if(this.animate){
2795             modal.cls += ' fade';
2796         }
2797
2798         return modal;
2799
2800     },
2801     getChildContainer : function() {
2802
2803          return this.bodyEl;
2804
2805     },
2806     getButtonContainer : function() {
2807          return this.el.select('.modal-footer div',true).first();
2808
2809     },
2810     initEvents : function()
2811     {
2812         if (this.allow_close) {
2813             this.closeEl.on('click', this.hide, this);
2814         }
2815         Roo.EventManager.onWindowResize(this.resize, this, true);
2816
2817
2818     },
2819
2820     resize : function()
2821     {
2822         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2823         if (this.fitwindow) {
2824             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2825             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2826             this.setSize(w,h);
2827         }
2828     },
2829
2830     setSize : function(w,h)
2831     {
2832         if (!w && !h) {
2833             return;
2834         }
2835         this.resizeTo(w,h);
2836     },
2837
2838     show : function() {
2839
2840         if (!this.rendered) {
2841             this.render();
2842         }
2843
2844         //this.el.setStyle('display', 'block');
2845         this.el.removeClass('hideing');        
2846         this.el.addClass('show');
2847  
2848         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2849             var _this = this;
2850             (function(){
2851                 this.el.addClass('in');
2852             }).defer(50, this);
2853         }else{
2854             this.el.addClass('in');
2855
2856         }
2857
2858         // not sure how we can show data in here..
2859         //if (this.tmpl) {
2860         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2861         //}
2862
2863         Roo.get(document.body).addClass("x-body-masked");
2864         
2865         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2866         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2867         this.maskEl.addClass('show');
2868         
2869         this.resize();
2870         
2871         this.fireEvent('show', this);
2872
2873         // set zindex here - otherwise it appears to be ignored...
2874         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2875
2876         (function () {
2877             this.items.forEach( function(e) {
2878                 e.layout ? e.layout() : false;
2879
2880             });
2881         }).defer(100,this);
2882
2883     },
2884     hide : function()
2885     {
2886         if(this.fireEvent("beforehide", this) !== false){
2887             this.maskEl.removeClass('show');
2888             Roo.get(document.body).removeClass("x-body-masked");
2889             this.el.removeClass('in');
2890             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2891
2892             if(this.animate){ // why
2893                 this.el.addClass('hideing');
2894                 (function(){
2895                     if (!this.el.hasClass('hideing')) {
2896                         return; // it's been shown again...
2897                     }
2898                     this.el.removeClass('show');
2899                     this.el.removeClass('hideing');
2900                 }).defer(150,this);
2901                 
2902             }else{
2903                  this.el.removeClass('show');
2904             }
2905             this.fireEvent('hide', this);
2906         }
2907     },
2908     isVisible : function()
2909     {
2910         
2911         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2912         
2913     },
2914
2915     addButton : function(str, cb)
2916     {
2917
2918
2919         var b = Roo.apply({}, { html : str } );
2920         b.xns = b.xns || Roo.bootstrap;
2921         b.xtype = b.xtype || 'Button';
2922         if (typeof(b.listeners) == 'undefined') {
2923             b.listeners = { click : cb.createDelegate(this)  };
2924         }
2925
2926         var btn = Roo.factory(b);
2927
2928         btn.render(this.el.select('.modal-footer div').first());
2929
2930         return btn;
2931
2932     },
2933
2934     setDefaultButton : function(btn)
2935     {
2936         //this.el.select('.modal-footer').()
2937     },
2938     diff : false,
2939
2940     resizeTo: function(w,h)
2941     {
2942         // skip.. ?? why??
2943
2944         this.dialogEl.setWidth(w);
2945         if (this.diff === false) {
2946             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2947         }
2948
2949         this.bodyEl.setHeight(h-this.diff);
2950
2951         this.fireEvent('resize', this);
2952
2953     },
2954     setContentSize  : function(w, h)
2955     {
2956
2957     },
2958     onButtonClick: function(btn,e)
2959     {
2960         //Roo.log([a,b,c]);
2961         this.fireEvent('btnclick', btn.name, e);
2962     },
2963      /**
2964      * Set the title of the Dialog
2965      * @param {String} str new Title
2966      */
2967     setTitle: function(str) {
2968         this.titleEl.dom.innerHTML = str;
2969     },
2970     /**
2971      * Set the body of the Dialog
2972      * @param {String} str new Title
2973      */
2974     setBody: function(str) {
2975         this.bodyEl.dom.innerHTML = str;
2976     },
2977     /**
2978      * Set the body of the Dialog using the template
2979      * @param {Obj} data - apply this data to the template and replace the body contents.
2980      */
2981     applyBody: function(obj)
2982     {
2983         if (!this.tmpl) {
2984             Roo.log("Error - using apply Body without a template");
2985             //code
2986         }
2987         this.tmpl.overwrite(this.bodyEl, obj);
2988     }
2989
2990 });
2991
2992
2993 Roo.apply(Roo.bootstrap.Modal,  {
2994     /**
2995          * Button config that displays a single OK button
2996          * @type Object
2997          */
2998         OK :  [{
2999             name : 'ok',
3000             weight : 'primary',
3001             html : 'OK'
3002         }],
3003         /**
3004          * Button config that displays Yes and No buttons
3005          * @type Object
3006          */
3007         YESNO : [
3008             {
3009                 name  : 'no',
3010                 html : 'No'
3011             },
3012             {
3013                 name  :'yes',
3014                 weight : 'primary',
3015                 html : 'Yes'
3016             }
3017         ],
3018
3019         /**
3020          * Button config that displays OK and Cancel buttons
3021          * @type Object
3022          */
3023         OKCANCEL : [
3024             {
3025                name : 'cancel',
3026                 html : 'Cancel'
3027             },
3028             {
3029                 name : 'ok',
3030                 weight : 'primary',
3031                 html : 'OK'
3032             }
3033         ],
3034         /**
3035          * Button config that displays Yes, No and Cancel buttons
3036          * @type Object
3037          */
3038         YESNOCANCEL : [
3039             {
3040                 name : 'yes',
3041                 weight : 'primary',
3042                 html : 'Yes'
3043             },
3044             {
3045                 name : 'no',
3046                 html : 'No'
3047             },
3048             {
3049                 name : 'cancel',
3050                 html : 'Cancel'
3051             }
3052         ],
3053         
3054         zIndex : 10001
3055 });
3056 /*
3057  * - LGPL
3058  *
3059  * messagebox - can be used as a replace
3060  * 
3061  */
3062 /**
3063  * @class Roo.MessageBox
3064  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3065  * Example usage:
3066  *<pre><code>
3067 // Basic alert:
3068 Roo.Msg.alert('Status', 'Changes saved successfully.');
3069
3070 // Prompt for user data:
3071 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3072     if (btn == 'ok'){
3073         // process text value...
3074     }
3075 });
3076
3077 // Show a dialog using config options:
3078 Roo.Msg.show({
3079    title:'Save Changes?',
3080    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3081    buttons: Roo.Msg.YESNOCANCEL,
3082    fn: processResult,
3083    animEl: 'elId'
3084 });
3085 </code></pre>
3086  * @singleton
3087  */
3088 Roo.bootstrap.MessageBox = function(){
3089     var dlg, opt, mask, waitTimer;
3090     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3091     var buttons, activeTextEl, bwidth;
3092
3093     
3094     // private
3095     var handleButton = function(button){
3096         dlg.hide();
3097         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3098     };
3099
3100     // private
3101     var handleHide = function(){
3102         if(opt && opt.cls){
3103             dlg.el.removeClass(opt.cls);
3104         }
3105         //if(waitTimer){
3106         //    Roo.TaskMgr.stop(waitTimer);
3107         //    waitTimer = null;
3108         //}
3109     };
3110
3111     // private
3112     var updateButtons = function(b){
3113         var width = 0;
3114         if(!b){
3115             buttons["ok"].hide();
3116             buttons["cancel"].hide();
3117             buttons["yes"].hide();
3118             buttons["no"].hide();
3119             //dlg.footer.dom.style.display = 'none';
3120             return width;
3121         }
3122         dlg.footerEl.dom.style.display = '';
3123         for(var k in buttons){
3124             if(typeof buttons[k] != "function"){
3125                 if(b[k]){
3126                     buttons[k].show();
3127                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3128                     width += buttons[k].el.getWidth()+15;
3129                 }else{
3130                     buttons[k].hide();
3131                 }
3132             }
3133         }
3134         return width;
3135     };
3136
3137     // private
3138     var handleEsc = function(d, k, e){
3139         if(opt && opt.closable !== false){
3140             dlg.hide();
3141         }
3142         if(e){
3143             e.stopEvent();
3144         }
3145     };
3146
3147     return {
3148         /**
3149          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3150          * @return {Roo.BasicDialog} The BasicDialog element
3151          */
3152         getDialog : function(){
3153            if(!dlg){
3154                 dlg = new Roo.bootstrap.Modal( {
3155                     //draggable: true,
3156                     //resizable:false,
3157                     //constraintoviewport:false,
3158                     //fixedcenter:true,
3159                     //collapsible : false,
3160                     //shim:true,
3161                     //modal: true,
3162                 //    width: 'auto',
3163                   //  height:100,
3164                     //buttonAlign:"center",
3165                     closeClick : function(){
3166                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3167                             handleButton("no");
3168                         }else{
3169                             handleButton("cancel");
3170                         }
3171                     }
3172                 });
3173                 dlg.render();
3174                 dlg.on("hide", handleHide);
3175                 mask = dlg.mask;
3176                 //dlg.addKeyListener(27, handleEsc);
3177                 buttons = {};
3178                 this.buttons = buttons;
3179                 var bt = this.buttonText;
3180                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3181                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3182                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3183                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3184                 //Roo.log(buttons);
3185                 bodyEl = dlg.bodyEl.createChild({
3186
3187                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3188                         '<textarea class="roo-mb-textarea"></textarea>' +
3189                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3190                 });
3191                 msgEl = bodyEl.dom.firstChild;
3192                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3193                 textboxEl.enableDisplayMode();
3194                 textboxEl.addKeyListener([10,13], function(){
3195                     if(dlg.isVisible() && opt && opt.buttons){
3196                         if(opt.buttons.ok){
3197                             handleButton("ok");
3198                         }else if(opt.buttons.yes){
3199                             handleButton("yes");
3200                         }
3201                     }
3202                 });
3203                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3204                 textareaEl.enableDisplayMode();
3205                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3206                 progressEl.enableDisplayMode();
3207                 
3208                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3209                 var pf = progressEl.dom.firstChild;
3210                 if (pf) {
3211                     pp = Roo.get(pf.firstChild);
3212                     pp.setHeight(pf.offsetHeight);
3213                 }
3214                 
3215             }
3216             return dlg;
3217         },
3218
3219         /**
3220          * Updates the message box body text
3221          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3222          * the XHTML-compliant non-breaking space character '&amp;#160;')
3223          * @return {Roo.MessageBox} This message box
3224          */
3225         updateText : function(text)
3226         {
3227             if(!dlg.isVisible() && !opt.width){
3228                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3229                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3230             }
3231             msgEl.innerHTML = text || '&#160;';
3232       
3233             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3234             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3235             var w = Math.max(
3236                     Math.min(opt.width || cw , this.maxWidth), 
3237                     Math.max(opt.minWidth || this.minWidth, bwidth)
3238             );
3239             if(opt.prompt){
3240                 activeTextEl.setWidth(w);
3241             }
3242             if(dlg.isVisible()){
3243                 dlg.fixedcenter = false;
3244             }
3245             // to big, make it scroll. = But as usual stupid IE does not support
3246             // !important..
3247             
3248             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3249                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3250                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3251             } else {
3252                 bodyEl.dom.style.height = '';
3253                 bodyEl.dom.style.overflowY = '';
3254             }
3255             if (cw > w) {
3256                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3257             } else {
3258                 bodyEl.dom.style.overflowX = '';
3259             }
3260             
3261             dlg.setContentSize(w, bodyEl.getHeight());
3262             if(dlg.isVisible()){
3263                 dlg.fixedcenter = true;
3264             }
3265             return this;
3266         },
3267
3268         /**
3269          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3270          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3271          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3272          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3273          * @return {Roo.MessageBox} This message box
3274          */
3275         updateProgress : function(value, text){
3276             if(text){
3277                 this.updateText(text);
3278             }
3279             
3280             if (pp) { // weird bug on my firefox - for some reason this is not defined
3281                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3282                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3283             }
3284             return this;
3285         },        
3286
3287         /**
3288          * Returns true if the message box is currently displayed
3289          * @return {Boolean} True if the message box is visible, else false
3290          */
3291         isVisible : function(){
3292             return dlg && dlg.isVisible();  
3293         },
3294
3295         /**
3296          * Hides the message box if it is displayed
3297          */
3298         hide : function(){
3299             if(this.isVisible()){
3300                 dlg.hide();
3301             }  
3302         },
3303
3304         /**
3305          * Displays a new message box, or reinitializes an existing message box, based on the config options
3306          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3307          * The following config object properties are supported:
3308          * <pre>
3309 Property    Type             Description
3310 ----------  ---------------  ------------------------------------------------------------------------------------
3311 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3312                                    closes (defaults to undefined)
3313 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3314                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3315 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3316                                    progress and wait dialogs will ignore this property and always hide the
3317                                    close button as they can only be closed programmatically.
3318 cls               String           A custom CSS class to apply to the message box element
3319 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3320                                    displayed (defaults to 75)
3321 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3322                                    function will be btn (the name of the button that was clicked, if applicable,
3323                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3324                                    Progress and wait dialogs will ignore this option since they do not respond to
3325                                    user actions and can only be closed programmatically, so any required function
3326                                    should be called by the same code after it closes the dialog.
3327 icon              String           A CSS class that provides a background image to be used as an icon for
3328                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3329 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3330 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3331 modal             Boolean          False to allow user interaction with the page while the message box is
3332                                    displayed (defaults to true)
3333 msg               String           A string that will replace the existing message box body text (defaults
3334                                    to the XHTML-compliant non-breaking space character '&#160;')
3335 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3336 progress          Boolean          True to display a progress bar (defaults to false)
3337 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3338 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3339 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3340 title             String           The title text
3341 value             String           The string value to set into the active textbox element if displayed
3342 wait              Boolean          True to display a progress bar (defaults to false)
3343 width             Number           The width of the dialog in pixels
3344 </pre>
3345          *
3346          * Example usage:
3347          * <pre><code>
3348 Roo.Msg.show({
3349    title: 'Address',
3350    msg: 'Please enter your address:',
3351    width: 300,
3352    buttons: Roo.MessageBox.OKCANCEL,
3353    multiline: true,
3354    fn: saveAddress,
3355    animEl: 'addAddressBtn'
3356 });
3357 </code></pre>
3358          * @param {Object} config Configuration options
3359          * @return {Roo.MessageBox} This message box
3360          */
3361         show : function(options)
3362         {
3363             
3364             // this causes nightmares if you show one dialog after another
3365             // especially on callbacks..
3366              
3367             if(this.isVisible()){
3368                 
3369                 this.hide();
3370                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3371                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3372                 Roo.log("New Dialog Message:" +  options.msg )
3373                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3374                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3375                 
3376             }
3377             var d = this.getDialog();
3378             opt = options;
3379             d.setTitle(opt.title || "&#160;");
3380             d.closeEl.setDisplayed(opt.closable !== false);
3381             activeTextEl = textboxEl;
3382             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3383             if(opt.prompt){
3384                 if(opt.multiline){
3385                     textboxEl.hide();
3386                     textareaEl.show();
3387                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3388                         opt.multiline : this.defaultTextHeight);
3389                     activeTextEl = textareaEl;
3390                 }else{
3391                     textboxEl.show();
3392                     textareaEl.hide();
3393                 }
3394             }else{
3395                 textboxEl.hide();
3396                 textareaEl.hide();
3397             }
3398             progressEl.setDisplayed(opt.progress === true);
3399             this.updateProgress(0);
3400             activeTextEl.dom.value = opt.value || "";
3401             if(opt.prompt){
3402                 dlg.setDefaultButton(activeTextEl);
3403             }else{
3404                 var bs = opt.buttons;
3405                 var db = null;
3406                 if(bs && bs.ok){
3407                     db = buttons["ok"];
3408                 }else if(bs && bs.yes){
3409                     db = buttons["yes"];
3410                 }
3411                 dlg.setDefaultButton(db);
3412             }
3413             bwidth = updateButtons(opt.buttons);
3414             this.updateText(opt.msg);
3415             if(opt.cls){
3416                 d.el.addClass(opt.cls);
3417             }
3418             d.proxyDrag = opt.proxyDrag === true;
3419             d.modal = opt.modal !== false;
3420             d.mask = opt.modal !== false ? mask : false;
3421             if(!d.isVisible()){
3422                 // force it to the end of the z-index stack so it gets a cursor in FF
3423                 document.body.appendChild(dlg.el.dom);
3424                 d.animateTarget = null;
3425                 d.show(options.animEl);
3426             }
3427             return this;
3428         },
3429
3430         /**
3431          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3432          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3433          * and closing the message box when the process is complete.
3434          * @param {String} title The title bar text
3435          * @param {String} msg The message box body text
3436          * @return {Roo.MessageBox} This message box
3437          */
3438         progress : function(title, msg){
3439             this.show({
3440                 title : title,
3441                 msg : msg,
3442                 buttons: false,
3443                 progress:true,
3444                 closable:false,
3445                 minWidth: this.minProgressWidth,
3446                 modal : true
3447             });
3448             return this;
3449         },
3450
3451         /**
3452          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3453          * If a callback function is passed it will be called after the user clicks the button, and the
3454          * id of the button that was clicked will be passed as the only parameter to the callback
3455          * (could also be the top-right close button).
3456          * @param {String} title The title bar text
3457          * @param {String} msg The message box body text
3458          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3459          * @param {Object} scope (optional) The scope of the callback function
3460          * @return {Roo.MessageBox} This message box
3461          */
3462         alert : function(title, msg, fn, scope)
3463         {
3464             this.show({
3465                 title : title,
3466                 msg : msg,
3467                 buttons: this.OK,
3468                 fn: fn,
3469                 closable : false,
3470                 scope : scope,
3471                 modal : true
3472             });
3473             return this;
3474         },
3475
3476         /**
3477          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3478          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3479          * You are responsible for closing the message box when the process is complete.
3480          * @param {String} msg The message box body text
3481          * @param {String} title (optional) The title bar text
3482          * @return {Roo.MessageBox} This message box
3483          */
3484         wait : function(msg, title){
3485             this.show({
3486                 title : title,
3487                 msg : msg,
3488                 buttons: false,
3489                 closable:false,
3490                 progress:true,
3491                 modal:true,
3492                 width:300,
3493                 wait:true
3494             });
3495             waitTimer = Roo.TaskMgr.start({
3496                 run: function(i){
3497                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3498                 },
3499                 interval: 1000
3500             });
3501             return this;
3502         },
3503
3504         /**
3505          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3506          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3507          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3508          * @param {String} title The title bar text
3509          * @param {String} msg The message box body text
3510          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3511          * @param {Object} scope (optional) The scope of the callback function
3512          * @return {Roo.MessageBox} This message box
3513          */
3514         confirm : function(title, msg, fn, scope){
3515             this.show({
3516                 title : title,
3517                 msg : msg,
3518                 buttons: this.YESNO,
3519                 fn: fn,
3520                 scope : scope,
3521                 modal : true
3522             });
3523             return this;
3524         },
3525
3526         /**
3527          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3528          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3529          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3530          * (could also be the top-right close button) and the text that was entered will be passed as the two
3531          * parameters to the callback.
3532          * @param {String} title The title bar text
3533          * @param {String} msg The message box body text
3534          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3535          * @param {Object} scope (optional) The scope of the callback function
3536          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3537          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3538          * @return {Roo.MessageBox} This message box
3539          */
3540         prompt : function(title, msg, fn, scope, multiline){
3541             this.show({
3542                 title : title,
3543                 msg : msg,
3544                 buttons: this.OKCANCEL,
3545                 fn: fn,
3546                 minWidth:250,
3547                 scope : scope,
3548                 prompt:true,
3549                 multiline: multiline,
3550                 modal : true
3551             });
3552             return this;
3553         },
3554
3555         /**
3556          * Button config that displays a single OK button
3557          * @type Object
3558          */
3559         OK : {ok:true},
3560         /**
3561          * Button config that displays Yes and No buttons
3562          * @type Object
3563          */
3564         YESNO : {yes:true, no:true},
3565         /**
3566          * Button config that displays OK and Cancel buttons
3567          * @type Object
3568          */
3569         OKCANCEL : {ok:true, cancel:true},
3570         /**
3571          * Button config that displays Yes, No and Cancel buttons
3572          * @type Object
3573          */
3574         YESNOCANCEL : {yes:true, no:true, cancel:true},
3575
3576         /**
3577          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3578          * @type Number
3579          */
3580         defaultTextHeight : 75,
3581         /**
3582          * The maximum width in pixels of the message box (defaults to 600)
3583          * @type Number
3584          */
3585         maxWidth : 600,
3586         /**
3587          * The minimum width in pixels of the message box (defaults to 100)
3588          * @type Number
3589          */
3590         minWidth : 100,
3591         /**
3592          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3593          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3594          * @type Number
3595          */
3596         minProgressWidth : 250,
3597         /**
3598          * An object containing the default button text strings that can be overriden for localized language support.
3599          * Supported properties are: ok, cancel, yes and no.
3600          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3601          * @type Object
3602          */
3603         buttonText : {
3604             ok : "OK",
3605             cancel : "Cancel",
3606             yes : "Yes",
3607             no : "No"
3608         }
3609     };
3610 }();
3611
3612 /**
3613  * Shorthand for {@link Roo.MessageBox}
3614  */
3615 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3616 Roo.Msg = Roo.Msg || Roo.MessageBox;
3617 /*
3618  * - LGPL
3619  *
3620  * navbar
3621  * 
3622  */
3623
3624 /**
3625  * @class Roo.bootstrap.Navbar
3626  * @extends Roo.bootstrap.Component
3627  * Bootstrap Navbar class
3628
3629  * @constructor
3630  * Create a new Navbar
3631  * @param {Object} config The config object
3632  */
3633
3634
3635 Roo.bootstrap.Navbar = function(config){
3636     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3637     this.addEvents({
3638         // raw events
3639         /**
3640          * @event beforetoggle
3641          * Fire before toggle the menu
3642          * @param {Roo.EventObject} e
3643          */
3644         "beforetoggle" : true
3645     });
3646 };
3647
3648 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3649     
3650     
3651    
3652     // private
3653     navItems : false,
3654     loadMask : false,
3655     
3656     
3657     getAutoCreate : function(){
3658         
3659         
3660         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3661         
3662     },
3663     
3664     initEvents :function ()
3665     {
3666         //Roo.log(this.el.select('.navbar-toggle',true));
3667         this.el.select('.navbar-toggle',true).on('click', function() {
3668             if(this.fireEvent('beforetoggle', this) !== false){
3669                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3670             }
3671             
3672         }, this);
3673         
3674         var mark = {
3675             tag: "div",
3676             cls:"x-dlg-mask"
3677         };
3678         
3679         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3680         
3681         var size = this.el.getSize();
3682         this.maskEl.setSize(size.width, size.height);
3683         this.maskEl.enableDisplayMode("block");
3684         this.maskEl.hide();
3685         
3686         if(this.loadMask){
3687             this.maskEl.show();
3688         }
3689     },
3690     
3691     
3692     getChildContainer : function()
3693     {
3694         if (this.el.select('.collapse').getCount()) {
3695             return this.el.select('.collapse',true).first();
3696         }
3697         
3698         return this.el;
3699     },
3700     
3701     mask : function()
3702     {
3703         this.maskEl.show();
3704     },
3705     
3706     unmask : function()
3707     {
3708         this.maskEl.hide();
3709     } 
3710     
3711     
3712     
3713     
3714 });
3715
3716
3717
3718  
3719
3720  /*
3721  * - LGPL
3722  *
3723  * navbar
3724  * 
3725  */
3726
3727 /**
3728  * @class Roo.bootstrap.NavSimplebar
3729  * @extends Roo.bootstrap.Navbar
3730  * Bootstrap Sidebar class
3731  *
3732  * @cfg {Boolean} inverse is inverted color
3733  * 
3734  * @cfg {String} type (nav | pills | tabs)
3735  * @cfg {Boolean} arrangement stacked | justified
3736  * @cfg {String} align (left | right) alignment
3737  * 
3738  * @cfg {Boolean} main (true|false) main nav bar? default false
3739  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3740  * 
3741  * @cfg {String} tag (header|footer|nav|div) default is nav 
3742
3743  * 
3744  * 
3745  * 
3746  * @constructor
3747  * Create a new Sidebar
3748  * @param {Object} config The config object
3749  */
3750
3751
3752 Roo.bootstrap.NavSimplebar = function(config){
3753     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3754 };
3755
3756 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3757     
3758     inverse: false,
3759     
3760     type: false,
3761     arrangement: '',
3762     align : false,
3763     
3764     
3765     
3766     main : false,
3767     
3768     
3769     tag : false,
3770     
3771     
3772     getAutoCreate : function(){
3773         
3774         
3775         var cfg = {
3776             tag : this.tag || 'div',
3777             cls : 'navbar'
3778         };
3779           
3780         
3781         cfg.cn = [
3782             {
3783                 cls: 'nav',
3784                 tag : 'ul'
3785             }
3786         ];
3787         
3788          
3789         this.type = this.type || 'nav';
3790         if (['tabs','pills'].indexOf(this.type)!==-1) {
3791             cfg.cn[0].cls += ' nav-' + this.type
3792         
3793         
3794         } else {
3795             if (this.type!=='nav') {
3796                 Roo.log('nav type must be nav/tabs/pills')
3797             }
3798             cfg.cn[0].cls += ' navbar-nav'
3799         }
3800         
3801         
3802         
3803         
3804         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3805             cfg.cn[0].cls += ' nav-' + this.arrangement;
3806         }
3807         
3808         
3809         if (this.align === 'right') {
3810             cfg.cn[0].cls += ' navbar-right';
3811         }
3812         
3813         if (this.inverse) {
3814             cfg.cls += ' navbar-inverse';
3815             
3816         }
3817         
3818         
3819         return cfg;
3820     
3821         
3822     }
3823     
3824     
3825     
3826 });
3827
3828
3829
3830  
3831
3832  
3833        /*
3834  * - LGPL
3835  *
3836  * navbar
3837  * 
3838  */
3839
3840 /**
3841  * @class Roo.bootstrap.NavHeaderbar
3842  * @extends Roo.bootstrap.NavSimplebar
3843  * Bootstrap Sidebar class
3844  *
3845  * @cfg {String} brand what is brand
3846  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3847  * @cfg {String} brand_href href of the brand
3848  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3849  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3850  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3851  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3852  * 
3853  * @constructor
3854  * Create a new Sidebar
3855  * @param {Object} config The config object
3856  */
3857
3858
3859 Roo.bootstrap.NavHeaderbar = function(config){
3860     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3861       
3862 };
3863
3864 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3865     
3866     position: '',
3867     brand: '',
3868     brand_href: false,
3869     srButton : true,
3870     autohide : false,
3871     desktopCenter : false,
3872    
3873     
3874     getAutoCreate : function(){
3875         
3876         var   cfg = {
3877             tag: this.nav || 'nav',
3878             cls: 'navbar',
3879             role: 'navigation',
3880             cn: []
3881         };
3882         
3883         var cn = cfg.cn;
3884         if (this.desktopCenter) {
3885             cn.push({cls : 'container', cn : []});
3886             cn = cn[0].cn;
3887         }
3888         
3889         if(this.srButton){
3890             cn.push({
3891                 tag: 'div',
3892                 cls: 'navbar-header',
3893                 cn: [
3894                     {
3895                         tag: 'button',
3896                         type: 'button',
3897                         cls: 'navbar-toggle',
3898                         'data-toggle': 'collapse',
3899                         cn: [
3900                             {
3901                                 tag: 'span',
3902                                 cls: 'sr-only',
3903                                 html: 'Toggle navigation'
3904                             },
3905                             {
3906                                 tag: 'span',
3907                                 cls: 'icon-bar'
3908                             },
3909                             {
3910                                 tag: 'span',
3911                                 cls: 'icon-bar'
3912                             },
3913                             {
3914                                 tag: 'span',
3915                                 cls: 'icon-bar'
3916                             }
3917                         ]
3918                     }
3919                 ]
3920             });
3921         }
3922         
3923         cn.push({
3924             tag: 'div',
3925             cls: 'collapse navbar-collapse',
3926             cn : []
3927         });
3928         
3929         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3930         
3931         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3932             cfg.cls += ' navbar-' + this.position;
3933             
3934             // tag can override this..
3935             
3936             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3937         }
3938         
3939         if (this.brand !== '') {
3940             cn[0].cn.push({
3941                 tag: 'a',
3942                 href: this.brand_href ? this.brand_href : '#',
3943                 cls: 'navbar-brand',
3944                 cn: [
3945                 this.brand
3946                 ]
3947             });
3948         }
3949         
3950         if(this.main){
3951             cfg.cls += ' main-nav';
3952         }
3953         
3954         
3955         return cfg;
3956
3957         
3958     },
3959     getHeaderChildContainer : function()
3960     {
3961         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3962             return this.el.select('.navbar-header',true).first();
3963         }
3964         
3965         return this.getChildContainer();
3966     },
3967     
3968     
3969     initEvents : function()
3970     {
3971         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3972         
3973         if (this.autohide) {
3974             
3975             var prevScroll = 0;
3976             var ft = this.el;
3977             
3978             Roo.get(document).on('scroll',function(e) {
3979                 var ns = Roo.get(document).getScroll().top;
3980                 var os = prevScroll;
3981                 prevScroll = ns;
3982                 
3983                 if(ns > os){
3984                     ft.removeClass('slideDown');
3985                     ft.addClass('slideUp');
3986                     return;
3987                 }
3988                 ft.removeClass('slideUp');
3989                 ft.addClass('slideDown');
3990                  
3991               
3992           },this);
3993         }
3994     }    
3995     
3996 });
3997
3998
3999
4000  
4001
4002  /*
4003  * - LGPL
4004  *
4005  * navbar
4006  * 
4007  */
4008
4009 /**
4010  * @class Roo.bootstrap.NavSidebar
4011  * @extends Roo.bootstrap.Navbar
4012  * Bootstrap Sidebar class
4013  * 
4014  * @constructor
4015  * Create a new Sidebar
4016  * @param {Object} config The config object
4017  */
4018
4019
4020 Roo.bootstrap.NavSidebar = function(config){
4021     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4022 };
4023
4024 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4025     
4026     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4027     
4028     getAutoCreate : function(){
4029         
4030         
4031         return  {
4032             tag: 'div',
4033             cls: 'sidebar sidebar-nav'
4034         };
4035     
4036         
4037     }
4038     
4039     
4040     
4041 });
4042
4043
4044
4045  
4046
4047  /*
4048  * - LGPL
4049  *
4050  * nav group
4051  * 
4052  */
4053
4054 /**
4055  * @class Roo.bootstrap.NavGroup
4056  * @extends Roo.bootstrap.Component
4057  * Bootstrap NavGroup class
4058  * @cfg {String} align (left|right)
4059  * @cfg {Boolean} inverse
4060  * @cfg {String} type (nav|pills|tab) default nav
4061  * @cfg {String} navId - reference Id for navbar.
4062
4063  * 
4064  * @constructor
4065  * Create a new nav group
4066  * @param {Object} config The config object
4067  */
4068
4069 Roo.bootstrap.NavGroup = function(config){
4070     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4071     this.navItems = [];
4072    
4073     Roo.bootstrap.NavGroup.register(this);
4074      this.addEvents({
4075         /**
4076              * @event changed
4077              * Fires when the active item changes
4078              * @param {Roo.bootstrap.NavGroup} this
4079              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4080              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4081          */
4082         'changed': true
4083      });
4084     
4085 };
4086
4087 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4088     
4089     align: '',
4090     inverse: false,
4091     form: false,
4092     type: 'nav',
4093     navId : '',
4094     // private
4095     
4096     navItems : false, 
4097     
4098     getAutoCreate : function()
4099     {
4100         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4101         
4102         cfg = {
4103             tag : 'ul',
4104             cls: 'nav' 
4105         };
4106         
4107         if (['tabs','pills'].indexOf(this.type)!==-1) {
4108             cfg.cls += ' nav-' + this.type
4109         } else {
4110             if (this.type!=='nav') {
4111                 Roo.log('nav type must be nav/tabs/pills')
4112             }
4113             cfg.cls += ' navbar-nav'
4114         }
4115         
4116         if (this.parent() && this.parent().sidebar) {
4117             cfg = {
4118                 tag: 'ul',
4119                 cls: 'dashboard-menu sidebar-menu'
4120             };
4121             
4122             return cfg;
4123         }
4124         
4125         if (this.form === true) {
4126             cfg = {
4127                 tag: 'form',
4128                 cls: 'navbar-form'
4129             };
4130             
4131             if (this.align === 'right') {
4132                 cfg.cls += ' navbar-right';
4133             } else {
4134                 cfg.cls += ' navbar-left';
4135             }
4136         }
4137         
4138         if (this.align === 'right') {
4139             cfg.cls += ' navbar-right';
4140         }
4141         
4142         if (this.inverse) {
4143             cfg.cls += ' navbar-inverse';
4144             
4145         }
4146         
4147         
4148         return cfg;
4149     },
4150     /**
4151     * sets the active Navigation item
4152     * @param {Roo.bootstrap.NavItem} the new current navitem
4153     */
4154     setActiveItem : function(item)
4155     {
4156         var prev = false;
4157         Roo.each(this.navItems, function(v){
4158             if (v == item) {
4159                 return ;
4160             }
4161             if (v.isActive()) {
4162                 v.setActive(false, true);
4163                 prev = v;
4164                 
4165             }
4166             
4167         });
4168
4169         item.setActive(true, true);
4170         this.fireEvent('changed', this, item, prev);
4171         
4172         
4173     },
4174     /**
4175     * gets the active Navigation item
4176     * @return {Roo.bootstrap.NavItem} the current navitem
4177     */
4178     getActive : function()
4179     {
4180         
4181         var prev = false;
4182         Roo.each(this.navItems, function(v){
4183             
4184             if (v.isActive()) {
4185                 prev = v;
4186                 
4187             }
4188             
4189         });
4190         return prev;
4191     },
4192     
4193     indexOfNav : function()
4194     {
4195         
4196         var prev = false;
4197         Roo.each(this.navItems, function(v,i){
4198             
4199             if (v.isActive()) {
4200                 prev = i;
4201                 
4202             }
4203             
4204         });
4205         return prev;
4206     },
4207     /**
4208     * adds a Navigation item
4209     * @param {Roo.bootstrap.NavItem} the navitem to add
4210     */
4211     addItem : function(cfg)
4212     {
4213         var cn = new Roo.bootstrap.NavItem(cfg);
4214         this.register(cn);
4215         cn.parentId = this.id;
4216         cn.onRender(this.el, null);
4217         return cn;
4218     },
4219     /**
4220     * register a Navigation item
4221     * @param {Roo.bootstrap.NavItem} the navitem to add
4222     */
4223     register : function(item)
4224     {
4225         this.navItems.push( item);
4226         item.navId = this.navId;
4227     
4228     },
4229     
4230     /**
4231     * clear all the Navigation item
4232     */
4233    
4234     clearAll : function()
4235     {
4236         this.navItems = [];
4237         this.el.dom.innerHTML = '';
4238     },
4239     
4240     getNavItem: function(tabId)
4241     {
4242         var ret = false;
4243         Roo.each(this.navItems, function(e) {
4244             if (e.tabId == tabId) {
4245                ret =  e;
4246                return false;
4247             }
4248             return true;
4249             
4250         });
4251         return ret;
4252     },
4253     
4254     setActiveNext : function()
4255     {
4256         var i = this.indexOfNav(this.getActive());
4257         if (i > this.navItems.length) {
4258             return;
4259         }
4260         this.setActiveItem(this.navItems[i+1]);
4261     },
4262     setActivePrev : function()
4263     {
4264         var i = this.indexOfNav(this.getActive());
4265         if (i  < 1) {
4266             return;
4267         }
4268         this.setActiveItem(this.navItems[i-1]);
4269     },
4270     clearWasActive : function(except) {
4271         Roo.each(this.navItems, function(e) {
4272             if (e.tabId != except.tabId && e.was_active) {
4273                e.was_active = false;
4274                return false;
4275             }
4276             return true;
4277             
4278         });
4279     },
4280     getWasActive : function ()
4281     {
4282         var r = false;
4283         Roo.each(this.navItems, function(e) {
4284             if (e.was_active) {
4285                r = e;
4286                return false;
4287             }
4288             return true;
4289             
4290         });
4291         return r;
4292     }
4293     
4294     
4295 });
4296
4297  
4298 Roo.apply(Roo.bootstrap.NavGroup, {
4299     
4300     groups: {},
4301      /**
4302     * register a Navigation Group
4303     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4304     */
4305     register : function(navgrp)
4306     {
4307         this.groups[navgrp.navId] = navgrp;
4308         
4309     },
4310     /**
4311     * fetch a Navigation Group based on the navigation ID
4312     * @param {string} the navgroup to add
4313     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4314     */
4315     get: function(navId) {
4316         if (typeof(this.groups[navId]) == 'undefined') {
4317             return false;
4318             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4319         }
4320         return this.groups[navId] ;
4321     }
4322     
4323     
4324     
4325 });
4326
4327  /*
4328  * - LGPL
4329  *
4330  * row
4331  * 
4332  */
4333
4334 /**
4335  * @class Roo.bootstrap.NavItem
4336  * @extends Roo.bootstrap.Component
4337  * Bootstrap Navbar.NavItem class
4338  * @cfg {String} href  link to
4339  * @cfg {String} html content of button
4340  * @cfg {String} badge text inside badge
4341  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4342  * @cfg {String} glyphicon name of glyphicon
4343  * @cfg {String} icon name of font awesome icon
4344  * @cfg {Boolean} active Is item active
4345  * @cfg {Boolean} disabled Is item disabled
4346  
4347  * @cfg {Boolean} preventDefault (true | false) default false
4348  * @cfg {String} tabId the tab that this item activates.
4349  * @cfg {String} tagtype (a|span) render as a href or span?
4350  * @cfg {Boolean} animateRef (true|false) link to element default false  
4351   
4352  * @constructor
4353  * Create a new Navbar Item
4354  * @param {Object} config The config object
4355  */
4356 Roo.bootstrap.NavItem = function(config){
4357     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4358     this.addEvents({
4359         // raw events
4360         /**
4361          * @event click
4362          * The raw click event for the entire grid.
4363          * @param {Roo.EventObject} e
4364          */
4365         "click" : true,
4366          /**
4367             * @event changed
4368             * Fires when the active item active state changes
4369             * @param {Roo.bootstrap.NavItem} this
4370             * @param {boolean} state the new state
4371              
4372          */
4373         'changed': true,
4374         /**
4375             * @event scrollto
4376             * Fires when scroll to element
4377             * @param {Roo.bootstrap.NavItem} this
4378             * @param {Object} options
4379             * @param {Roo.EventObject} e
4380              
4381          */
4382         'scrollto': true
4383     });
4384    
4385 };
4386
4387 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4388     
4389     href: false,
4390     html: '',
4391     badge: '',
4392     icon: false,
4393     glyphicon: false,
4394     active: false,
4395     preventDefault : false,
4396     tabId : false,
4397     tagtype : 'a',
4398     disabled : false,
4399     animateRef : false,
4400     was_active : false,
4401     
4402     getAutoCreate : function(){
4403          
4404         var cfg = {
4405             tag: 'li',
4406             cls: 'nav-item'
4407             
4408         };
4409         
4410         if (this.active) {
4411             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4412         }
4413         if (this.disabled) {
4414             cfg.cls += ' disabled';
4415         }
4416         
4417         if (this.href || this.html || this.glyphicon || this.icon) {
4418             cfg.cn = [
4419                 {
4420                     tag: this.tagtype,
4421                     href : this.href || "#",
4422                     html: this.html || ''
4423                 }
4424             ];
4425             
4426             if (this.icon) {
4427                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4428             }
4429
4430             if(this.glyphicon) {
4431                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4432             }
4433             
4434             if (this.menu) {
4435                 
4436                 cfg.cn[0].html += " <span class='caret'></span>";
4437              
4438             }
4439             
4440             if (this.badge !== '') {
4441                  
4442                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4443             }
4444         }
4445         
4446         
4447         
4448         return cfg;
4449     },
4450     initEvents: function() 
4451     {
4452         if (typeof (this.menu) != 'undefined') {
4453             this.menu.parentType = this.xtype;
4454             this.menu.triggerEl = this.el;
4455             this.menu = this.addxtype(Roo.apply({}, this.menu));
4456         }
4457         
4458         this.el.select('a',true).on('click', this.onClick, this);
4459         
4460         if(this.tagtype == 'span'){
4461             this.el.select('span',true).on('click', this.onClick, this);
4462         }
4463        
4464         // at this point parent should be available..
4465         this.parent().register(this);
4466     },
4467     
4468     onClick : function(e)
4469     {
4470         if (e.getTarget('.dropdown-menu-item')) {
4471             // did you click on a menu itemm.... - then don't trigger onclick..
4472             return;
4473         }
4474         
4475         if(
4476                 this.preventDefault || 
4477                 this.href == '#' 
4478         ){
4479             Roo.log("NavItem - prevent Default?");
4480             e.preventDefault();
4481         }
4482         
4483         if (this.disabled) {
4484             return;
4485         }
4486         
4487         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4488         if (tg && tg.transition) {
4489             Roo.log("waiting for the transitionend");
4490             return;
4491         }
4492         
4493         
4494         
4495         //Roo.log("fire event clicked");
4496         if(this.fireEvent('click', this, e) === false){
4497             return;
4498         };
4499         
4500         if(this.tagtype == 'span'){
4501             return;
4502         }
4503         
4504         //Roo.log(this.href);
4505         var ael = this.el.select('a',true).first();
4506         //Roo.log(ael);
4507         
4508         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4509             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4510             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4511                 return; // ignore... - it's a 'hash' to another page.
4512             }
4513             Roo.log("NavItem - prevent Default?");
4514             e.preventDefault();
4515             this.scrollToElement(e);
4516         }
4517         
4518         
4519         var p =  this.parent();
4520    
4521         if (['tabs','pills'].indexOf(p.type)!==-1) {
4522             if (typeof(p.setActiveItem) !== 'undefined') {
4523                 p.setActiveItem(this);
4524             }
4525         }
4526         
4527         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4528         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4529             // remove the collapsed menu expand...
4530             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4531         }
4532     },
4533     
4534     isActive: function () {
4535         return this.active
4536     },
4537     setActive : function(state, fire, is_was_active)
4538     {
4539         if (this.active && !state && this.navId) {
4540             this.was_active = true;
4541             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4542             if (nv) {
4543                 nv.clearWasActive(this);
4544             }
4545             
4546         }
4547         this.active = state;
4548         
4549         if (!state ) {
4550             this.el.removeClass('active');
4551         } else if (!this.el.hasClass('active')) {
4552             this.el.addClass('active');
4553         }
4554         if (fire) {
4555             this.fireEvent('changed', this, state);
4556         }
4557         
4558         // show a panel if it's registered and related..
4559         
4560         if (!this.navId || !this.tabId || !state || is_was_active) {
4561             return;
4562         }
4563         
4564         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4565         if (!tg) {
4566             return;
4567         }
4568         var pan = tg.getPanelByName(this.tabId);
4569         if (!pan) {
4570             return;
4571         }
4572         // if we can not flip to new panel - go back to old nav highlight..
4573         if (false == tg.showPanel(pan)) {
4574             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4575             if (nv) {
4576                 var onav = nv.getWasActive();
4577                 if (onav) {
4578                     onav.setActive(true, false, true);
4579                 }
4580             }
4581             
4582         }
4583         
4584         
4585         
4586     },
4587      // this should not be here...
4588     setDisabled : function(state)
4589     {
4590         this.disabled = state;
4591         if (!state ) {
4592             this.el.removeClass('disabled');
4593         } else if (!this.el.hasClass('disabled')) {
4594             this.el.addClass('disabled');
4595         }
4596         
4597     },
4598     
4599     /**
4600      * Fetch the element to display the tooltip on.
4601      * @return {Roo.Element} defaults to this.el
4602      */
4603     tooltipEl : function()
4604     {
4605         return this.el.select('' + this.tagtype + '', true).first();
4606     },
4607     
4608     scrollToElement : function(e)
4609     {
4610         var c = document.body;
4611         
4612         /*
4613          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4614          */
4615         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4616             c = document.documentElement;
4617         }
4618         
4619         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4620         
4621         if(!target){
4622             return;
4623         }
4624
4625         var o = target.calcOffsetsTo(c);
4626         
4627         var options = {
4628             target : target,
4629             value : o[1]
4630         };
4631         
4632         this.fireEvent('scrollto', this, options, e);
4633         
4634         Roo.get(c).scrollTo('top', options.value, true);
4635         
4636         return;
4637     }
4638 });
4639  
4640
4641  /*
4642  * - LGPL
4643  *
4644  * sidebar item
4645  *
4646  *  li
4647  *    <span> icon </span>
4648  *    <span> text </span>
4649  *    <span>badge </span>
4650  */
4651
4652 /**
4653  * @class Roo.bootstrap.NavSidebarItem
4654  * @extends Roo.bootstrap.NavItem
4655  * Bootstrap Navbar.NavSidebarItem class
4656  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4657  * {Boolean} open is the menu open
4658  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4659  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4660  * {String} buttonSize (sm|md|lg)the extra classes for the button
4661  * {Boolean} showArrow show arrow next to the text (default true)
4662  * @constructor
4663  * Create a new Navbar Button
4664  * @param {Object} config The config object
4665  */
4666 Roo.bootstrap.NavSidebarItem = function(config){
4667     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4668     this.addEvents({
4669         // raw events
4670         /**
4671          * @event click
4672          * The raw click event for the entire grid.
4673          * @param {Roo.EventObject} e
4674          */
4675         "click" : true,
4676          /**
4677             * @event changed
4678             * Fires when the active item active state changes
4679             * @param {Roo.bootstrap.NavSidebarItem} this
4680             * @param {boolean} state the new state
4681              
4682          */
4683         'changed': true
4684     });
4685    
4686 };
4687
4688 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4689     
4690     badgeWeight : 'default',
4691     
4692     open: false,
4693     
4694     buttonView : false,
4695     
4696     buttonWeight : 'default',
4697     
4698     buttonSize : 'md',
4699     
4700     showArrow : true,
4701     
4702     getAutoCreate : function(){
4703         
4704         
4705         var a = {
4706                 tag: 'a',
4707                 href : this.href || '#',
4708                 cls: '',
4709                 html : '',
4710                 cn : []
4711         };
4712         
4713         if(this.buttonView){
4714             a = {
4715                 tag: 'button',
4716                 href : this.href || '#',
4717                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4718                 html : this.html,
4719                 cn : []
4720             };
4721         }
4722         
4723         var cfg = {
4724             tag: 'li',
4725             cls: '',
4726             cn: [ a ]
4727         };
4728         
4729         if (this.active) {
4730             cfg.cls += ' active';
4731         }
4732         
4733         if (this.disabled) {
4734             cfg.cls += ' disabled';
4735         }
4736         if (this.open) {
4737             cfg.cls += ' open x-open';
4738         }
4739         // left icon..
4740         if (this.glyphicon || this.icon) {
4741             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4742             a.cn.push({ tag : 'i', cls : c }) ;
4743         }
4744         
4745         if(!this.buttonView){
4746             var span = {
4747                 tag: 'span',
4748                 html : this.html || ''
4749             };
4750
4751             a.cn.push(span);
4752             
4753         }
4754         
4755         if (this.badge !== '') {
4756             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4757         }
4758         
4759         if (this.menu) {
4760             
4761             if(this.showArrow){
4762                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4763             }
4764             
4765             a.cls += ' dropdown-toggle treeview' ;
4766         }
4767         
4768         return cfg;
4769     },
4770     
4771     initEvents : function()
4772     { 
4773         if (typeof (this.menu) != 'undefined') {
4774             this.menu.parentType = this.xtype;
4775             this.menu.triggerEl = this.el;
4776             this.menu = this.addxtype(Roo.apply({}, this.menu));
4777         }
4778         
4779         this.el.on('click', this.onClick, this);
4780         
4781         if(this.badge !== ''){
4782             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4783         }
4784         
4785     },
4786     
4787     onClick : function(e)
4788     {
4789         if(this.disabled){
4790             e.preventDefault();
4791             return;
4792         }
4793         
4794         if(this.preventDefault){
4795             e.preventDefault();
4796         }
4797         
4798         this.fireEvent('click', this);
4799     },
4800     
4801     disable : function()
4802     {
4803         this.setDisabled(true);
4804     },
4805     
4806     enable : function()
4807     {
4808         this.setDisabled(false);
4809     },
4810     
4811     setDisabled : function(state)
4812     {
4813         if(this.disabled == state){
4814             return;
4815         }
4816         
4817         this.disabled = state;
4818         
4819         if (state) {
4820             this.el.addClass('disabled');
4821             return;
4822         }
4823         
4824         this.el.removeClass('disabled');
4825         
4826         return;
4827     },
4828     
4829     setActive : function(state)
4830     {
4831         if(this.active == state){
4832             return;
4833         }
4834         
4835         this.active = state;
4836         
4837         if (state) {
4838             this.el.addClass('active');
4839             return;
4840         }
4841         
4842         this.el.removeClass('active');
4843         
4844         return;
4845     },
4846     
4847     isActive: function () 
4848     {
4849         return this.active;
4850     },
4851     
4852     setBadge : function(str)
4853     {
4854         if(!this.badgeEl){
4855             return;
4856         }
4857         
4858         this.badgeEl.dom.innerHTML = str;
4859     }
4860     
4861    
4862      
4863  
4864 });
4865  
4866
4867  /*
4868  * - LGPL
4869  *
4870  * row
4871  * 
4872  */
4873
4874 /**
4875  * @class Roo.bootstrap.Row
4876  * @extends Roo.bootstrap.Component
4877  * Bootstrap Row class (contains columns...)
4878  * 
4879  * @constructor
4880  * Create a new Row
4881  * @param {Object} config The config object
4882  */
4883
4884 Roo.bootstrap.Row = function(config){
4885     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4886 };
4887
4888 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4889     
4890     getAutoCreate : function(){
4891        return {
4892             cls: 'row clearfix'
4893        };
4894     }
4895     
4896     
4897 });
4898
4899  
4900
4901  /*
4902  * - LGPL
4903  *
4904  * element
4905  * 
4906  */
4907
4908 /**
4909  * @class Roo.bootstrap.Element
4910  * @extends Roo.bootstrap.Component
4911  * Bootstrap Element class
4912  * @cfg {String} html contents of the element
4913  * @cfg {String} tag tag of the element
4914  * @cfg {String} cls class of the element
4915  * @cfg {Boolean} preventDefault (true|false) default false
4916  * @cfg {Boolean} clickable (true|false) default false
4917  * 
4918  * @constructor
4919  * Create a new Element
4920  * @param {Object} config The config object
4921  */
4922
4923 Roo.bootstrap.Element = function(config){
4924     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4925     
4926     this.addEvents({
4927         // raw events
4928         /**
4929          * @event click
4930          * When a element is chick
4931          * @param {Roo.bootstrap.Element} this
4932          * @param {Roo.EventObject} e
4933          */
4934         "click" : true
4935     });
4936 };
4937
4938 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4939     
4940     tag: 'div',
4941     cls: '',
4942     html: '',
4943     preventDefault: false, 
4944     clickable: false,
4945     
4946     getAutoCreate : function(){
4947         
4948         var cfg = {
4949             tag: this.tag,
4950             // cls: this.cls, double assign in parent class Component.js :: onRender
4951             html: this.html
4952         };
4953         
4954         return cfg;
4955     },
4956     
4957     initEvents: function() 
4958     {
4959         Roo.bootstrap.Element.superclass.initEvents.call(this);
4960         
4961         if(this.clickable){
4962             this.el.on('click', this.onClick, this);
4963         }
4964         
4965     },
4966     
4967     onClick : function(e)
4968     {
4969         if(this.preventDefault){
4970             e.preventDefault();
4971         }
4972         
4973         this.fireEvent('click', this, e);
4974     },
4975     
4976     getValue : function()
4977     {
4978         return this.el.dom.innerHTML;
4979     },
4980     
4981     setValue : function(value)
4982     {
4983         this.el.dom.innerHTML = value;
4984     }
4985    
4986 });
4987
4988  
4989
4990  /*
4991  * - LGPL
4992  *
4993  * pagination
4994  * 
4995  */
4996
4997 /**
4998  * @class Roo.bootstrap.Pagination
4999  * @extends Roo.bootstrap.Component
5000  * Bootstrap Pagination class
5001  * @cfg {String} size xs | sm | md | lg
5002  * @cfg {Boolean} inverse false | true
5003  * 
5004  * @constructor
5005  * Create a new Pagination
5006  * @param {Object} config The config object
5007  */
5008
5009 Roo.bootstrap.Pagination = function(config){
5010     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5011 };
5012
5013 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5014     
5015     cls: false,
5016     size: false,
5017     inverse: false,
5018     
5019     getAutoCreate : function(){
5020         var cfg = {
5021             tag: 'ul',
5022                 cls: 'pagination'
5023         };
5024         if (this.inverse) {
5025             cfg.cls += ' inverse';
5026         }
5027         if (this.html) {
5028             cfg.html=this.html;
5029         }
5030         if (this.cls) {
5031             cfg.cls += " " + this.cls;
5032         }
5033         return cfg;
5034     }
5035    
5036 });
5037
5038  
5039
5040  /*
5041  * - LGPL
5042  *
5043  * Pagination item
5044  * 
5045  */
5046
5047
5048 /**
5049  * @class Roo.bootstrap.PaginationItem
5050  * @extends Roo.bootstrap.Component
5051  * Bootstrap PaginationItem class
5052  * @cfg {String} html text
5053  * @cfg {String} href the link
5054  * @cfg {Boolean} preventDefault (true | false) default true
5055  * @cfg {Boolean} active (true | false) default false
5056  * @cfg {Boolean} disabled default false
5057  * 
5058  * 
5059  * @constructor
5060  * Create a new PaginationItem
5061  * @param {Object} config The config object
5062  */
5063
5064
5065 Roo.bootstrap.PaginationItem = function(config){
5066     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5067     this.addEvents({
5068         // raw events
5069         /**
5070          * @event click
5071          * The raw click event for the entire grid.
5072          * @param {Roo.EventObject} e
5073          */
5074         "click" : true
5075     });
5076 };
5077
5078 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5079     
5080     href : false,
5081     html : false,
5082     preventDefault: true,
5083     active : false,
5084     cls : false,
5085     disabled: false,
5086     
5087     getAutoCreate : function(){
5088         var cfg= {
5089             tag: 'li',
5090             cn: [
5091                 {
5092                     tag : 'a',
5093                     href : this.href ? this.href : '#',
5094                     html : this.html ? this.html : ''
5095                 }
5096             ]
5097         };
5098         
5099         if(this.cls){
5100             cfg.cls = this.cls;
5101         }
5102         
5103         if(this.disabled){
5104             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5105         }
5106         
5107         if(this.active){
5108             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5109         }
5110         
5111         return cfg;
5112     },
5113     
5114     initEvents: function() {
5115         
5116         this.el.on('click', this.onClick, this);
5117         
5118     },
5119     onClick : function(e)
5120     {
5121         Roo.log('PaginationItem on click ');
5122         if(this.preventDefault){
5123             e.preventDefault();
5124         }
5125         
5126         if(this.disabled){
5127             return;
5128         }
5129         
5130         this.fireEvent('click', this, e);
5131     }
5132    
5133 });
5134
5135  
5136
5137  /*
5138  * - LGPL
5139  *
5140  * slider
5141  * 
5142  */
5143
5144
5145 /**
5146  * @class Roo.bootstrap.Slider
5147  * @extends Roo.bootstrap.Component
5148  * Bootstrap Slider class
5149  *    
5150  * @constructor
5151  * Create a new Slider
5152  * @param {Object} config The config object
5153  */
5154
5155 Roo.bootstrap.Slider = function(config){
5156     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5157 };
5158
5159 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5160     
5161     getAutoCreate : function(){
5162         
5163         var cfg = {
5164             tag: 'div',
5165             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5166             cn: [
5167                 {
5168                     tag: 'a',
5169                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5170                 }
5171             ]
5172         };
5173         
5174         return cfg;
5175     }
5176    
5177 });
5178
5179  /*
5180  * Based on:
5181  * Ext JS Library 1.1.1
5182  * Copyright(c) 2006-2007, Ext JS, LLC.
5183  *
5184  * Originally Released Under LGPL - original licence link has changed is not relivant.
5185  *
5186  * Fork - LGPL
5187  * <script type="text/javascript">
5188  */
5189  
5190
5191 /**
5192  * @class Roo.grid.ColumnModel
5193  * @extends Roo.util.Observable
5194  * This is the default implementation of a ColumnModel used by the Grid. It defines
5195  * the columns in the grid.
5196  * <br>Usage:<br>
5197  <pre><code>
5198  var colModel = new Roo.grid.ColumnModel([
5199         {header: "Ticker", width: 60, sortable: true, locked: true},
5200         {header: "Company Name", width: 150, sortable: true},
5201         {header: "Market Cap.", width: 100, sortable: true},
5202         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5203         {header: "Employees", width: 100, sortable: true, resizable: false}
5204  ]);
5205  </code></pre>
5206  * <p>
5207  
5208  * The config options listed for this class are options which may appear in each
5209  * individual column definition.
5210  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5211  * @constructor
5212  * @param {Object} config An Array of column config objects. See this class's
5213  * config objects for details.
5214 */
5215 Roo.grid.ColumnModel = function(config){
5216         /**
5217      * The config passed into the constructor
5218      */
5219     this.config = config;
5220     this.lookup = {};
5221
5222     // if no id, create one
5223     // if the column does not have a dataIndex mapping,
5224     // map it to the order it is in the config
5225     for(var i = 0, len = config.length; i < len; i++){
5226         var c = config[i];
5227         if(typeof c.dataIndex == "undefined"){
5228             c.dataIndex = i;
5229         }
5230         if(typeof c.renderer == "string"){
5231             c.renderer = Roo.util.Format[c.renderer];
5232         }
5233         if(typeof c.id == "undefined"){
5234             c.id = Roo.id();
5235         }
5236         if(c.editor && c.editor.xtype){
5237             c.editor  = Roo.factory(c.editor, Roo.grid);
5238         }
5239         if(c.editor && c.editor.isFormField){
5240             c.editor = new Roo.grid.GridEditor(c.editor);
5241         }
5242         this.lookup[c.id] = c;
5243     }
5244
5245     /**
5246      * The width of columns which have no width specified (defaults to 100)
5247      * @type Number
5248      */
5249     this.defaultWidth = 100;
5250
5251     /**
5252      * Default sortable of columns which have no sortable specified (defaults to false)
5253      * @type Boolean
5254      */
5255     this.defaultSortable = false;
5256
5257     this.addEvents({
5258         /**
5259              * @event widthchange
5260              * Fires when the width of a column changes.
5261              * @param {ColumnModel} this
5262              * @param {Number} columnIndex The column index
5263              * @param {Number} newWidth The new width
5264              */
5265             "widthchange": true,
5266         /**
5267              * @event headerchange
5268              * Fires when the text of a header changes.
5269              * @param {ColumnModel} this
5270              * @param {Number} columnIndex The column index
5271              * @param {Number} newText The new header text
5272              */
5273             "headerchange": true,
5274         /**
5275              * @event hiddenchange
5276              * Fires when a column is hidden or "unhidden".
5277              * @param {ColumnModel} this
5278              * @param {Number} columnIndex The column index
5279              * @param {Boolean} hidden true if hidden, false otherwise
5280              */
5281             "hiddenchange": true,
5282             /**
5283          * @event columnmoved
5284          * Fires when a column is moved.
5285          * @param {ColumnModel} this
5286          * @param {Number} oldIndex
5287          * @param {Number} newIndex
5288          */
5289         "columnmoved" : true,
5290         /**
5291          * @event columlockchange
5292          * Fires when a column's locked state is changed
5293          * @param {ColumnModel} this
5294          * @param {Number} colIndex
5295          * @param {Boolean} locked true if locked
5296          */
5297         "columnlockchange" : true
5298     });
5299     Roo.grid.ColumnModel.superclass.constructor.call(this);
5300 };
5301 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5302     /**
5303      * @cfg {String} header The header text to display in the Grid view.
5304      */
5305     /**
5306      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5307      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5308      * specified, the column's index is used as an index into the Record's data Array.
5309      */
5310     /**
5311      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5312      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5313      */
5314     /**
5315      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5316      * Defaults to the value of the {@link #defaultSortable} property.
5317      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5318      */
5319     /**
5320      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5321      */
5322     /**
5323      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5324      */
5325     /**
5326      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5327      */
5328     /**
5329      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5330      */
5331     /**
5332      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5333      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5334      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5335      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5336      */
5337        /**
5338      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5339      */
5340     /**
5341      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5342      */
5343     /**
5344      * @cfg {String} cursor (Optional)
5345      */
5346     /**
5347      * @cfg {String} tooltip (Optional)
5348      */
5349     /**
5350      * @cfg {Number} xs (Optional)
5351      */
5352     /**
5353      * @cfg {Number} sm (Optional)
5354      */
5355     /**
5356      * @cfg {Number} md (Optional)
5357      */
5358     /**
5359      * @cfg {Number} lg (Optional)
5360      */
5361     /**
5362      * Returns the id of the column at the specified index.
5363      * @param {Number} index The column index
5364      * @return {String} the id
5365      */
5366     getColumnId : function(index){
5367         return this.config[index].id;
5368     },
5369
5370     /**
5371      * Returns the column for a specified id.
5372      * @param {String} id The column id
5373      * @return {Object} the column
5374      */
5375     getColumnById : function(id){
5376         return this.lookup[id];
5377     },
5378
5379     
5380     /**
5381      * Returns the column for a specified dataIndex.
5382      * @param {String} dataIndex The column dataIndex
5383      * @return {Object|Boolean} the column or false if not found
5384      */
5385     getColumnByDataIndex: function(dataIndex){
5386         var index = this.findColumnIndex(dataIndex);
5387         return index > -1 ? this.config[index] : false;
5388     },
5389     
5390     /**
5391      * Returns the index for a specified column id.
5392      * @param {String} id The column id
5393      * @return {Number} the index, or -1 if not found
5394      */
5395     getIndexById : function(id){
5396         for(var i = 0, len = this.config.length; i < len; i++){
5397             if(this.config[i].id == id){
5398                 return i;
5399             }
5400         }
5401         return -1;
5402     },
5403     
5404     /**
5405      * Returns the index for a specified column dataIndex.
5406      * @param {String} dataIndex The column dataIndex
5407      * @return {Number} the index, or -1 if not found
5408      */
5409     
5410     findColumnIndex : function(dataIndex){
5411         for(var i = 0, len = this.config.length; i < len; i++){
5412             if(this.config[i].dataIndex == dataIndex){
5413                 return i;
5414             }
5415         }
5416         return -1;
5417     },
5418     
5419     
5420     moveColumn : function(oldIndex, newIndex){
5421         var c = this.config[oldIndex];
5422         this.config.splice(oldIndex, 1);
5423         this.config.splice(newIndex, 0, c);
5424         this.dataMap = null;
5425         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5426     },
5427
5428     isLocked : function(colIndex){
5429         return this.config[colIndex].locked === true;
5430     },
5431
5432     setLocked : function(colIndex, value, suppressEvent){
5433         if(this.isLocked(colIndex) == value){
5434             return;
5435         }
5436         this.config[colIndex].locked = value;
5437         if(!suppressEvent){
5438             this.fireEvent("columnlockchange", this, colIndex, value);
5439         }
5440     },
5441
5442     getTotalLockedWidth : function(){
5443         var totalWidth = 0;
5444         for(var i = 0; i < this.config.length; i++){
5445             if(this.isLocked(i) && !this.isHidden(i)){
5446                 this.totalWidth += this.getColumnWidth(i);
5447             }
5448         }
5449         return totalWidth;
5450     },
5451
5452     getLockedCount : function(){
5453         for(var i = 0, len = this.config.length; i < len; i++){
5454             if(!this.isLocked(i)){
5455                 return i;
5456             }
5457         }
5458         
5459         return this.config.length;
5460     },
5461
5462     /**
5463      * Returns the number of columns.
5464      * @return {Number}
5465      */
5466     getColumnCount : function(visibleOnly){
5467         if(visibleOnly === true){
5468             var c = 0;
5469             for(var i = 0, len = this.config.length; i < len; i++){
5470                 if(!this.isHidden(i)){
5471                     c++;
5472                 }
5473             }
5474             return c;
5475         }
5476         return this.config.length;
5477     },
5478
5479     /**
5480      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5481      * @param {Function} fn
5482      * @param {Object} scope (optional)
5483      * @return {Array} result
5484      */
5485     getColumnsBy : function(fn, scope){
5486         var r = [];
5487         for(var i = 0, len = this.config.length; i < len; i++){
5488             var c = this.config[i];
5489             if(fn.call(scope||this, c, i) === true){
5490                 r[r.length] = c;
5491             }
5492         }
5493         return r;
5494     },
5495
5496     /**
5497      * Returns true if the specified column is sortable.
5498      * @param {Number} col The column index
5499      * @return {Boolean}
5500      */
5501     isSortable : function(col){
5502         if(typeof this.config[col].sortable == "undefined"){
5503             return this.defaultSortable;
5504         }
5505         return this.config[col].sortable;
5506     },
5507
5508     /**
5509      * Returns the rendering (formatting) function defined for the column.
5510      * @param {Number} col The column index.
5511      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5512      */
5513     getRenderer : function(col){
5514         if(!this.config[col].renderer){
5515             return Roo.grid.ColumnModel.defaultRenderer;
5516         }
5517         return this.config[col].renderer;
5518     },
5519
5520     /**
5521      * Sets the rendering (formatting) function for a column.
5522      * @param {Number} col The column index
5523      * @param {Function} fn The function to use to process the cell's raw data
5524      * to return HTML markup for the grid view. The render function is called with
5525      * the following parameters:<ul>
5526      * <li>Data value.</li>
5527      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5528      * <li>css A CSS style string to apply to the table cell.</li>
5529      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5530      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5531      * <li>Row index</li>
5532      * <li>Column index</li>
5533      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5534      */
5535     setRenderer : function(col, fn){
5536         this.config[col].renderer = fn;
5537     },
5538
5539     /**
5540      * Returns the width for the specified column.
5541      * @param {Number} col The column index
5542      * @return {Number}
5543      */
5544     getColumnWidth : function(col){
5545         return this.config[col].width * 1 || this.defaultWidth;
5546     },
5547
5548     /**
5549      * Sets the width for a column.
5550      * @param {Number} col The column index
5551      * @param {Number} width The new width
5552      */
5553     setColumnWidth : function(col, width, suppressEvent){
5554         this.config[col].width = width;
5555         this.totalWidth = null;
5556         if(!suppressEvent){
5557              this.fireEvent("widthchange", this, col, width);
5558         }
5559     },
5560
5561     /**
5562      * Returns the total width of all columns.
5563      * @param {Boolean} includeHidden True to include hidden column widths
5564      * @return {Number}
5565      */
5566     getTotalWidth : function(includeHidden){
5567         if(!this.totalWidth){
5568             this.totalWidth = 0;
5569             for(var i = 0, len = this.config.length; i < len; i++){
5570                 if(includeHidden || !this.isHidden(i)){
5571                     this.totalWidth += this.getColumnWidth(i);
5572                 }
5573             }
5574         }
5575         return this.totalWidth;
5576     },
5577
5578     /**
5579      * Returns the header for the specified column.
5580      * @param {Number} col The column index
5581      * @return {String}
5582      */
5583     getColumnHeader : function(col){
5584         return this.config[col].header;
5585     },
5586
5587     /**
5588      * Sets the header for a column.
5589      * @param {Number} col The column index
5590      * @param {String} header The new header
5591      */
5592     setColumnHeader : function(col, header){
5593         this.config[col].header = header;
5594         this.fireEvent("headerchange", this, col, header);
5595     },
5596
5597     /**
5598      * Returns the tooltip for the specified column.
5599      * @param {Number} col The column index
5600      * @return {String}
5601      */
5602     getColumnTooltip : function(col){
5603             return this.config[col].tooltip;
5604     },
5605     /**
5606      * Sets the tooltip for a column.
5607      * @param {Number} col The column index
5608      * @param {String} tooltip The new tooltip
5609      */
5610     setColumnTooltip : function(col, tooltip){
5611             this.config[col].tooltip = tooltip;
5612     },
5613
5614     /**
5615      * Returns the dataIndex for the specified column.
5616      * @param {Number} col The column index
5617      * @return {Number}
5618      */
5619     getDataIndex : function(col){
5620         return this.config[col].dataIndex;
5621     },
5622
5623     /**
5624      * Sets the dataIndex for a column.
5625      * @param {Number} col The column index
5626      * @param {Number} dataIndex The new dataIndex
5627      */
5628     setDataIndex : function(col, dataIndex){
5629         this.config[col].dataIndex = dataIndex;
5630     },
5631
5632     
5633     
5634     /**
5635      * Returns true if the cell is editable.
5636      * @param {Number} colIndex The column index
5637      * @param {Number} rowIndex The row index - this is nto actually used..?
5638      * @return {Boolean}
5639      */
5640     isCellEditable : function(colIndex, rowIndex){
5641         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5642     },
5643
5644     /**
5645      * Returns the editor defined for the cell/column.
5646      * return false or null to disable editing.
5647      * @param {Number} colIndex The column index
5648      * @param {Number} rowIndex The row index
5649      * @return {Object}
5650      */
5651     getCellEditor : function(colIndex, rowIndex){
5652         return this.config[colIndex].editor;
5653     },
5654
5655     /**
5656      * Sets if a column is editable.
5657      * @param {Number} col The column index
5658      * @param {Boolean} editable True if the column is editable
5659      */
5660     setEditable : function(col, editable){
5661         this.config[col].editable = editable;
5662     },
5663
5664
5665     /**
5666      * Returns true if the column is hidden.
5667      * @param {Number} colIndex The column index
5668      * @return {Boolean}
5669      */
5670     isHidden : function(colIndex){
5671         return this.config[colIndex].hidden;
5672     },
5673
5674
5675     /**
5676      * Returns true if the column width cannot be changed
5677      */
5678     isFixed : function(colIndex){
5679         return this.config[colIndex].fixed;
5680     },
5681
5682     /**
5683      * Returns true if the column can be resized
5684      * @return {Boolean}
5685      */
5686     isResizable : function(colIndex){
5687         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5688     },
5689     /**
5690      * Sets if a column is hidden.
5691      * @param {Number} colIndex The column index
5692      * @param {Boolean} hidden True if the column is hidden
5693      */
5694     setHidden : function(colIndex, hidden){
5695         this.config[colIndex].hidden = hidden;
5696         this.totalWidth = null;
5697         this.fireEvent("hiddenchange", this, colIndex, hidden);
5698     },
5699
5700     /**
5701      * Sets the editor for a column.
5702      * @param {Number} col The column index
5703      * @param {Object} editor The editor object
5704      */
5705     setEditor : function(col, editor){
5706         this.config[col].editor = editor;
5707     }
5708 });
5709
5710 Roo.grid.ColumnModel.defaultRenderer = function(value)
5711 {
5712     if(typeof value == "object") {
5713         return value;
5714     }
5715         if(typeof value == "string" && value.length < 1){
5716             return "&#160;";
5717         }
5718     
5719         return String.format("{0}", value);
5720 };
5721
5722 // Alias for backwards compatibility
5723 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5724 /*
5725  * Based on:
5726  * Ext JS Library 1.1.1
5727  * Copyright(c) 2006-2007, Ext JS, LLC.
5728  *
5729  * Originally Released Under LGPL - original licence link has changed is not relivant.
5730  *
5731  * Fork - LGPL
5732  * <script type="text/javascript">
5733  */
5734  
5735 /**
5736  * @class Roo.LoadMask
5737  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5738  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5739  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5740  * element's UpdateManager load indicator and will be destroyed after the initial load.
5741  * @constructor
5742  * Create a new LoadMask
5743  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5744  * @param {Object} config The config object
5745  */
5746 Roo.LoadMask = function(el, config){
5747     this.el = Roo.get(el);
5748     Roo.apply(this, config);
5749     if(this.store){
5750         this.store.on('beforeload', this.onBeforeLoad, this);
5751         this.store.on('load', this.onLoad, this);
5752         this.store.on('loadexception', this.onLoadException, this);
5753         this.removeMask = false;
5754     }else{
5755         var um = this.el.getUpdateManager();
5756         um.showLoadIndicator = false; // disable the default indicator
5757         um.on('beforeupdate', this.onBeforeLoad, this);
5758         um.on('update', this.onLoad, this);
5759         um.on('failure', this.onLoad, this);
5760         this.removeMask = true;
5761     }
5762 };
5763
5764 Roo.LoadMask.prototype = {
5765     /**
5766      * @cfg {Boolean} removeMask
5767      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5768      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5769      */
5770     /**
5771      * @cfg {String} msg
5772      * The text to display in a centered loading message box (defaults to 'Loading...')
5773      */
5774     msg : 'Loading...',
5775     /**
5776      * @cfg {String} msgCls
5777      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5778      */
5779     msgCls : 'x-mask-loading',
5780
5781     /**
5782      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5783      * @type Boolean
5784      */
5785     disabled: false,
5786
5787     /**
5788      * Disables the mask to prevent it from being displayed
5789      */
5790     disable : function(){
5791        this.disabled = true;
5792     },
5793
5794     /**
5795      * Enables the mask so that it can be displayed
5796      */
5797     enable : function(){
5798         this.disabled = false;
5799     },
5800     
5801     onLoadException : function()
5802     {
5803         Roo.log(arguments);
5804         
5805         if (typeof(arguments[3]) != 'undefined') {
5806             Roo.MessageBox.alert("Error loading",arguments[3]);
5807         } 
5808         /*
5809         try {
5810             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5811                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5812             }   
5813         } catch(e) {
5814             
5815         }
5816         */
5817     
5818         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5819     },
5820     // private
5821     onLoad : function()
5822     {
5823         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5824     },
5825
5826     // private
5827     onBeforeLoad : function(){
5828         if(!this.disabled){
5829             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5830         }
5831     },
5832
5833     // private
5834     destroy : function(){
5835         if(this.store){
5836             this.store.un('beforeload', this.onBeforeLoad, this);
5837             this.store.un('load', this.onLoad, this);
5838             this.store.un('loadexception', this.onLoadException, this);
5839         }else{
5840             var um = this.el.getUpdateManager();
5841             um.un('beforeupdate', this.onBeforeLoad, this);
5842             um.un('update', this.onLoad, this);
5843             um.un('failure', this.onLoad, this);
5844         }
5845     }
5846 };/*
5847  * - LGPL
5848  *
5849  * table
5850  * 
5851  */
5852
5853 /**
5854  * @class Roo.bootstrap.Table
5855  * @extends Roo.bootstrap.Component
5856  * Bootstrap Table class
5857  * @cfg {String} cls table class
5858  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5859  * @cfg {String} bgcolor Specifies the background color for a table
5860  * @cfg {Number} border Specifies whether the table cells should have borders or not
5861  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5862  * @cfg {Number} cellspacing Specifies the space between cells
5863  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5864  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5865  * @cfg {String} sortable Specifies that the table should be sortable
5866  * @cfg {String} summary Specifies a summary of the content of a table
5867  * @cfg {Number} width Specifies the width of a table
5868  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5869  * 
5870  * @cfg {boolean} striped Should the rows be alternative striped
5871  * @cfg {boolean} bordered Add borders to the table
5872  * @cfg {boolean} hover Add hover highlighting
5873  * @cfg {boolean} condensed Format condensed
5874  * @cfg {boolean} responsive Format condensed
5875  * @cfg {Boolean} loadMask (true|false) default false
5876  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5877  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5878  * @cfg {Boolean} rowSelection (true|false) default false
5879  * @cfg {Boolean} cellSelection (true|false) default false
5880  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5881  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5882  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5883  
5884  * 
5885  * @constructor
5886  * Create a new Table
5887  * @param {Object} config The config object
5888  */
5889
5890 Roo.bootstrap.Table = function(config){
5891     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5892     
5893   
5894     
5895     // BC...
5896     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5897     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5898     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5899     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5900     
5901     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5902     if (this.sm) {
5903         this.sm.grid = this;
5904         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5905         this.sm = this.selModel;
5906         this.sm.xmodule = this.xmodule || false;
5907     }
5908     
5909     if (this.cm && typeof(this.cm.config) == 'undefined') {
5910         this.colModel = new Roo.grid.ColumnModel(this.cm);
5911         this.cm = this.colModel;
5912         this.cm.xmodule = this.xmodule || false;
5913     }
5914     if (this.store) {
5915         this.store= Roo.factory(this.store, Roo.data);
5916         this.ds = this.store;
5917         this.ds.xmodule = this.xmodule || false;
5918          
5919     }
5920     if (this.footer && this.store) {
5921         this.footer.dataSource = this.ds;
5922         this.footer = Roo.factory(this.footer);
5923     }
5924     
5925     /** @private */
5926     this.addEvents({
5927         /**
5928          * @event cellclick
5929          * Fires when a cell is clicked
5930          * @param {Roo.bootstrap.Table} this
5931          * @param {Roo.Element} el
5932          * @param {Number} rowIndex
5933          * @param {Number} columnIndex
5934          * @param {Roo.EventObject} e
5935          */
5936         "cellclick" : true,
5937         /**
5938          * @event celldblclick
5939          * Fires when a cell is double clicked
5940          * @param {Roo.bootstrap.Table} this
5941          * @param {Roo.Element} el
5942          * @param {Number} rowIndex
5943          * @param {Number} columnIndex
5944          * @param {Roo.EventObject} e
5945          */
5946         "celldblclick" : true,
5947         /**
5948          * @event rowclick
5949          * Fires when a row is clicked
5950          * @param {Roo.bootstrap.Table} this
5951          * @param {Roo.Element} el
5952          * @param {Number} rowIndex
5953          * @param {Roo.EventObject} e
5954          */
5955         "rowclick" : true,
5956         /**
5957          * @event rowdblclick
5958          * Fires when a row is double clicked
5959          * @param {Roo.bootstrap.Table} this
5960          * @param {Roo.Element} el
5961          * @param {Number} rowIndex
5962          * @param {Roo.EventObject} e
5963          */
5964         "rowdblclick" : true,
5965         /**
5966          * @event mouseover
5967          * Fires when a mouseover occur
5968          * @param {Roo.bootstrap.Table} this
5969          * @param {Roo.Element} el
5970          * @param {Number} rowIndex
5971          * @param {Number} columnIndex
5972          * @param {Roo.EventObject} e
5973          */
5974         "mouseover" : true,
5975         /**
5976          * @event mouseout
5977          * Fires when a mouseout occur
5978          * @param {Roo.bootstrap.Table} this
5979          * @param {Roo.Element} el
5980          * @param {Number} rowIndex
5981          * @param {Number} columnIndex
5982          * @param {Roo.EventObject} e
5983          */
5984         "mouseout" : true,
5985         /**
5986          * @event rowclass
5987          * Fires when a row is rendered, so you can change add a style to it.
5988          * @param {Roo.bootstrap.Table} this
5989          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5990          */
5991         'rowclass' : true,
5992           /**
5993          * @event rowsrendered
5994          * Fires when all the  rows have been rendered
5995          * @param {Roo.bootstrap.Table} this
5996          */
5997         'rowsrendered' : true,
5998         /**
5999          * @event contextmenu
6000          * The raw contextmenu event for the entire grid.
6001          * @param {Roo.EventObject} e
6002          */
6003         "contextmenu" : true,
6004         /**
6005          * @event rowcontextmenu
6006          * Fires when a row is right clicked
6007          * @param {Roo.bootstrap.Table} this
6008          * @param {Number} rowIndex
6009          * @param {Roo.EventObject} e
6010          */
6011         "rowcontextmenu" : true,
6012         /**
6013          * @event cellcontextmenu
6014          * Fires when a cell is right clicked
6015          * @param {Roo.bootstrap.Table} this
6016          * @param {Number} rowIndex
6017          * @param {Number} cellIndex
6018          * @param {Roo.EventObject} e
6019          */
6020          "cellcontextmenu" : true,
6021          /**
6022          * @event headercontextmenu
6023          * Fires when a header is right clicked
6024          * @param {Roo.bootstrap.Table} this
6025          * @param {Number} columnIndex
6026          * @param {Roo.EventObject} e
6027          */
6028         "headercontextmenu" : true
6029     });
6030 };
6031
6032 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6033     
6034     cls: false,
6035     align: false,
6036     bgcolor: false,
6037     border: false,
6038     cellpadding: false,
6039     cellspacing: false,
6040     frame: false,
6041     rules: false,
6042     sortable: false,
6043     summary: false,
6044     width: false,
6045     striped : false,
6046     scrollBody : false,
6047     bordered: false,
6048     hover:  false,
6049     condensed : false,
6050     responsive : false,
6051     sm : false,
6052     cm : false,
6053     store : false,
6054     loadMask : false,
6055     footerShow : true,
6056     headerShow : true,
6057   
6058     rowSelection : false,
6059     cellSelection : false,
6060     layout : false,
6061     
6062     // Roo.Element - the tbody
6063     mainBody: false,
6064     // Roo.Element - thead element
6065     mainHead: false,
6066     
6067     container: false, // used by gridpanel...
6068     
6069     lazyLoad : false,
6070     
6071     CSS : Roo.util.CSS,
6072     
6073     getAutoCreate : function()
6074     {
6075         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6076         
6077         cfg = {
6078             tag: 'table',
6079             cls : 'table',
6080             cn : []
6081         };
6082         if (this.scrollBody) {
6083             cfg.cls += ' table-body-fixed';
6084         }    
6085         if (this.striped) {
6086             cfg.cls += ' table-striped';
6087         }
6088         
6089         if (this.hover) {
6090             cfg.cls += ' table-hover';
6091         }
6092         if (this.bordered) {
6093             cfg.cls += ' table-bordered';
6094         }
6095         if (this.condensed) {
6096             cfg.cls += ' table-condensed';
6097         }
6098         if (this.responsive) {
6099             cfg.cls += ' table-responsive';
6100         }
6101         
6102         if (this.cls) {
6103             cfg.cls+=  ' ' +this.cls;
6104         }
6105         
6106         // this lot should be simplifed...
6107         
6108         if (this.align) {
6109             cfg.align=this.align;
6110         }
6111         if (this.bgcolor) {
6112             cfg.bgcolor=this.bgcolor;
6113         }
6114         if (this.border) {
6115             cfg.border=this.border;
6116         }
6117         if (this.cellpadding) {
6118             cfg.cellpadding=this.cellpadding;
6119         }
6120         if (this.cellspacing) {
6121             cfg.cellspacing=this.cellspacing;
6122         }
6123         if (this.frame) {
6124             cfg.frame=this.frame;
6125         }
6126         if (this.rules) {
6127             cfg.rules=this.rules;
6128         }
6129         if (this.sortable) {
6130             cfg.sortable=this.sortable;
6131         }
6132         if (this.summary) {
6133             cfg.summary=this.summary;
6134         }
6135         if (this.width) {
6136             cfg.width=this.width;
6137         }
6138         if (this.layout) {
6139             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6140         }
6141         
6142         if(this.store || this.cm){
6143             if(this.headerShow){
6144                 cfg.cn.push(this.renderHeader());
6145             }
6146             
6147             cfg.cn.push(this.renderBody());
6148             
6149             if(this.footerShow){
6150                 cfg.cn.push(this.renderFooter());
6151             }
6152             // where does this come from?
6153             //cfg.cls+=  ' TableGrid';
6154         }
6155         
6156         return { cn : [ cfg ] };
6157     },
6158     
6159     initEvents : function()
6160     {   
6161         if(!this.store || !this.cm){
6162             return;
6163         }
6164         if (this.selModel) {
6165             this.selModel.initEvents();
6166         }
6167         
6168         
6169         //Roo.log('initEvents with ds!!!!');
6170         
6171         this.mainBody = this.el.select('tbody', true).first();
6172         this.mainHead = this.el.select('thead', true).first();
6173         
6174         
6175         
6176         
6177         var _this = this;
6178         
6179         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6180             e.on('click', _this.sort, _this);
6181         });
6182         
6183         this.mainBody.on("click", this.onClick, this);
6184         this.mainBody.on("dblclick", this.onDblClick, this);
6185         
6186         // why is this done????? = it breaks dialogs??
6187         //this.parent().el.setStyle('position', 'relative');
6188         
6189         
6190         if (this.footer) {
6191             this.footer.parentId = this.id;
6192             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6193             
6194             if(this.lazyLoad){
6195                 this.el.select('tfoot tr td').first().addClass('hide');
6196             }
6197         } 
6198         
6199         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6200         
6201         this.store.on('load', this.onLoad, this);
6202         this.store.on('beforeload', this.onBeforeLoad, this);
6203         this.store.on('update', this.onUpdate, this);
6204         this.store.on('add', this.onAdd, this);
6205         this.store.on("clear", this.clear, this);
6206         
6207         this.el.on("contextmenu", this.onContextMenu, this);
6208         
6209         this.mainBody.on('scroll', this.onBodyScroll, this);
6210         
6211         this.cm.on("headerchange", this.onHeaderChange, this);
6212         
6213         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6214         
6215     },
6216     
6217     onContextMenu : function(e, t)
6218     {
6219         this.processEvent("contextmenu", e);
6220     },
6221     
6222     processEvent : function(name, e)
6223     {
6224         if (name != 'touchstart' ) {
6225             this.fireEvent(name, e);    
6226         }
6227         
6228         var t = e.getTarget();
6229         
6230         var cell = Roo.get(t);
6231         
6232         if(!cell){
6233             return;
6234         }
6235         
6236         if(cell.findParent('tfoot', false, true)){
6237             return;
6238         }
6239         
6240         if(cell.findParent('thead', false, true)){
6241             
6242             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6243                 cell = Roo.get(t).findParent('th', false, true);
6244                 if (!cell) {
6245                     Roo.log("failed to find th in thead?");
6246                     Roo.log(e.getTarget());
6247                     return;
6248                 }
6249             }
6250             
6251             var cellIndex = cell.dom.cellIndex;
6252             
6253             var ename = name == 'touchstart' ? 'click' : name;
6254             this.fireEvent("header" + ename, this, cellIndex, e);
6255             
6256             return;
6257         }
6258         
6259         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6260             cell = Roo.get(t).findParent('td', false, true);
6261             if (!cell) {
6262                 Roo.log("failed to find th in tbody?");
6263                 Roo.log(e.getTarget());
6264                 return;
6265             }
6266         }
6267         
6268         var row = cell.findParent('tr', false, true);
6269         var cellIndex = cell.dom.cellIndex;
6270         var rowIndex = row.dom.rowIndex - 1;
6271         
6272         if(row !== false){
6273             
6274             this.fireEvent("row" + name, this, rowIndex, e);
6275             
6276             if(cell !== false){
6277             
6278                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6279             }
6280         }
6281         
6282     },
6283     
6284     onMouseover : function(e, el)
6285     {
6286         var cell = Roo.get(el);
6287         
6288         if(!cell){
6289             return;
6290         }
6291         
6292         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6293             cell = cell.findParent('td', false, true);
6294         }
6295         
6296         var row = cell.findParent('tr', false, true);
6297         var cellIndex = cell.dom.cellIndex;
6298         var rowIndex = row.dom.rowIndex - 1; // start from 0
6299         
6300         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6301         
6302     },
6303     
6304     onMouseout : function(e, el)
6305     {
6306         var cell = Roo.get(el);
6307         
6308         if(!cell){
6309             return;
6310         }
6311         
6312         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6313             cell = cell.findParent('td', false, true);
6314         }
6315         
6316         var row = cell.findParent('tr', false, true);
6317         var cellIndex = cell.dom.cellIndex;
6318         var rowIndex = row.dom.rowIndex - 1; // start from 0
6319         
6320         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6321         
6322     },
6323     
6324     onClick : function(e, el)
6325     {
6326         var cell = Roo.get(el);
6327         
6328         if(!cell || (!this.cellSelection && !this.rowSelection)){
6329             return;
6330         }
6331         
6332         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6333             cell = cell.findParent('td', false, true);
6334         }
6335         
6336         if(!cell || typeof(cell) == 'undefined'){
6337             return;
6338         }
6339         
6340         var row = cell.findParent('tr', false, true);
6341         
6342         if(!row || typeof(row) == 'undefined'){
6343             return;
6344         }
6345         
6346         var cellIndex = cell.dom.cellIndex;
6347         var rowIndex = this.getRowIndex(row);
6348         
6349         // why??? - should these not be based on SelectionModel?
6350         if(this.cellSelection){
6351             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6352         }
6353         
6354         if(this.rowSelection){
6355             this.fireEvent('rowclick', this, row, rowIndex, e);
6356         }
6357         
6358         
6359     },
6360         
6361     onDblClick : function(e,el)
6362     {
6363         var cell = Roo.get(el);
6364         
6365         if(!cell || (!this.cellSelection && !this.rowSelection)){
6366             return;
6367         }
6368         
6369         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6370             cell = cell.findParent('td', false, true);
6371         }
6372         
6373         if(!cell || typeof(cell) == 'undefined'){
6374             return;
6375         }
6376         
6377         var row = cell.findParent('tr', false, true);
6378         
6379         if(!row || typeof(row) == 'undefined'){
6380             return;
6381         }
6382         
6383         var cellIndex = cell.dom.cellIndex;
6384         var rowIndex = this.getRowIndex(row);
6385         
6386         if(this.cellSelection){
6387             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6388         }
6389         
6390         if(this.rowSelection){
6391             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6392         }
6393     },
6394     
6395     sort : function(e,el)
6396     {
6397         var col = Roo.get(el);
6398         
6399         if(!col.hasClass('sortable')){
6400             return;
6401         }
6402         
6403         var sort = col.attr('sort');
6404         var dir = 'ASC';
6405         
6406         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6407             dir = 'DESC';
6408         }
6409         
6410         this.store.sortInfo = {field : sort, direction : dir};
6411         
6412         if (this.footer) {
6413             Roo.log("calling footer first");
6414             this.footer.onClick('first');
6415         } else {
6416         
6417             this.store.load({ params : { start : 0 } });
6418         }
6419     },
6420     
6421     renderHeader : function()
6422     {
6423         var header = {
6424             tag: 'thead',
6425             cn : []
6426         };
6427         
6428         var cm = this.cm;
6429         this.totalWidth = 0;
6430         
6431         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6432             
6433             var config = cm.config[i];
6434             
6435             var c = {
6436                 tag: 'th',
6437                 cls : 'x-hcol-' + i,
6438                 style : '',
6439                 html: cm.getColumnHeader(i)
6440             };
6441             
6442             var hh = '';
6443             
6444             if(typeof(config.sortable) != 'undefined' && config.sortable){
6445                 c.cls = 'sortable';
6446                 c.html = '<i class="glyphicon"></i>' + c.html;
6447             }
6448             
6449             if(typeof(config.lgHeader) != 'undefined'){
6450                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6451             }
6452             
6453             if(typeof(config.mdHeader) != 'undefined'){
6454                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6455             }
6456             
6457             if(typeof(config.smHeader) != 'undefined'){
6458                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6459             }
6460             
6461             if(typeof(config.xsHeader) != 'undefined'){
6462                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6463             }
6464             
6465             if(hh.length){
6466                 c.html = hh;
6467             }
6468             
6469             if(typeof(config.tooltip) != 'undefined'){
6470                 c.tooltip = config.tooltip;
6471             }
6472             
6473             if(typeof(config.colspan) != 'undefined'){
6474                 c.colspan = config.colspan;
6475             }
6476             
6477             if(typeof(config.hidden) != 'undefined' && config.hidden){
6478                 c.style += ' display:none;';
6479             }
6480             
6481             if(typeof(config.dataIndex) != 'undefined'){
6482                 c.sort = config.dataIndex;
6483             }
6484             
6485            
6486             
6487             if(typeof(config.align) != 'undefined' && config.align.length){
6488                 c.style += ' text-align:' + config.align + ';';
6489             }
6490             
6491             if(typeof(config.width) != 'undefined'){
6492                 c.style += ' width:' + config.width + 'px;';
6493                 this.totalWidth += config.width;
6494             } else {
6495                 this.totalWidth += 100; // assume minimum of 100 per column?
6496             }
6497             
6498             if(typeof(config.cls) != 'undefined'){
6499                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6500             }
6501             
6502             ['xs','sm','md','lg'].map(function(size){
6503                 
6504                 if(typeof(config[size]) == 'undefined'){
6505                     return;
6506                 }
6507                 
6508                 if (!config[size]) { // 0 = hidden
6509                     c.cls += ' hidden-' + size;
6510                     return;
6511                 }
6512                 
6513                 c.cls += ' col-' + size + '-' + config[size];
6514
6515             });
6516             
6517             header.cn.push(c)
6518         }
6519         
6520         return header;
6521     },
6522     
6523     renderBody : function()
6524     {
6525         var body = {
6526             tag: 'tbody',
6527             cn : [
6528                 {
6529                     tag: 'tr',
6530                     cn : [
6531                         {
6532                             tag : 'td',
6533                             colspan :  this.cm.getColumnCount()
6534                         }
6535                     ]
6536                 }
6537             ]
6538         };
6539         
6540         return body;
6541     },
6542     
6543     renderFooter : function()
6544     {
6545         var footer = {
6546             tag: 'tfoot',
6547             cn : [
6548                 {
6549                     tag: 'tr',
6550                     cn : [
6551                         {
6552                             tag : 'td',
6553                             colspan :  this.cm.getColumnCount()
6554                         }
6555                     ]
6556                 }
6557             ]
6558         };
6559         
6560         return footer;
6561     },
6562     
6563     
6564     
6565     onLoad : function()
6566     {
6567 //        Roo.log('ds onload');
6568         this.clear();
6569         
6570         var _this = this;
6571         var cm = this.cm;
6572         var ds = this.store;
6573         
6574         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6575             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6576             if (_this.store.sortInfo) {
6577                     
6578                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6579                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6580                 }
6581                 
6582                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6583                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6584                 }
6585             }
6586         });
6587         
6588         var tbody =  this.mainBody;
6589               
6590         if(ds.getCount() > 0){
6591             ds.data.each(function(d,rowIndex){
6592                 var row =  this.renderRow(cm, ds, rowIndex);
6593                 
6594                 tbody.createChild(row);
6595                 
6596                 var _this = this;
6597                 
6598                 if(row.cellObjects.length){
6599                     Roo.each(row.cellObjects, function(r){
6600                         _this.renderCellObject(r);
6601                     })
6602                 }
6603                 
6604             }, this);
6605         }
6606         
6607         Roo.each(this.el.select('tbody td', true).elements, function(e){
6608             e.on('mouseover', _this.onMouseover, _this);
6609         });
6610         
6611         Roo.each(this.el.select('tbody td', true).elements, function(e){
6612             e.on('mouseout', _this.onMouseout, _this);
6613         });
6614         this.fireEvent('rowsrendered', this);
6615         //if(this.loadMask){
6616         //    this.maskEl.hide();
6617         //}
6618         
6619         this.autoSize();
6620     },
6621     
6622     
6623     onUpdate : function(ds,record)
6624     {
6625         this.refreshRow(record);
6626         this.autoSize();
6627     },
6628     
6629     onRemove : function(ds, record, index, isUpdate){
6630         if(isUpdate !== true){
6631             this.fireEvent("beforerowremoved", this, index, record);
6632         }
6633         var bt = this.mainBody.dom;
6634         
6635         var rows = this.el.select('tbody > tr', true).elements;
6636         
6637         if(typeof(rows[index]) != 'undefined'){
6638             bt.removeChild(rows[index].dom);
6639         }
6640         
6641 //        if(bt.rows[index]){
6642 //            bt.removeChild(bt.rows[index]);
6643 //        }
6644         
6645         if(isUpdate !== true){
6646             //this.stripeRows(index);
6647             //this.syncRowHeights(index, index);
6648             //this.layout();
6649             this.fireEvent("rowremoved", this, index, record);
6650         }
6651     },
6652     
6653     onAdd : function(ds, records, rowIndex)
6654     {
6655         //Roo.log('on Add called');
6656         // - note this does not handle multiple adding very well..
6657         var bt = this.mainBody.dom;
6658         for (var i =0 ; i < records.length;i++) {
6659             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6660             //Roo.log(records[i]);
6661             //Roo.log(this.store.getAt(rowIndex+i));
6662             this.insertRow(this.store, rowIndex + i, false);
6663             return;
6664         }
6665         
6666     },
6667     
6668     
6669     refreshRow : function(record){
6670         var ds = this.store, index;
6671         if(typeof record == 'number'){
6672             index = record;
6673             record = ds.getAt(index);
6674         }else{
6675             index = ds.indexOf(record);
6676         }
6677         this.insertRow(ds, index, true);
6678         this.autoSize();
6679         this.onRemove(ds, record, index+1, true);
6680         this.autoSize();
6681         //this.syncRowHeights(index, index);
6682         //this.layout();
6683         this.fireEvent("rowupdated", this, index, record);
6684     },
6685     
6686     insertRow : function(dm, rowIndex, isUpdate){
6687         
6688         if(!isUpdate){
6689             this.fireEvent("beforerowsinserted", this, rowIndex);
6690         }
6691             //var s = this.getScrollState();
6692         var row = this.renderRow(this.cm, this.store, rowIndex);
6693         // insert before rowIndex..
6694         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6695         
6696         var _this = this;
6697                 
6698         if(row.cellObjects.length){
6699             Roo.each(row.cellObjects, function(r){
6700                 _this.renderCellObject(r);
6701             })
6702         }
6703             
6704         if(!isUpdate){
6705             this.fireEvent("rowsinserted", this, rowIndex);
6706             //this.syncRowHeights(firstRow, lastRow);
6707             //this.stripeRows(firstRow);
6708             //this.layout();
6709         }
6710         
6711     },
6712     
6713     
6714     getRowDom : function(rowIndex)
6715     {
6716         var rows = this.el.select('tbody > tr', true).elements;
6717         
6718         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6719         
6720     },
6721     // returns the object tree for a tr..
6722   
6723     
6724     renderRow : function(cm, ds, rowIndex) 
6725     {
6726         var d = ds.getAt(rowIndex);
6727         
6728         var row = {
6729             tag : 'tr',
6730             cls : 'x-row-' + rowIndex,
6731             cn : []
6732         };
6733             
6734         var cellObjects = [];
6735         
6736         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6737             var config = cm.config[i];
6738             
6739             var renderer = cm.getRenderer(i);
6740             var value = '';
6741             var id = false;
6742             
6743             if(typeof(renderer) !== 'undefined'){
6744                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6745             }
6746             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6747             // and are rendered into the cells after the row is rendered - using the id for the element.
6748             
6749             if(typeof(value) === 'object'){
6750                 id = Roo.id();
6751                 cellObjects.push({
6752                     container : id,
6753                     cfg : value 
6754                 })
6755             }
6756             
6757             var rowcfg = {
6758                 record: d,
6759                 rowIndex : rowIndex,
6760                 colIndex : i,
6761                 rowClass : ''
6762             };
6763
6764             this.fireEvent('rowclass', this, rowcfg);
6765             
6766             var td = {
6767                 tag: 'td',
6768                 cls : rowcfg.rowClass + ' x-col-' + i,
6769                 style: '',
6770                 html: (typeof(value) === 'object') ? '' : value
6771             };
6772             
6773             if (id) {
6774                 td.id = id;
6775             }
6776             
6777             if(typeof(config.colspan) != 'undefined'){
6778                 td.colspan = config.colspan;
6779             }
6780             
6781             if(typeof(config.hidden) != 'undefined' && config.hidden){
6782                 td.style += ' display:none;';
6783             }
6784             
6785             if(typeof(config.align) != 'undefined' && config.align.length){
6786                 td.style += ' text-align:' + config.align + ';';
6787             }
6788             
6789             if(typeof(config.width) != 'undefined'){
6790                 td.style += ' width:' +  config.width + 'px;';
6791             }
6792             
6793             if(typeof(config.cursor) != 'undefined'){
6794                 td.style += ' cursor:' +  config.cursor + ';';
6795             }
6796             
6797             if(typeof(config.cls) != 'undefined'){
6798                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6799             }
6800             
6801             ['xs','sm','md','lg'].map(function(size){
6802                 
6803                 if(typeof(config[size]) == 'undefined'){
6804                     return;
6805                 }
6806                 
6807                 if (!config[size]) { // 0 = hidden
6808                     td.cls += ' hidden-' + size;
6809                     return;
6810                 }
6811                 
6812                 td.cls += ' col-' + size + '-' + config[size];
6813
6814             });
6815             
6816             row.cn.push(td);
6817            
6818         }
6819         
6820         row.cellObjects = cellObjects;
6821         
6822         return row;
6823           
6824     },
6825     
6826     
6827     
6828     onBeforeLoad : function()
6829     {
6830         //Roo.log('ds onBeforeLoad');
6831         
6832         //this.clear();
6833         
6834         //if(this.loadMask){
6835         //    this.maskEl.show();
6836         //}
6837     },
6838      /**
6839      * Remove all rows
6840      */
6841     clear : function()
6842     {
6843         this.el.select('tbody', true).first().dom.innerHTML = '';
6844     },
6845     /**
6846      * Show or hide a row.
6847      * @param {Number} rowIndex to show or hide
6848      * @param {Boolean} state hide
6849      */
6850     setRowVisibility : function(rowIndex, state)
6851     {
6852         var bt = this.mainBody.dom;
6853         
6854         var rows = this.el.select('tbody > tr', true).elements;
6855         
6856         if(typeof(rows[rowIndex]) == 'undefined'){
6857             return;
6858         }
6859         rows[rowIndex].dom.style.display = state ? '' : 'none';
6860     },
6861     
6862     
6863     getSelectionModel : function(){
6864         if(!this.selModel){
6865             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6866         }
6867         return this.selModel;
6868     },
6869     /*
6870      * Render the Roo.bootstrap object from renderder
6871      */
6872     renderCellObject : function(r)
6873     {
6874         var _this = this;
6875         
6876         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6877         
6878         var t = r.cfg.render(r.container);
6879         
6880         if(r.cfg.cn){
6881             Roo.each(r.cfg.cn, function(c){
6882                 var child = {
6883                     container: t.getChildContainer(),
6884                     cfg: c
6885                 };
6886                 _this.renderCellObject(child);
6887             })
6888         }
6889     },
6890     
6891     getRowIndex : function(row)
6892     {
6893         var rowIndex = -1;
6894         
6895         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6896             if(el != row){
6897                 return;
6898             }
6899             
6900             rowIndex = index;
6901         });
6902         
6903         return rowIndex;
6904     },
6905      /**
6906      * Returns the grid's underlying element = used by panel.Grid
6907      * @return {Element} The element
6908      */
6909     getGridEl : function(){
6910         return this.el;
6911     },
6912      /**
6913      * Forces a resize - used by panel.Grid
6914      * @return {Element} The element
6915      */
6916     autoSize : function()
6917     {
6918         //var ctr = Roo.get(this.container.dom.parentElement);
6919         var ctr = Roo.get(this.el.dom);
6920         
6921         var thd = this.getGridEl().select('thead',true).first();
6922         var tbd = this.getGridEl().select('tbody', true).first();
6923         var tfd = this.getGridEl().select('tfoot', true).first();
6924         
6925         var cw = ctr.getWidth();
6926         
6927         if (tbd) {
6928             
6929             tbd.setSize(ctr.getWidth(),
6930                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6931             );
6932             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6933             cw -= barsize;
6934         }
6935         cw = Math.max(cw, this.totalWidth);
6936         this.getGridEl().select('tr',true).setWidth(cw);
6937         // resize 'expandable coloumn?
6938         
6939         return; // we doe not have a view in this design..
6940         
6941     },
6942     onBodyScroll: function()
6943     {
6944         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6945         if(this.mainHead){
6946             this.mainHead.setStyle({
6947                 'position' : 'relative',
6948                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6949             });
6950         }
6951         
6952         if(this.lazyLoad){
6953             
6954             var scrollHeight = this.mainBody.dom.scrollHeight;
6955             
6956             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6957             
6958             var height = this.mainBody.getHeight();
6959             
6960             if(scrollHeight - height == scrollTop) {
6961                 
6962                 var total = this.ds.getTotalCount();
6963                 
6964                 if(this.footer.cursor + this.footer.pageSize < total){
6965                     
6966                     this.footer.ds.load({
6967                         params : {
6968                             start : this.footer.cursor + this.footer.pageSize,
6969                             limit : this.footer.pageSize
6970                         },
6971                         add : true
6972                     });
6973                 }
6974             }
6975             
6976         }
6977     },
6978     
6979     onHeaderChange : function()
6980     {
6981         var header = this.renderHeader();
6982         var table = this.el.select('table', true).first();
6983         
6984         this.mainHead.remove();
6985         this.mainHead = table.createChild(header, this.mainBody, false);
6986     },
6987     
6988     onHiddenChange : function(colModel, colIndex, hidden)
6989     {
6990         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6991         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6992         
6993         this.CSS.updateRule(thSelector, "display", "");
6994         this.CSS.updateRule(tdSelector, "display", "");
6995         
6996         if(hidden){
6997             this.CSS.updateRule(thSelector, "display", "none");
6998             this.CSS.updateRule(tdSelector, "display", "none");
6999         }
7000         
7001         this.onHeaderChange();
7002         this.onLoad();
7003         
7004     }
7005     
7006 });
7007
7008  
7009
7010  /*
7011  * - LGPL
7012  *
7013  * table cell
7014  * 
7015  */
7016
7017 /**
7018  * @class Roo.bootstrap.TableCell
7019  * @extends Roo.bootstrap.Component
7020  * Bootstrap TableCell class
7021  * @cfg {String} html cell contain text
7022  * @cfg {String} cls cell class
7023  * @cfg {String} tag cell tag (td|th) default td
7024  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7025  * @cfg {String} align Aligns the content in a cell
7026  * @cfg {String} axis Categorizes cells
7027  * @cfg {String} bgcolor Specifies the background color of a cell
7028  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7029  * @cfg {Number} colspan Specifies the number of columns a cell should span
7030  * @cfg {String} headers Specifies one or more header cells a cell is related to
7031  * @cfg {Number} height Sets the height of a cell
7032  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7033  * @cfg {Number} rowspan Sets the number of rows a cell should span
7034  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7035  * @cfg {String} valign Vertical aligns the content in a cell
7036  * @cfg {Number} width Specifies the width of a cell
7037  * 
7038  * @constructor
7039  * Create a new TableCell
7040  * @param {Object} config The config object
7041  */
7042
7043 Roo.bootstrap.TableCell = function(config){
7044     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7045 };
7046
7047 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7048     
7049     html: false,
7050     cls: false,
7051     tag: false,
7052     abbr: false,
7053     align: false,
7054     axis: false,
7055     bgcolor: false,
7056     charoff: false,
7057     colspan: false,
7058     headers: false,
7059     height: false,
7060     nowrap: false,
7061     rowspan: false,
7062     scope: false,
7063     valign: false,
7064     width: false,
7065     
7066     
7067     getAutoCreate : function(){
7068         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7069         
7070         cfg = {
7071             tag: 'td'
7072         };
7073         
7074         if(this.tag){
7075             cfg.tag = this.tag;
7076         }
7077         
7078         if (this.html) {
7079             cfg.html=this.html
7080         }
7081         if (this.cls) {
7082             cfg.cls=this.cls
7083         }
7084         if (this.abbr) {
7085             cfg.abbr=this.abbr
7086         }
7087         if (this.align) {
7088             cfg.align=this.align
7089         }
7090         if (this.axis) {
7091             cfg.axis=this.axis
7092         }
7093         if (this.bgcolor) {
7094             cfg.bgcolor=this.bgcolor
7095         }
7096         if (this.charoff) {
7097             cfg.charoff=this.charoff
7098         }
7099         if (this.colspan) {
7100             cfg.colspan=this.colspan
7101         }
7102         if (this.headers) {
7103             cfg.headers=this.headers
7104         }
7105         if (this.height) {
7106             cfg.height=this.height
7107         }
7108         if (this.nowrap) {
7109             cfg.nowrap=this.nowrap
7110         }
7111         if (this.rowspan) {
7112             cfg.rowspan=this.rowspan
7113         }
7114         if (this.scope) {
7115             cfg.scope=this.scope
7116         }
7117         if (this.valign) {
7118             cfg.valign=this.valign
7119         }
7120         if (this.width) {
7121             cfg.width=this.width
7122         }
7123         
7124         
7125         return cfg;
7126     }
7127    
7128 });
7129
7130  
7131
7132  /*
7133  * - LGPL
7134  *
7135  * table row
7136  * 
7137  */
7138
7139 /**
7140  * @class Roo.bootstrap.TableRow
7141  * @extends Roo.bootstrap.Component
7142  * Bootstrap TableRow class
7143  * @cfg {String} cls row class
7144  * @cfg {String} align Aligns the content in a table row
7145  * @cfg {String} bgcolor Specifies a background color for a table row
7146  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7147  * @cfg {String} valign Vertical aligns the content in a table row
7148  * 
7149  * @constructor
7150  * Create a new TableRow
7151  * @param {Object} config The config object
7152  */
7153
7154 Roo.bootstrap.TableRow = function(config){
7155     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7156 };
7157
7158 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7159     
7160     cls: false,
7161     align: false,
7162     bgcolor: false,
7163     charoff: false,
7164     valign: false,
7165     
7166     getAutoCreate : function(){
7167         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7168         
7169         cfg = {
7170             tag: 'tr'
7171         };
7172             
7173         if(this.cls){
7174             cfg.cls = this.cls;
7175         }
7176         if(this.align){
7177             cfg.align = this.align;
7178         }
7179         if(this.bgcolor){
7180             cfg.bgcolor = this.bgcolor;
7181         }
7182         if(this.charoff){
7183             cfg.charoff = this.charoff;
7184         }
7185         if(this.valign){
7186             cfg.valign = this.valign;
7187         }
7188         
7189         return cfg;
7190     }
7191    
7192 });
7193
7194  
7195
7196  /*
7197  * - LGPL
7198  *
7199  * table body
7200  * 
7201  */
7202
7203 /**
7204  * @class Roo.bootstrap.TableBody
7205  * @extends Roo.bootstrap.Component
7206  * Bootstrap TableBody class
7207  * @cfg {String} cls element class
7208  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7209  * @cfg {String} align Aligns the content inside the element
7210  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7211  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7212  * 
7213  * @constructor
7214  * Create a new TableBody
7215  * @param {Object} config The config object
7216  */
7217
7218 Roo.bootstrap.TableBody = function(config){
7219     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7220 };
7221
7222 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7223     
7224     cls: false,
7225     tag: false,
7226     align: false,
7227     charoff: false,
7228     valign: false,
7229     
7230     getAutoCreate : function(){
7231         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7232         
7233         cfg = {
7234             tag: 'tbody'
7235         };
7236             
7237         if (this.cls) {
7238             cfg.cls=this.cls
7239         }
7240         if(this.tag){
7241             cfg.tag = this.tag;
7242         }
7243         
7244         if(this.align){
7245             cfg.align = this.align;
7246         }
7247         if(this.charoff){
7248             cfg.charoff = this.charoff;
7249         }
7250         if(this.valign){
7251             cfg.valign = this.valign;
7252         }
7253         
7254         return cfg;
7255     }
7256     
7257     
7258 //    initEvents : function()
7259 //    {
7260 //        
7261 //        if(!this.store){
7262 //            return;
7263 //        }
7264 //        
7265 //        this.store = Roo.factory(this.store, Roo.data);
7266 //        this.store.on('load', this.onLoad, this);
7267 //        
7268 //        this.store.load();
7269 //        
7270 //    },
7271 //    
7272 //    onLoad: function () 
7273 //    {   
7274 //        this.fireEvent('load', this);
7275 //    }
7276 //    
7277 //   
7278 });
7279
7280  
7281
7282  /*
7283  * Based on:
7284  * Ext JS Library 1.1.1
7285  * Copyright(c) 2006-2007, Ext JS, LLC.
7286  *
7287  * Originally Released Under LGPL - original licence link has changed is not relivant.
7288  *
7289  * Fork - LGPL
7290  * <script type="text/javascript">
7291  */
7292
7293 // as we use this in bootstrap.
7294 Roo.namespace('Roo.form');
7295  /**
7296  * @class Roo.form.Action
7297  * Internal Class used to handle form actions
7298  * @constructor
7299  * @param {Roo.form.BasicForm} el The form element or its id
7300  * @param {Object} config Configuration options
7301  */
7302
7303  
7304  
7305 // define the action interface
7306 Roo.form.Action = function(form, options){
7307     this.form = form;
7308     this.options = options || {};
7309 };
7310 /**
7311  * Client Validation Failed
7312  * @const 
7313  */
7314 Roo.form.Action.CLIENT_INVALID = 'client';
7315 /**
7316  * Server Validation Failed
7317  * @const 
7318  */
7319 Roo.form.Action.SERVER_INVALID = 'server';
7320  /**
7321  * Connect to Server Failed
7322  * @const 
7323  */
7324 Roo.form.Action.CONNECT_FAILURE = 'connect';
7325 /**
7326  * Reading Data from Server Failed
7327  * @const 
7328  */
7329 Roo.form.Action.LOAD_FAILURE = 'load';
7330
7331 Roo.form.Action.prototype = {
7332     type : 'default',
7333     failureType : undefined,
7334     response : undefined,
7335     result : undefined,
7336
7337     // interface method
7338     run : function(options){
7339
7340     },
7341
7342     // interface method
7343     success : function(response){
7344
7345     },
7346
7347     // interface method
7348     handleResponse : function(response){
7349
7350     },
7351
7352     // default connection failure
7353     failure : function(response){
7354         
7355         this.response = response;
7356         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7357         this.form.afterAction(this, false);
7358     },
7359
7360     processResponse : function(response){
7361         this.response = response;
7362         if(!response.responseText){
7363             return true;
7364         }
7365         this.result = this.handleResponse(response);
7366         return this.result;
7367     },
7368
7369     // utility functions used internally
7370     getUrl : function(appendParams){
7371         var url = this.options.url || this.form.url || this.form.el.dom.action;
7372         if(appendParams){
7373             var p = this.getParams();
7374             if(p){
7375                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7376             }
7377         }
7378         return url;
7379     },
7380
7381     getMethod : function(){
7382         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7383     },
7384
7385     getParams : function(){
7386         var bp = this.form.baseParams;
7387         var p = this.options.params;
7388         if(p){
7389             if(typeof p == "object"){
7390                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7391             }else if(typeof p == 'string' && bp){
7392                 p += '&' + Roo.urlEncode(bp);
7393             }
7394         }else if(bp){
7395             p = Roo.urlEncode(bp);
7396         }
7397         return p;
7398     },
7399
7400     createCallback : function(){
7401         return {
7402             success: this.success,
7403             failure: this.failure,
7404             scope: this,
7405             timeout: (this.form.timeout*1000),
7406             upload: this.form.fileUpload ? this.success : undefined
7407         };
7408     }
7409 };
7410
7411 Roo.form.Action.Submit = function(form, options){
7412     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7413 };
7414
7415 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7416     type : 'submit',
7417
7418     haveProgress : false,
7419     uploadComplete : false,
7420     
7421     // uploadProgress indicator.
7422     uploadProgress : function()
7423     {
7424         if (!this.form.progressUrl) {
7425             return;
7426         }
7427         
7428         if (!this.haveProgress) {
7429             Roo.MessageBox.progress("Uploading", "Uploading");
7430         }
7431         if (this.uploadComplete) {
7432            Roo.MessageBox.hide();
7433            return;
7434         }
7435         
7436         this.haveProgress = true;
7437    
7438         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7439         
7440         var c = new Roo.data.Connection();
7441         c.request({
7442             url : this.form.progressUrl,
7443             params: {
7444                 id : uid
7445             },
7446             method: 'GET',
7447             success : function(req){
7448                //console.log(data);
7449                 var rdata = false;
7450                 var edata;
7451                 try  {
7452                    rdata = Roo.decode(req.responseText)
7453                 } catch (e) {
7454                     Roo.log("Invalid data from server..");
7455                     Roo.log(edata);
7456                     return;
7457                 }
7458                 if (!rdata || !rdata.success) {
7459                     Roo.log(rdata);
7460                     Roo.MessageBox.alert(Roo.encode(rdata));
7461                     return;
7462                 }
7463                 var data = rdata.data;
7464                 
7465                 if (this.uploadComplete) {
7466                    Roo.MessageBox.hide();
7467                    return;
7468                 }
7469                    
7470                 if (data){
7471                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7472                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7473                     );
7474                 }
7475                 this.uploadProgress.defer(2000,this);
7476             },
7477        
7478             failure: function(data) {
7479                 Roo.log('progress url failed ');
7480                 Roo.log(data);
7481             },
7482             scope : this
7483         });
7484            
7485     },
7486     
7487     
7488     run : function()
7489     {
7490         // run get Values on the form, so it syncs any secondary forms.
7491         this.form.getValues();
7492         
7493         var o = this.options;
7494         var method = this.getMethod();
7495         var isPost = method == 'POST';
7496         if(o.clientValidation === false || this.form.isValid()){
7497             
7498             if (this.form.progressUrl) {
7499                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7500                     (new Date() * 1) + '' + Math.random());
7501                     
7502             } 
7503             
7504             
7505             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7506                 form:this.form.el.dom,
7507                 url:this.getUrl(!isPost),
7508                 method: method,
7509                 params:isPost ? this.getParams() : null,
7510                 isUpload: this.form.fileUpload
7511             }));
7512             
7513             this.uploadProgress();
7514
7515         }else if (o.clientValidation !== false){ // client validation failed
7516             this.failureType = Roo.form.Action.CLIENT_INVALID;
7517             this.form.afterAction(this, false);
7518         }
7519     },
7520
7521     success : function(response)
7522     {
7523         this.uploadComplete= true;
7524         if (this.haveProgress) {
7525             Roo.MessageBox.hide();
7526         }
7527         
7528         
7529         var result = this.processResponse(response);
7530         if(result === true || result.success){
7531             this.form.afterAction(this, true);
7532             return;
7533         }
7534         if(result.errors){
7535             this.form.markInvalid(result.errors);
7536             this.failureType = Roo.form.Action.SERVER_INVALID;
7537         }
7538         this.form.afterAction(this, false);
7539     },
7540     failure : function(response)
7541     {
7542         this.uploadComplete= true;
7543         if (this.haveProgress) {
7544             Roo.MessageBox.hide();
7545         }
7546         
7547         this.response = response;
7548         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7549         this.form.afterAction(this, false);
7550     },
7551     
7552     handleResponse : function(response){
7553         if(this.form.errorReader){
7554             var rs = this.form.errorReader.read(response);
7555             var errors = [];
7556             if(rs.records){
7557                 for(var i = 0, len = rs.records.length; i < len; i++) {
7558                     var r = rs.records[i];
7559                     errors[i] = r.data;
7560                 }
7561             }
7562             if(errors.length < 1){
7563                 errors = null;
7564             }
7565             return {
7566                 success : rs.success,
7567                 errors : errors
7568             };
7569         }
7570         var ret = false;
7571         try {
7572             ret = Roo.decode(response.responseText);
7573         } catch (e) {
7574             ret = {
7575                 success: false,
7576                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7577                 errors : []
7578             };
7579         }
7580         return ret;
7581         
7582     }
7583 });
7584
7585
7586 Roo.form.Action.Load = function(form, options){
7587     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7588     this.reader = this.form.reader;
7589 };
7590
7591 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7592     type : 'load',
7593
7594     run : function(){
7595         
7596         Roo.Ajax.request(Roo.apply(
7597                 this.createCallback(), {
7598                     method:this.getMethod(),
7599                     url:this.getUrl(false),
7600                     params:this.getParams()
7601         }));
7602     },
7603
7604     success : function(response){
7605         
7606         var result = this.processResponse(response);
7607         if(result === true || !result.success || !result.data){
7608             this.failureType = Roo.form.Action.LOAD_FAILURE;
7609             this.form.afterAction(this, false);
7610             return;
7611         }
7612         this.form.clearInvalid();
7613         this.form.setValues(result.data);
7614         this.form.afterAction(this, true);
7615     },
7616
7617     handleResponse : function(response){
7618         if(this.form.reader){
7619             var rs = this.form.reader.read(response);
7620             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7621             return {
7622                 success : rs.success,
7623                 data : data
7624             };
7625         }
7626         return Roo.decode(response.responseText);
7627     }
7628 });
7629
7630 Roo.form.Action.ACTION_TYPES = {
7631     'load' : Roo.form.Action.Load,
7632     'submit' : Roo.form.Action.Submit
7633 };/*
7634  * - LGPL
7635  *
7636  * form
7637  *
7638  */
7639
7640 /**
7641  * @class Roo.bootstrap.Form
7642  * @extends Roo.bootstrap.Component
7643  * Bootstrap Form class
7644  * @cfg {String} method  GET | POST (default POST)
7645  * @cfg {String} labelAlign top | left (default top)
7646  * @cfg {String} align left  | right - for navbars
7647  * @cfg {Boolean} loadMask load mask when submit (default true)
7648
7649  *
7650  * @constructor
7651  * Create a new Form
7652  * @param {Object} config The config object
7653  */
7654
7655
7656 Roo.bootstrap.Form = function(config){
7657     
7658     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7659     
7660     Roo.bootstrap.Form.popover.apply();
7661     
7662     this.addEvents({
7663         /**
7664          * @event clientvalidation
7665          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7666          * @param {Form} this
7667          * @param {Boolean} valid true if the form has passed client-side validation
7668          */
7669         clientvalidation: true,
7670         /**
7671          * @event beforeaction
7672          * Fires before any action is performed. Return false to cancel the action.
7673          * @param {Form} this
7674          * @param {Action} action The action to be performed
7675          */
7676         beforeaction: true,
7677         /**
7678          * @event actionfailed
7679          * Fires when an action fails.
7680          * @param {Form} this
7681          * @param {Action} action The action that failed
7682          */
7683         actionfailed : true,
7684         /**
7685          * @event actioncomplete
7686          * Fires when an action is completed.
7687          * @param {Form} this
7688          * @param {Action} action The action that completed
7689          */
7690         actioncomplete : true
7691     });
7692 };
7693
7694 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7695
7696      /**
7697      * @cfg {String} method
7698      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7699      */
7700     method : 'POST',
7701     /**
7702      * @cfg {String} url
7703      * The URL to use for form actions if one isn't supplied in the action options.
7704      */
7705     /**
7706      * @cfg {Boolean} fileUpload
7707      * Set to true if this form is a file upload.
7708      */
7709
7710     /**
7711      * @cfg {Object} baseParams
7712      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7713      */
7714
7715     /**
7716      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7717      */
7718     timeout: 30,
7719     /**
7720      * @cfg {Sting} align (left|right) for navbar forms
7721      */
7722     align : 'left',
7723
7724     // private
7725     activeAction : null,
7726
7727     /**
7728      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7729      * element by passing it or its id or mask the form itself by passing in true.
7730      * @type Mixed
7731      */
7732     waitMsgTarget : false,
7733
7734     loadMask : true,
7735     
7736     /**
7737      * @cfg {Boolean} errorMask (true|false) default false
7738      */
7739     errorMask : false,
7740     
7741     /**
7742      * @cfg {Number} maskOffset Default 100
7743      */
7744     maskOffset : 100,
7745     
7746     /**
7747      * @cfg {Boolean} maskBody
7748      */
7749     maskBody : false,
7750
7751     getAutoCreate : function(){
7752
7753         var cfg = {
7754             tag: 'form',
7755             method : this.method || 'POST',
7756             id : this.id || Roo.id(),
7757             cls : ''
7758         };
7759         if (this.parent().xtype.match(/^Nav/)) {
7760             cfg.cls = 'navbar-form navbar-' + this.align;
7761
7762         }
7763
7764         if (this.labelAlign == 'left' ) {
7765             cfg.cls += ' form-horizontal';
7766         }
7767
7768
7769         return cfg;
7770     },
7771     initEvents : function()
7772     {
7773         this.el.on('submit', this.onSubmit, this);
7774         // this was added as random key presses on the form where triggering form submit.
7775         this.el.on('keypress', function(e) {
7776             if (e.getCharCode() != 13) {
7777                 return true;
7778             }
7779             // we might need to allow it for textareas.. and some other items.
7780             // check e.getTarget().
7781
7782             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7783                 return true;
7784             }
7785
7786             Roo.log("keypress blocked");
7787
7788             e.preventDefault();
7789             return false;
7790         });
7791         
7792     },
7793     // private
7794     onSubmit : function(e){
7795         e.stopEvent();
7796     },
7797
7798      /**
7799      * Returns true if client-side validation on the form is successful.
7800      * @return Boolean
7801      */
7802     isValid : function(){
7803         var items = this.getItems();
7804         var valid = true;
7805         var target = false;
7806         
7807         items.each(function(f){
7808             
7809             if(f.validate()){
7810                 return;
7811             }
7812             valid = false;
7813
7814             if(!target && f.el.isVisible(true)){
7815                 target = f;
7816             }
7817            
7818         });
7819         
7820         if(this.errorMask && !valid){
7821             Roo.bootstrap.Form.popover.mask(this, target);
7822         }
7823         
7824         return valid;
7825     },
7826     
7827     /**
7828      * Returns true if any fields in this form have changed since their original load.
7829      * @return Boolean
7830      */
7831     isDirty : function(){
7832         var dirty = false;
7833         var items = this.getItems();
7834         items.each(function(f){
7835            if(f.isDirty()){
7836                dirty = true;
7837                return false;
7838            }
7839            return true;
7840         });
7841         return dirty;
7842     },
7843      /**
7844      * Performs a predefined action (submit or load) or custom actions you define on this form.
7845      * @param {String} actionName The name of the action type
7846      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7847      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7848      * accept other config options):
7849      * <pre>
7850 Property          Type             Description
7851 ----------------  ---------------  ----------------------------------------------------------------------------------
7852 url               String           The url for the action (defaults to the form's url)
7853 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7854 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7855 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7856                                    validate the form on the client (defaults to false)
7857      * </pre>
7858      * @return {BasicForm} this
7859      */
7860     doAction : function(action, options){
7861         if(typeof action == 'string'){
7862             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7863         }
7864         if(this.fireEvent('beforeaction', this, action) !== false){
7865             this.beforeAction(action);
7866             action.run.defer(100, action);
7867         }
7868         return this;
7869     },
7870
7871     // private
7872     beforeAction : function(action){
7873         var o = action.options;
7874         
7875         if(this.loadMask){
7876             
7877             if(this.maskBody){
7878                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7879             } else {
7880                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7881             }
7882         }
7883         // not really supported yet.. ??
7884
7885         //if(this.waitMsgTarget === true){
7886         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7887         //}else if(this.waitMsgTarget){
7888         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7889         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7890         //}else {
7891         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7892        // }
7893
7894     },
7895
7896     // private
7897     afterAction : function(action, success){
7898         this.activeAction = null;
7899         var o = action.options;
7900
7901         if(this.loadMask){
7902             
7903             if(this.maskBody){
7904                 Roo.get(document.body).unmask();
7905             } else {
7906                 this.el.unmask();
7907             }
7908         }
7909         
7910         //if(this.waitMsgTarget === true){
7911 //            this.el.unmask();
7912         //}else if(this.waitMsgTarget){
7913         //    this.waitMsgTarget.unmask();
7914         //}else{
7915         //    Roo.MessageBox.updateProgress(1);
7916         //    Roo.MessageBox.hide();
7917        // }
7918         //
7919         if(success){
7920             if(o.reset){
7921                 this.reset();
7922             }
7923             Roo.callback(o.success, o.scope, [this, action]);
7924             this.fireEvent('actioncomplete', this, action);
7925
7926         }else{
7927
7928             // failure condition..
7929             // we have a scenario where updates need confirming.
7930             // eg. if a locking scenario exists..
7931             // we look for { errors : { needs_confirm : true }} in the response.
7932             if (
7933                 (typeof(action.result) != 'undefined')  &&
7934                 (typeof(action.result.errors) != 'undefined')  &&
7935                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7936            ){
7937                 var _t = this;
7938                 Roo.log("not supported yet");
7939                  /*
7940
7941                 Roo.MessageBox.confirm(
7942                     "Change requires confirmation",
7943                     action.result.errorMsg,
7944                     function(r) {
7945                         if (r != 'yes') {
7946                             return;
7947                         }
7948                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7949                     }
7950
7951                 );
7952                 */
7953
7954
7955                 return;
7956             }
7957
7958             Roo.callback(o.failure, o.scope, [this, action]);
7959             // show an error message if no failed handler is set..
7960             if (!this.hasListener('actionfailed')) {
7961                 Roo.log("need to add dialog support");
7962                 /*
7963                 Roo.MessageBox.alert("Error",
7964                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7965                         action.result.errorMsg :
7966                         "Saving Failed, please check your entries or try again"
7967                 );
7968                 */
7969             }
7970
7971             this.fireEvent('actionfailed', this, action);
7972         }
7973
7974     },
7975     /**
7976      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7977      * @param {String} id The value to search for
7978      * @return Field
7979      */
7980     findField : function(id){
7981         var items = this.getItems();
7982         var field = items.get(id);
7983         if(!field){
7984              items.each(function(f){
7985                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7986                     field = f;
7987                     return false;
7988                 }
7989                 return true;
7990             });
7991         }
7992         return field || null;
7993     },
7994      /**
7995      * Mark fields in this form invalid in bulk.
7996      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7997      * @return {BasicForm} this
7998      */
7999     markInvalid : function(errors){
8000         if(errors instanceof Array){
8001             for(var i = 0, len = errors.length; i < len; i++){
8002                 var fieldError = errors[i];
8003                 var f = this.findField(fieldError.id);
8004                 if(f){
8005                     f.markInvalid(fieldError.msg);
8006                 }
8007             }
8008         }else{
8009             var field, id;
8010             for(id in errors){
8011                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8012                     field.markInvalid(errors[id]);
8013                 }
8014             }
8015         }
8016         //Roo.each(this.childForms || [], function (f) {
8017         //    f.markInvalid(errors);
8018         //});
8019
8020         return this;
8021     },
8022
8023     /**
8024      * Set values for fields in this form in bulk.
8025      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8026      * @return {BasicForm} this
8027      */
8028     setValues : function(values){
8029         if(values instanceof Array){ // array of objects
8030             for(var i = 0, len = values.length; i < len; i++){
8031                 var v = values[i];
8032                 var f = this.findField(v.id);
8033                 if(f){
8034                     f.setValue(v.value);
8035                     if(this.trackResetOnLoad){
8036                         f.originalValue = f.getValue();
8037                     }
8038                 }
8039             }
8040         }else{ // object hash
8041             var field, id;
8042             for(id in values){
8043                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8044
8045                     if (field.setFromData &&
8046                         field.valueField &&
8047                         field.displayField &&
8048                         // combos' with local stores can
8049                         // be queried via setValue()
8050                         // to set their value..
8051                         (field.store && !field.store.isLocal)
8052                         ) {
8053                         // it's a combo
8054                         var sd = { };
8055                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8056                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8057                         field.setFromData(sd);
8058
8059                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8060                         
8061                         field.setFromData(values);
8062                         
8063                     } else {
8064                         field.setValue(values[id]);
8065                     }
8066
8067
8068                     if(this.trackResetOnLoad){
8069                         field.originalValue = field.getValue();
8070                     }
8071                 }
8072             }
8073         }
8074
8075         //Roo.each(this.childForms || [], function (f) {
8076         //    f.setValues(values);
8077         //});
8078
8079         return this;
8080     },
8081
8082     /**
8083      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8084      * they are returned as an array.
8085      * @param {Boolean} asString
8086      * @return {Object}
8087      */
8088     getValues : function(asString){
8089         //if (this.childForms) {
8090             // copy values from the child forms
8091         //    Roo.each(this.childForms, function (f) {
8092         //        this.setValues(f.getValues());
8093         //    }, this);
8094         //}
8095
8096
8097
8098         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8099         if(asString === true){
8100             return fs;
8101         }
8102         return Roo.urlDecode(fs);
8103     },
8104
8105     /**
8106      * Returns the fields in this form as an object with key/value pairs.
8107      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8108      * @return {Object}
8109      */
8110     getFieldValues : function(with_hidden)
8111     {
8112         var items = this.getItems();
8113         var ret = {};
8114         items.each(function(f){
8115             
8116             if (!f.getName()) {
8117                 return;
8118             }
8119             
8120             var v = f.getValue();
8121             
8122             if (f.inputType =='radio') {
8123                 if (typeof(ret[f.getName()]) == 'undefined') {
8124                     ret[f.getName()] = ''; // empty..
8125                 }
8126
8127                 if (!f.el.dom.checked) {
8128                     return;
8129
8130                 }
8131                 v = f.el.dom.value;
8132
8133             }
8134             
8135             if(f.xtype == 'MoneyField'){
8136                 ret[f.currencyName] = f.getCurrency();
8137             }
8138
8139             // not sure if this supported any more..
8140             if ((typeof(v) == 'object') && f.getRawValue) {
8141                 v = f.getRawValue() ; // dates..
8142             }
8143             // combo boxes where name != hiddenName...
8144             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8145                 ret[f.name] = f.getRawValue();
8146             }
8147             ret[f.getName()] = v;
8148         });
8149
8150         return ret;
8151     },
8152
8153     /**
8154      * Clears all invalid messages in this form.
8155      * @return {BasicForm} this
8156      */
8157     clearInvalid : function(){
8158         var items = this.getItems();
8159
8160         items.each(function(f){
8161            f.clearInvalid();
8162         });
8163
8164         return this;
8165     },
8166
8167     /**
8168      * Resets this form.
8169      * @return {BasicForm} this
8170      */
8171     reset : function(){
8172         var items = this.getItems();
8173         items.each(function(f){
8174             f.reset();
8175         });
8176
8177         Roo.each(this.childForms || [], function (f) {
8178             f.reset();
8179         });
8180
8181
8182         return this;
8183     },
8184     
8185     getItems : function()
8186     {
8187         var r=new Roo.util.MixedCollection(false, function(o){
8188             return o.id || (o.id = Roo.id());
8189         });
8190         var iter = function(el) {
8191             if (el.inputEl) {
8192                 r.add(el);
8193             }
8194             if (!el.items) {
8195                 return;
8196             }
8197             Roo.each(el.items,function(e) {
8198                 iter(e);
8199             });
8200         };
8201
8202         iter(this);
8203         return r;
8204     },
8205     
8206     hideFields : function(items)
8207     {
8208         Roo.each(items, function(i){
8209             
8210             var f = this.findField(i);
8211             
8212             if(!f){
8213                 return;
8214             }
8215             
8216             if(f.xtype == 'DateField'){
8217                 f.setVisible(false);
8218                 return;
8219             }
8220             
8221             f.hide();
8222             
8223         }, this);
8224     },
8225     
8226     showFields : function(items)
8227     {
8228         Roo.each(items, function(i){
8229             
8230             var f = this.findField(i);
8231             
8232             if(!f){
8233                 return;
8234             }
8235             
8236             if(f.xtype == 'DateField'){
8237                 f.setVisible(true);
8238                 return;
8239             }
8240             
8241             f.show();
8242             
8243         }, this);
8244     }
8245
8246 });
8247
8248 Roo.apply(Roo.bootstrap.Form, {
8249     
8250     popover : {
8251         
8252         padding : 5,
8253         
8254         isApplied : false,
8255         
8256         isMasked : false,
8257         
8258         form : false,
8259         
8260         target : false,
8261         
8262         toolTip : false,
8263         
8264         intervalID : false,
8265         
8266         maskEl : false,
8267         
8268         apply : function()
8269         {
8270             if(this.isApplied){
8271                 return;
8272             }
8273             
8274             this.maskEl = {
8275                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8276                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8277                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8278                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8279             };
8280             
8281             this.maskEl.top.enableDisplayMode("block");
8282             this.maskEl.left.enableDisplayMode("block");
8283             this.maskEl.bottom.enableDisplayMode("block");
8284             this.maskEl.right.enableDisplayMode("block");
8285             
8286             this.toolTip = new Roo.bootstrap.Tooltip({
8287                 cls : 'roo-form-error-popover',
8288                 alignment : {
8289                     'left' : ['r-l', [-2,0], 'right'],
8290                     'right' : ['l-r', [2,0], 'left'],
8291                     'bottom' : ['tl-bl', [0,2], 'top'],
8292                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8293                 }
8294             });
8295             
8296             this.toolTip.render(Roo.get(document.body));
8297
8298             this.toolTip.el.enableDisplayMode("block");
8299             
8300             Roo.get(document.body).on('click', function(){
8301                 this.unmask();
8302             }, this);
8303             
8304             Roo.get(document.body).on('touchstart', function(){
8305                 this.unmask();
8306             }, this);
8307             
8308             this.isApplied = true
8309         },
8310         
8311         mask : function(form, target)
8312         {
8313             this.form = form;
8314             
8315             this.target = target;
8316             
8317             if(!this.form.errorMask || !target.el){
8318                 return;
8319             }
8320             
8321             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8322             
8323             Roo.log(scrollable);
8324             
8325             var ot = this.target.el.calcOffsetsTo(scrollable);
8326             
8327             var scrollTo = ot[1] - this.form.maskOffset;
8328             
8329             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8330             
8331             scrollable.scrollTo('top', scrollTo);
8332             
8333             var box = this.target.el.getBox();
8334             Roo.log(box);
8335             var zIndex = Roo.bootstrap.Modal.zIndex++;
8336
8337             
8338             this.maskEl.top.setStyle('position', 'absolute');
8339             this.maskEl.top.setStyle('z-index', zIndex);
8340             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8341             this.maskEl.top.setLeft(0);
8342             this.maskEl.top.setTop(0);
8343             this.maskEl.top.show();
8344             
8345             this.maskEl.left.setStyle('position', 'absolute');
8346             this.maskEl.left.setStyle('z-index', zIndex);
8347             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8348             this.maskEl.left.setLeft(0);
8349             this.maskEl.left.setTop(box.y - this.padding);
8350             this.maskEl.left.show();
8351
8352             this.maskEl.bottom.setStyle('position', 'absolute');
8353             this.maskEl.bottom.setStyle('z-index', zIndex);
8354             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8355             this.maskEl.bottom.setLeft(0);
8356             this.maskEl.bottom.setTop(box.bottom + this.padding);
8357             this.maskEl.bottom.show();
8358
8359             this.maskEl.right.setStyle('position', 'absolute');
8360             this.maskEl.right.setStyle('z-index', zIndex);
8361             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8362             this.maskEl.right.setLeft(box.right + this.padding);
8363             this.maskEl.right.setTop(box.y - this.padding);
8364             this.maskEl.right.show();
8365
8366             this.toolTip.bindEl = this.target.el;
8367
8368             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8369
8370             var tip = this.target.blankText;
8371
8372             if(this.target.getValue() !== '' ) {
8373                 
8374                 if (this.target.invalidText.length) {
8375                     tip = this.target.invalidText;
8376                 } else if (this.target.regexText.length){
8377                     tip = this.target.regexText;
8378                 }
8379             }
8380
8381             this.toolTip.show(tip);
8382
8383             this.intervalID = window.setInterval(function() {
8384                 Roo.bootstrap.Form.popover.unmask();
8385             }, 10000);
8386
8387             window.onwheel = function(){ return false;};
8388             
8389             (function(){ this.isMasked = true; }).defer(500, this);
8390             
8391         },
8392         
8393         unmask : function()
8394         {
8395             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8396                 return;
8397             }
8398             
8399             this.maskEl.top.setStyle('position', 'absolute');
8400             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8401             this.maskEl.top.hide();
8402
8403             this.maskEl.left.setStyle('position', 'absolute');
8404             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8405             this.maskEl.left.hide();
8406
8407             this.maskEl.bottom.setStyle('position', 'absolute');
8408             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8409             this.maskEl.bottom.hide();
8410
8411             this.maskEl.right.setStyle('position', 'absolute');
8412             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8413             this.maskEl.right.hide();
8414             
8415             this.toolTip.hide();
8416             
8417             this.toolTip.el.hide();
8418             
8419             window.onwheel = function(){ return true;};
8420             
8421             if(this.intervalID){
8422                 window.clearInterval(this.intervalID);
8423                 this.intervalID = false;
8424             }
8425             
8426             this.isMasked = false;
8427             
8428         }
8429         
8430     }
8431     
8432 });
8433
8434 /*
8435  * Based on:
8436  * Ext JS Library 1.1.1
8437  * Copyright(c) 2006-2007, Ext JS, LLC.
8438  *
8439  * Originally Released Under LGPL - original licence link has changed is not relivant.
8440  *
8441  * Fork - LGPL
8442  * <script type="text/javascript">
8443  */
8444 /**
8445  * @class Roo.form.VTypes
8446  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8447  * @singleton
8448  */
8449 Roo.form.VTypes = function(){
8450     // closure these in so they are only created once.
8451     var alpha = /^[a-zA-Z_]+$/;
8452     var alphanum = /^[a-zA-Z0-9_]+$/;
8453     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8454     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8455
8456     // All these messages and functions are configurable
8457     return {
8458         /**
8459          * The function used to validate email addresses
8460          * @param {String} value The email address
8461          */
8462         'email' : function(v){
8463             return email.test(v);
8464         },
8465         /**
8466          * The error text to display when the email validation function returns false
8467          * @type String
8468          */
8469         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8470         /**
8471          * The keystroke filter mask to be applied on email input
8472          * @type RegExp
8473          */
8474         'emailMask' : /[a-z0-9_\.\-@]/i,
8475
8476         /**
8477          * The function used to validate URLs
8478          * @param {String} value The URL
8479          */
8480         'url' : function(v){
8481             return url.test(v);
8482         },
8483         /**
8484          * The error text to display when the url validation function returns false
8485          * @type String
8486          */
8487         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8488         
8489         /**
8490          * The function used to validate alpha values
8491          * @param {String} value The value
8492          */
8493         'alpha' : function(v){
8494             return alpha.test(v);
8495         },
8496         /**
8497          * The error text to display when the alpha validation function returns false
8498          * @type String
8499          */
8500         'alphaText' : 'This field should only contain letters and _',
8501         /**
8502          * The keystroke filter mask to be applied on alpha input
8503          * @type RegExp
8504          */
8505         'alphaMask' : /[a-z_]/i,
8506
8507         /**
8508          * The function used to validate alphanumeric values
8509          * @param {String} value The value
8510          */
8511         'alphanum' : function(v){
8512             return alphanum.test(v);
8513         },
8514         /**
8515          * The error text to display when the alphanumeric validation function returns false
8516          * @type String
8517          */
8518         'alphanumText' : 'This field should only contain letters, numbers and _',
8519         /**
8520          * The keystroke filter mask to be applied on alphanumeric input
8521          * @type RegExp
8522          */
8523         'alphanumMask' : /[a-z0-9_]/i
8524     };
8525 }();/*
8526  * - LGPL
8527  *
8528  * Input
8529  * 
8530  */
8531
8532 /**
8533  * @class Roo.bootstrap.Input
8534  * @extends Roo.bootstrap.Component
8535  * Bootstrap Input class
8536  * @cfg {Boolean} disabled is it disabled
8537  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8538  * @cfg {String} name name of the input
8539  * @cfg {string} fieldLabel - the label associated
8540  * @cfg {string} placeholder - placeholder to put in text.
8541  * @cfg {string}  before - input group add on before
8542  * @cfg {string} after - input group add on after
8543  * @cfg {string} size - (lg|sm) or leave empty..
8544  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8545  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8546  * @cfg {Number} md colspan out of 12 for computer-sized screens
8547  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8548  * @cfg {string} value default value of the input
8549  * @cfg {Number} labelWidth set the width of label 
8550  * @cfg {Number} labellg set the width of label (1-12)
8551  * @cfg {Number} labelmd set the width of label (1-12)
8552  * @cfg {Number} labelsm set the width of label (1-12)
8553  * @cfg {Number} labelxs set the width of label (1-12)
8554  * @cfg {String} labelAlign (top|left)
8555  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8556  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8557  * @cfg {String} indicatorpos (left|right) default left
8558  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8559  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8560
8561  * @cfg {String} align (left|center|right) Default left
8562  * @cfg {Boolean} forceFeedback (true|false) Default false
8563  * 
8564  * @constructor
8565  * Create a new Input
8566  * @param {Object} config The config object
8567  */
8568
8569 Roo.bootstrap.Input = function(config){
8570     
8571     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8572     
8573     this.addEvents({
8574         /**
8575          * @event focus
8576          * Fires when this field receives input focus.
8577          * @param {Roo.form.Field} this
8578          */
8579         focus : true,
8580         /**
8581          * @event blur
8582          * Fires when this field loses input focus.
8583          * @param {Roo.form.Field} this
8584          */
8585         blur : true,
8586         /**
8587          * @event specialkey
8588          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8589          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8590          * @param {Roo.form.Field} this
8591          * @param {Roo.EventObject} e The event object
8592          */
8593         specialkey : true,
8594         /**
8595          * @event change
8596          * Fires just before the field blurs if the field value has changed.
8597          * @param {Roo.form.Field} this
8598          * @param {Mixed} newValue The new value
8599          * @param {Mixed} oldValue The original value
8600          */
8601         change : true,
8602         /**
8603          * @event invalid
8604          * Fires after the field has been marked as invalid.
8605          * @param {Roo.form.Field} this
8606          * @param {String} msg The validation message
8607          */
8608         invalid : true,
8609         /**
8610          * @event valid
8611          * Fires after the field has been validated with no errors.
8612          * @param {Roo.form.Field} this
8613          */
8614         valid : true,
8615          /**
8616          * @event keyup
8617          * Fires after the key up
8618          * @param {Roo.form.Field} this
8619          * @param {Roo.EventObject}  e The event Object
8620          */
8621         keyup : true
8622     });
8623 };
8624
8625 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8626      /**
8627      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8628       automatic validation (defaults to "keyup").
8629      */
8630     validationEvent : "keyup",
8631      /**
8632      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8633      */
8634     validateOnBlur : true,
8635     /**
8636      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8637      */
8638     validationDelay : 250,
8639      /**
8640      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8641      */
8642     focusClass : "x-form-focus",  // not needed???
8643     
8644        
8645     /**
8646      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8647      */
8648     invalidClass : "has-warning",
8649     
8650     /**
8651      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8652      */
8653     validClass : "has-success",
8654     
8655     /**
8656      * @cfg {Boolean} hasFeedback (true|false) default true
8657      */
8658     hasFeedback : true,
8659     
8660     /**
8661      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8662      */
8663     invalidFeedbackClass : "glyphicon-warning-sign",
8664     
8665     /**
8666      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8667      */
8668     validFeedbackClass : "glyphicon-ok",
8669     
8670     /**
8671      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8672      */
8673     selectOnFocus : false,
8674     
8675      /**
8676      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8677      */
8678     maskRe : null,
8679        /**
8680      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8681      */
8682     vtype : null,
8683     
8684       /**
8685      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8686      */
8687     disableKeyFilter : false,
8688     
8689        /**
8690      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8691      */
8692     disabled : false,
8693      /**
8694      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8695      */
8696     allowBlank : true,
8697     /**
8698      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8699      */
8700     blankText : "Please complete this mandatory field",
8701     
8702      /**
8703      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8704      */
8705     minLength : 0,
8706     /**
8707      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8708      */
8709     maxLength : Number.MAX_VALUE,
8710     /**
8711      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8712      */
8713     minLengthText : "The minimum length for this field is {0}",
8714     /**
8715      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8716      */
8717     maxLengthText : "The maximum length for this field is {0}",
8718   
8719     
8720     /**
8721      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8722      * If available, this function will be called only after the basic validators all return true, and will be passed the
8723      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8724      */
8725     validator : null,
8726     /**
8727      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8728      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8729      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8730      */
8731     regex : null,
8732     /**
8733      * @cfg {String} regexText -- Depricated - use Invalid Text
8734      */
8735     regexText : "",
8736     
8737     /**
8738      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8739      */
8740     invalidText : "",
8741     
8742     
8743     
8744     autocomplete: false,
8745     
8746     
8747     fieldLabel : '',
8748     inputType : 'text',
8749     
8750     name : false,
8751     placeholder: false,
8752     before : false,
8753     after : false,
8754     size : false,
8755     hasFocus : false,
8756     preventMark: false,
8757     isFormField : true,
8758     value : '',
8759     labelWidth : 2,
8760     labelAlign : false,
8761     readOnly : false,
8762     align : false,
8763     formatedValue : false,
8764     forceFeedback : false,
8765     
8766     indicatorpos : 'left',
8767     
8768     labellg : 0,
8769     labelmd : 0,
8770     labelsm : 0,
8771     labelxs : 0,
8772     
8773     capture : '',
8774     accept : '',
8775     
8776     parentLabelAlign : function()
8777     {
8778         var parent = this;
8779         while (parent.parent()) {
8780             parent = parent.parent();
8781             if (typeof(parent.labelAlign) !='undefined') {
8782                 return parent.labelAlign;
8783             }
8784         }
8785         return 'left';
8786         
8787     },
8788     
8789     getAutoCreate : function()
8790     {
8791         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8792         
8793         var id = Roo.id();
8794         
8795         var cfg = {};
8796         
8797         if(this.inputType != 'hidden'){
8798             cfg.cls = 'form-group' //input-group
8799         }
8800         
8801         var input =  {
8802             tag: 'input',
8803             id : id,
8804             type : this.inputType,
8805             value : this.value,
8806             cls : 'form-control',
8807             placeholder : this.placeholder || '',
8808             autocomplete : this.autocomplete || 'new-password'
8809         };
8810         
8811         if(this.capture.length){
8812             input.capture = this.capture;
8813         }
8814         
8815         if(this.accept.length){
8816             input.accept = this.accept + "/*";
8817         }
8818         
8819         if(this.align){
8820             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8821         }
8822         
8823         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8824             input.maxLength = this.maxLength;
8825         }
8826         
8827         if (this.disabled) {
8828             input.disabled=true;
8829         }
8830         
8831         if (this.readOnly) {
8832             input.readonly=true;
8833         }
8834         
8835         if (this.name) {
8836             input.name = this.name;
8837         }
8838         
8839         if (this.size) {
8840             input.cls += ' input-' + this.size;
8841         }
8842         
8843         var settings=this;
8844         ['xs','sm','md','lg'].map(function(size){
8845             if (settings[size]) {
8846                 cfg.cls += ' col-' + size + '-' + settings[size];
8847             }
8848         });
8849         
8850         var inputblock = input;
8851         
8852         var feedback = {
8853             tag: 'span',
8854             cls: 'glyphicon form-control-feedback'
8855         };
8856             
8857         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8858             
8859             inputblock = {
8860                 cls : 'has-feedback',
8861                 cn :  [
8862                     input,
8863                     feedback
8864                 ] 
8865             };  
8866         }
8867         
8868         if (this.before || this.after) {
8869             
8870             inputblock = {
8871                 cls : 'input-group',
8872                 cn :  [] 
8873             };
8874             
8875             if (this.before && typeof(this.before) == 'string') {
8876                 
8877                 inputblock.cn.push({
8878                     tag :'span',
8879                     cls : 'roo-input-before input-group-addon',
8880                     html : this.before
8881                 });
8882             }
8883             if (this.before && typeof(this.before) == 'object') {
8884                 this.before = Roo.factory(this.before);
8885                 
8886                 inputblock.cn.push({
8887                     tag :'span',
8888                     cls : 'roo-input-before input-group-' +
8889                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8890                 });
8891             }
8892             
8893             inputblock.cn.push(input);
8894             
8895             if (this.after && typeof(this.after) == 'string') {
8896                 inputblock.cn.push({
8897                     tag :'span',
8898                     cls : 'roo-input-after input-group-addon',
8899                     html : this.after
8900                 });
8901             }
8902             if (this.after && typeof(this.after) == 'object') {
8903                 this.after = Roo.factory(this.after);
8904                 
8905                 inputblock.cn.push({
8906                     tag :'span',
8907                     cls : 'roo-input-after input-group-' +
8908                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8909                 });
8910             }
8911             
8912             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8913                 inputblock.cls += ' has-feedback';
8914                 inputblock.cn.push(feedback);
8915             }
8916         };
8917         
8918         if (align ==='left' && this.fieldLabel.length) {
8919             
8920             cfg.cls += ' roo-form-group-label-left';
8921             
8922             cfg.cn = [
8923                 {
8924                     tag : 'i',
8925                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8926                     tooltip : 'This field is required'
8927                 },
8928                 {
8929                     tag: 'label',
8930                     'for' :  id,
8931                     cls : 'control-label',
8932                     html : this.fieldLabel
8933
8934                 },
8935                 {
8936                     cls : "", 
8937                     cn: [
8938                         inputblock
8939                     ]
8940                 }
8941             ];
8942             
8943             var labelCfg = cfg.cn[1];
8944             var contentCfg = cfg.cn[2];
8945             
8946             if(this.indicatorpos == 'right'){
8947                 cfg.cn = [
8948                     {
8949                         tag: 'label',
8950                         'for' :  id,
8951                         cls : 'control-label',
8952                         cn : [
8953                             {
8954                                 tag : 'span',
8955                                 html : this.fieldLabel
8956                             },
8957                             {
8958                                 tag : 'i',
8959                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8960                                 tooltip : 'This field is required'
8961                             }
8962                         ]
8963                     },
8964                     {
8965                         cls : "",
8966                         cn: [
8967                             inputblock
8968                         ]
8969                     }
8970
8971                 ];
8972                 
8973                 labelCfg = cfg.cn[0];
8974                 contentCfg = cfg.cn[1];
8975             
8976             }
8977             
8978             if(this.labelWidth > 12){
8979                 labelCfg.style = "width: " + this.labelWidth + 'px';
8980             }
8981             
8982             if(this.labelWidth < 13 && this.labelmd == 0){
8983                 this.labelmd = this.labelWidth;
8984             }
8985             
8986             if(this.labellg > 0){
8987                 labelCfg.cls += ' col-lg-' + this.labellg;
8988                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8989             }
8990             
8991             if(this.labelmd > 0){
8992                 labelCfg.cls += ' col-md-' + this.labelmd;
8993                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8994             }
8995             
8996             if(this.labelsm > 0){
8997                 labelCfg.cls += ' col-sm-' + this.labelsm;
8998                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8999             }
9000             
9001             if(this.labelxs > 0){
9002                 labelCfg.cls += ' col-xs-' + this.labelxs;
9003                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9004             }
9005             
9006             
9007         } else if ( this.fieldLabel.length) {
9008                 
9009             cfg.cn = [
9010                 {
9011                     tag : 'i',
9012                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9013                     tooltip : 'This field is required'
9014                 },
9015                 {
9016                     tag: 'label',
9017                    //cls : 'input-group-addon',
9018                     html : this.fieldLabel
9019
9020                 },
9021
9022                inputblock
9023
9024            ];
9025            
9026            if(this.indicatorpos == 'right'){
9027                 
9028                 cfg.cn = [
9029                     {
9030                         tag: 'label',
9031                        //cls : 'input-group-addon',
9032                         html : this.fieldLabel
9033
9034                     },
9035                     {
9036                         tag : 'i',
9037                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9038                         tooltip : 'This field is required'
9039                     },
9040
9041                    inputblock
9042
9043                ];
9044
9045             }
9046
9047         } else {
9048             
9049             cfg.cn = [
9050
9051                     inputblock
9052
9053             ];
9054                 
9055                 
9056         };
9057         
9058         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9059            cfg.cls += ' navbar-form';
9060         }
9061         
9062         if (this.parentType === 'NavGroup') {
9063            cfg.cls += ' navbar-form';
9064            cfg.tag = 'li';
9065         }
9066         
9067         return cfg;
9068         
9069     },
9070     /**
9071      * return the real input element.
9072      */
9073     inputEl: function ()
9074     {
9075         return this.el.select('input.form-control',true).first();
9076     },
9077     
9078     tooltipEl : function()
9079     {
9080         return this.inputEl();
9081     },
9082     
9083     indicatorEl : function()
9084     {
9085         var indicator = this.el.select('i.roo-required-indicator',true).first();
9086         
9087         if(!indicator){
9088             return false;
9089         }
9090         
9091         return indicator;
9092         
9093     },
9094     
9095     setDisabled : function(v)
9096     {
9097         var i  = this.inputEl().dom;
9098         if (!v) {
9099             i.removeAttribute('disabled');
9100             return;
9101             
9102         }
9103         i.setAttribute('disabled','true');
9104     },
9105     initEvents : function()
9106     {
9107           
9108         this.inputEl().on("keydown" , this.fireKey,  this);
9109         this.inputEl().on("focus", this.onFocus,  this);
9110         this.inputEl().on("blur", this.onBlur,  this);
9111         
9112         this.inputEl().relayEvent('keyup', this);
9113         
9114         this.indicator = this.indicatorEl();
9115         
9116         if(this.indicator){
9117             this.indicator.addClass('invisible');
9118         }
9119  
9120         // reference to original value for reset
9121         this.originalValue = this.getValue();
9122         //Roo.form.TextField.superclass.initEvents.call(this);
9123         if(this.validationEvent == 'keyup'){
9124             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9125             this.inputEl().on('keyup', this.filterValidation, this);
9126         }
9127         else if(this.validationEvent !== false){
9128             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9129         }
9130         
9131         if(this.selectOnFocus){
9132             this.on("focus", this.preFocus, this);
9133             
9134         }
9135         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9136             this.inputEl().on("keypress", this.filterKeys, this);
9137         } else {
9138             this.inputEl().relayEvent('keypress', this);
9139         }
9140        /* if(this.grow){
9141             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9142             this.el.on("click", this.autoSize,  this);
9143         }
9144         */
9145         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9146             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9147         }
9148         
9149         if (typeof(this.before) == 'object') {
9150             this.before.render(this.el.select('.roo-input-before',true).first());
9151         }
9152         if (typeof(this.after) == 'object') {
9153             this.after.render(this.el.select('.roo-input-after',true).first());
9154         }
9155         
9156         this.inputEl().on('change', this.onChange, this);
9157         
9158     },
9159     filterValidation : function(e){
9160         if(!e.isNavKeyPress()){
9161             this.validationTask.delay(this.validationDelay);
9162         }
9163     },
9164      /**
9165      * Validates the field value
9166      * @return {Boolean} True if the value is valid, else false
9167      */
9168     validate : function(){
9169         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9170         if(this.disabled || this.validateValue(this.getRawValue())){
9171             this.markValid();
9172             return true;
9173         }
9174         
9175         this.markInvalid();
9176         return false;
9177     },
9178     
9179     
9180     /**
9181      * Validates a value according to the field's validation rules and marks the field as invalid
9182      * if the validation fails
9183      * @param {Mixed} value The value to validate
9184      * @return {Boolean} True if the value is valid, else false
9185      */
9186     validateValue : function(value)
9187     {
9188         if(this.getVisibilityEl().hasClass('hidden')){
9189             return true;
9190         }
9191         
9192         if(value.length < 1)  { // if it's blank
9193             if(this.allowBlank){
9194                 return true;
9195             }
9196             return false;
9197         }
9198         
9199         if(value.length < this.minLength){
9200             return false;
9201         }
9202         if(value.length > this.maxLength){
9203             return false;
9204         }
9205         if(this.vtype){
9206             var vt = Roo.form.VTypes;
9207             if(!vt[this.vtype](value, this)){
9208                 return false;
9209             }
9210         }
9211         if(typeof this.validator == "function"){
9212             var msg = this.validator(value);
9213             if(msg !== true){
9214                 return false;
9215             }
9216             if (typeof(msg) == 'string') {
9217                 this.invalidText = msg;
9218             }
9219         }
9220         
9221         if(this.regex && !this.regex.test(value)){
9222             return false;
9223         }
9224         
9225         return true;
9226     },
9227     
9228      // private
9229     fireKey : function(e){
9230         //Roo.log('field ' + e.getKey());
9231         if(e.isNavKeyPress()){
9232             this.fireEvent("specialkey", this, e);
9233         }
9234     },
9235     focus : function (selectText){
9236         if(this.rendered){
9237             this.inputEl().focus();
9238             if(selectText === true){
9239                 this.inputEl().dom.select();
9240             }
9241         }
9242         return this;
9243     } ,
9244     
9245     onFocus : function(){
9246         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9247            // this.el.addClass(this.focusClass);
9248         }
9249         if(!this.hasFocus){
9250             this.hasFocus = true;
9251             this.startValue = this.getValue();
9252             this.fireEvent("focus", this);
9253         }
9254     },
9255     
9256     beforeBlur : Roo.emptyFn,
9257
9258     
9259     // private
9260     onBlur : function(){
9261         this.beforeBlur();
9262         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9263             //this.el.removeClass(this.focusClass);
9264         }
9265         this.hasFocus = false;
9266         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9267             this.validate();
9268         }
9269         var v = this.getValue();
9270         if(String(v) !== String(this.startValue)){
9271             this.fireEvent('change', this, v, this.startValue);
9272         }
9273         this.fireEvent("blur", this);
9274     },
9275     
9276     onChange : function(e)
9277     {
9278         var v = this.getValue();
9279         if(String(v) !== String(this.startValue)){
9280             this.fireEvent('change', this, v, this.startValue);
9281         }
9282         
9283     },
9284     
9285     /**
9286      * Resets the current field value to the originally loaded value and clears any validation messages
9287      */
9288     reset : function(){
9289         this.setValue(this.originalValue);
9290         this.validate();
9291     },
9292      /**
9293      * Returns the name of the field
9294      * @return {Mixed} name The name field
9295      */
9296     getName: function(){
9297         return this.name;
9298     },
9299      /**
9300      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9301      * @return {Mixed} value The field value
9302      */
9303     getValue : function(){
9304         
9305         var v = this.inputEl().getValue();
9306         
9307         return v;
9308     },
9309     /**
9310      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9311      * @return {Mixed} value The field value
9312      */
9313     getRawValue : function(){
9314         var v = this.inputEl().getValue();
9315         
9316         return v;
9317     },
9318     
9319     /**
9320      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9321      * @param {Mixed} value The value to set
9322      */
9323     setRawValue : function(v){
9324         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9325     },
9326     
9327     selectText : function(start, end){
9328         var v = this.getRawValue();
9329         if(v.length > 0){
9330             start = start === undefined ? 0 : start;
9331             end = end === undefined ? v.length : end;
9332             var d = this.inputEl().dom;
9333             if(d.setSelectionRange){
9334                 d.setSelectionRange(start, end);
9335             }else if(d.createTextRange){
9336                 var range = d.createTextRange();
9337                 range.moveStart("character", start);
9338                 range.moveEnd("character", v.length-end);
9339                 range.select();
9340             }
9341         }
9342     },
9343     
9344     /**
9345      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9346      * @param {Mixed} value The value to set
9347      */
9348     setValue : function(v){
9349         this.value = v;
9350         if(this.rendered){
9351             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9352             this.validate();
9353         }
9354     },
9355     
9356     /*
9357     processValue : function(value){
9358         if(this.stripCharsRe){
9359             var newValue = value.replace(this.stripCharsRe, '');
9360             if(newValue !== value){
9361                 this.setRawValue(newValue);
9362                 return newValue;
9363             }
9364         }
9365         return value;
9366     },
9367   */
9368     preFocus : function(){
9369         
9370         if(this.selectOnFocus){
9371             this.inputEl().dom.select();
9372         }
9373     },
9374     filterKeys : function(e){
9375         var k = e.getKey();
9376         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9377             return;
9378         }
9379         var c = e.getCharCode(), cc = String.fromCharCode(c);
9380         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9381             return;
9382         }
9383         if(!this.maskRe.test(cc)){
9384             e.stopEvent();
9385         }
9386     },
9387      /**
9388      * Clear any invalid styles/messages for this field
9389      */
9390     clearInvalid : function(){
9391         
9392         if(!this.el || this.preventMark){ // not rendered
9393             return;
9394         }
9395         
9396      
9397         this.el.removeClass(this.invalidClass);
9398         
9399         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9400             
9401             var feedback = this.el.select('.form-control-feedback', true).first();
9402             
9403             if(feedback){
9404                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9405             }
9406             
9407         }
9408         
9409         this.fireEvent('valid', this);
9410     },
9411     
9412      /**
9413      * Mark this field as valid
9414      */
9415     markValid : function()
9416     {
9417         if(!this.el  || this.preventMark){ // not rendered...
9418             return;
9419         }
9420         
9421         this.el.removeClass([this.invalidClass, this.validClass]);
9422         
9423         var feedback = this.el.select('.form-control-feedback', true).first();
9424             
9425         if(feedback){
9426             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9427         }
9428         
9429         if(this.indicator){
9430             this.indicator.removeClass('visible');
9431             this.indicator.addClass('invisible');
9432         }
9433         
9434         if(this.disabled){
9435             return;
9436         }
9437         
9438         if(this.allowBlank && !this.getRawValue().length){
9439             return;
9440         }
9441         
9442         this.el.addClass(this.validClass);
9443         
9444         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9445             
9446             var feedback = this.el.select('.form-control-feedback', true).first();
9447             
9448             if(feedback){
9449                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9450                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9451             }
9452             
9453         }
9454         
9455         this.fireEvent('valid', this);
9456     },
9457     
9458      /**
9459      * Mark this field as invalid
9460      * @param {String} msg The validation message
9461      */
9462     markInvalid : function(msg)
9463     {
9464         if(!this.el  || this.preventMark){ // not rendered
9465             return;
9466         }
9467         
9468         this.el.removeClass([this.invalidClass, this.validClass]);
9469         
9470         var feedback = this.el.select('.form-control-feedback', true).first();
9471             
9472         if(feedback){
9473             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9474         }
9475
9476         if(this.disabled){
9477             return;
9478         }
9479         
9480         if(this.allowBlank && !this.getRawValue().length){
9481             return;
9482         }
9483         
9484         if(this.indicator){
9485             this.indicator.removeClass('invisible');
9486             this.indicator.addClass('visible');
9487         }
9488         
9489         this.el.addClass(this.invalidClass);
9490         
9491         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9492             
9493             var feedback = this.el.select('.form-control-feedback', true).first();
9494             
9495             if(feedback){
9496                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9497                 
9498                 if(this.getValue().length || this.forceFeedback){
9499                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9500                 }
9501                 
9502             }
9503             
9504         }
9505         
9506         this.fireEvent('invalid', this, msg);
9507     },
9508     // private
9509     SafariOnKeyDown : function(event)
9510     {
9511         // this is a workaround for a password hang bug on chrome/ webkit.
9512         if (this.inputEl().dom.type != 'password') {
9513             return;
9514         }
9515         
9516         var isSelectAll = false;
9517         
9518         if(this.inputEl().dom.selectionEnd > 0){
9519             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9520         }
9521         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9522             event.preventDefault();
9523             this.setValue('');
9524             return;
9525         }
9526         
9527         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9528             
9529             event.preventDefault();
9530             // this is very hacky as keydown always get's upper case.
9531             //
9532             var cc = String.fromCharCode(event.getCharCode());
9533             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9534             
9535         }
9536     },
9537     adjustWidth : function(tag, w){
9538         tag = tag.toLowerCase();
9539         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9540             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9541                 if(tag == 'input'){
9542                     return w + 2;
9543                 }
9544                 if(tag == 'textarea'){
9545                     return w-2;
9546                 }
9547             }else if(Roo.isOpera){
9548                 if(tag == 'input'){
9549                     return w + 2;
9550                 }
9551                 if(tag == 'textarea'){
9552                     return w-2;
9553                 }
9554             }
9555         }
9556         return w;
9557     },
9558     
9559     setFieldLabel : function(v)
9560     {
9561         if(!this.rendered){
9562             return;
9563         }
9564         
9565         if(this.indicator){
9566             var ar = this.el.select('label > span',true);
9567             
9568             if (ar.elements.length) {
9569                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9570                 this.fieldLabel = v;
9571                 return;
9572             }
9573             
9574             var br = this.el.select('label',true);
9575             
9576             if(br.elements.length) {
9577                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9578                 this.fieldLabel = v;
9579                 return;
9580             }
9581             
9582             Roo.log('Cannot Found any of label > span || label in input');
9583             return;
9584         }
9585         
9586         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9587         this.fieldLabel = v;
9588         
9589         
9590     }
9591 });
9592
9593  
9594 /*
9595  * - LGPL
9596  *
9597  * Input
9598  * 
9599  */
9600
9601 /**
9602  * @class Roo.bootstrap.TextArea
9603  * @extends Roo.bootstrap.Input
9604  * Bootstrap TextArea class
9605  * @cfg {Number} cols Specifies the visible width of a text area
9606  * @cfg {Number} rows Specifies the visible number of lines in a text area
9607  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9608  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9609  * @cfg {string} html text
9610  * 
9611  * @constructor
9612  * Create a new TextArea
9613  * @param {Object} config The config object
9614  */
9615
9616 Roo.bootstrap.TextArea = function(config){
9617     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9618    
9619 };
9620
9621 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9622      
9623     cols : false,
9624     rows : 5,
9625     readOnly : false,
9626     warp : 'soft',
9627     resize : false,
9628     value: false,
9629     html: false,
9630     
9631     getAutoCreate : function(){
9632         
9633         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9634         
9635         var id = Roo.id();
9636         
9637         var cfg = {};
9638         
9639         if(this.inputType != 'hidden'){
9640             cfg.cls = 'form-group' //input-group
9641         }
9642         
9643         var input =  {
9644             tag: 'textarea',
9645             id : id,
9646             warp : this.warp,
9647             rows : this.rows,
9648             value : this.value || '',
9649             html: this.html || '',
9650             cls : 'form-control',
9651             placeholder : this.placeholder || '' 
9652             
9653         };
9654         
9655         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9656             input.maxLength = this.maxLength;
9657         }
9658         
9659         if(this.resize){
9660             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9661         }
9662         
9663         if(this.cols){
9664             input.cols = this.cols;
9665         }
9666         
9667         if (this.readOnly) {
9668             input.readonly = true;
9669         }
9670         
9671         if (this.name) {
9672             input.name = this.name;
9673         }
9674         
9675         if (this.size) {
9676             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9677         }
9678         
9679         var settings=this;
9680         ['xs','sm','md','lg'].map(function(size){
9681             if (settings[size]) {
9682                 cfg.cls += ' col-' + size + '-' + settings[size];
9683             }
9684         });
9685         
9686         var inputblock = input;
9687         
9688         if(this.hasFeedback && !this.allowBlank){
9689             
9690             var feedback = {
9691                 tag: 'span',
9692                 cls: 'glyphicon form-control-feedback'
9693             };
9694
9695             inputblock = {
9696                 cls : 'has-feedback',
9697                 cn :  [
9698                     input,
9699                     feedback
9700                 ] 
9701             };  
9702         }
9703         
9704         
9705         if (this.before || this.after) {
9706             
9707             inputblock = {
9708                 cls : 'input-group',
9709                 cn :  [] 
9710             };
9711             if (this.before) {
9712                 inputblock.cn.push({
9713                     tag :'span',
9714                     cls : 'input-group-addon',
9715                     html : this.before
9716                 });
9717             }
9718             
9719             inputblock.cn.push(input);
9720             
9721             if(this.hasFeedback && !this.allowBlank){
9722                 inputblock.cls += ' has-feedback';
9723                 inputblock.cn.push(feedback);
9724             }
9725             
9726             if (this.after) {
9727                 inputblock.cn.push({
9728                     tag :'span',
9729                     cls : 'input-group-addon',
9730                     html : this.after
9731                 });
9732             }
9733             
9734         }
9735         
9736         if (align ==='left' && this.fieldLabel.length) {
9737             cfg.cn = [
9738                 {
9739                     tag: 'label',
9740                     'for' :  id,
9741                     cls : 'control-label',
9742                     html : this.fieldLabel
9743                 },
9744                 {
9745                     cls : "",
9746                     cn: [
9747                         inputblock
9748                     ]
9749                 }
9750
9751             ];
9752             
9753             if(this.labelWidth > 12){
9754                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9755             }
9756
9757             if(this.labelWidth < 13 && this.labelmd == 0){
9758                 this.labelmd = this.labelWidth;
9759             }
9760
9761             if(this.labellg > 0){
9762                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9763                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9764             }
9765
9766             if(this.labelmd > 0){
9767                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9768                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9769             }
9770
9771             if(this.labelsm > 0){
9772                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9773                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9774             }
9775
9776             if(this.labelxs > 0){
9777                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9778                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9779             }
9780             
9781         } else if ( this.fieldLabel.length) {
9782             cfg.cn = [
9783
9784                {
9785                    tag: 'label',
9786                    //cls : 'input-group-addon',
9787                    html : this.fieldLabel
9788
9789                },
9790
9791                inputblock
9792
9793            ];
9794
9795         } else {
9796
9797             cfg.cn = [
9798
9799                 inputblock
9800
9801             ];
9802                 
9803         }
9804         
9805         if (this.disabled) {
9806             input.disabled=true;
9807         }
9808         
9809         return cfg;
9810         
9811     },
9812     /**
9813      * return the real textarea element.
9814      */
9815     inputEl: function ()
9816     {
9817         return this.el.select('textarea.form-control',true).first();
9818     },
9819     
9820     /**
9821      * Clear any invalid styles/messages for this field
9822      */
9823     clearInvalid : function()
9824     {
9825         
9826         if(!this.el || this.preventMark){ // not rendered
9827             return;
9828         }
9829         
9830         var label = this.el.select('label', true).first();
9831         var icon = this.el.select('i.fa-star', true).first();
9832         
9833         if(label && icon){
9834             icon.remove();
9835         }
9836         
9837         this.el.removeClass(this.invalidClass);
9838         
9839         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9840             
9841             var feedback = this.el.select('.form-control-feedback', true).first();
9842             
9843             if(feedback){
9844                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9845             }
9846             
9847         }
9848         
9849         this.fireEvent('valid', this);
9850     },
9851     
9852      /**
9853      * Mark this field as valid
9854      */
9855     markValid : function()
9856     {
9857         if(!this.el  || this.preventMark){ // not rendered
9858             return;
9859         }
9860         
9861         this.el.removeClass([this.invalidClass, this.validClass]);
9862         
9863         var feedback = this.el.select('.form-control-feedback', true).first();
9864             
9865         if(feedback){
9866             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9867         }
9868
9869         if(this.disabled || this.allowBlank){
9870             return;
9871         }
9872         
9873         var label = this.el.select('label', true).first();
9874         var icon = this.el.select('i.fa-star', true).first();
9875         
9876         if(label && icon){
9877             icon.remove();
9878         }
9879         
9880         this.el.addClass(this.validClass);
9881         
9882         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9883             
9884             var feedback = this.el.select('.form-control-feedback', true).first();
9885             
9886             if(feedback){
9887                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9888                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9889             }
9890             
9891         }
9892         
9893         this.fireEvent('valid', this);
9894     },
9895     
9896      /**
9897      * Mark this field as invalid
9898      * @param {String} msg The validation message
9899      */
9900     markInvalid : function(msg)
9901     {
9902         if(!this.el  || this.preventMark){ // not rendered
9903             return;
9904         }
9905         
9906         this.el.removeClass([this.invalidClass, this.validClass]);
9907         
9908         var feedback = this.el.select('.form-control-feedback', true).first();
9909             
9910         if(feedback){
9911             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9912         }
9913
9914         if(this.disabled || this.allowBlank){
9915             return;
9916         }
9917         
9918         var label = this.el.select('label', true).first();
9919         var icon = this.el.select('i.fa-star', true).first();
9920         
9921         if(!this.getValue().length && label && !icon){
9922             this.el.createChild({
9923                 tag : 'i',
9924                 cls : 'text-danger fa fa-lg fa-star',
9925                 tooltip : 'This field is required',
9926                 style : 'margin-right:5px;'
9927             }, label, true);
9928         }
9929
9930         this.el.addClass(this.invalidClass);
9931         
9932         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9933             
9934             var feedback = this.el.select('.form-control-feedback', true).first();
9935             
9936             if(feedback){
9937                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9938                 
9939                 if(this.getValue().length || this.forceFeedback){
9940                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9941                 }
9942                 
9943             }
9944             
9945         }
9946         
9947         this.fireEvent('invalid', this, msg);
9948     }
9949 });
9950
9951  
9952 /*
9953  * - LGPL
9954  *
9955  * trigger field - base class for combo..
9956  * 
9957  */
9958  
9959 /**
9960  * @class Roo.bootstrap.TriggerField
9961  * @extends Roo.bootstrap.Input
9962  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9963  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9964  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9965  * for which you can provide a custom implementation.  For example:
9966  * <pre><code>
9967 var trigger = new Roo.bootstrap.TriggerField();
9968 trigger.onTriggerClick = myTriggerFn;
9969 trigger.applyTo('my-field');
9970 </code></pre>
9971  *
9972  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9973  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9974  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9975  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9976  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9977
9978  * @constructor
9979  * Create a new TriggerField.
9980  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9981  * to the base TextField)
9982  */
9983 Roo.bootstrap.TriggerField = function(config){
9984     this.mimicing = false;
9985     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9986 };
9987
9988 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9989     /**
9990      * @cfg {String} triggerClass A CSS class to apply to the trigger
9991      */
9992      /**
9993      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9994      */
9995     hideTrigger:false,
9996
9997     /**
9998      * @cfg {Boolean} removable (true|false) special filter default false
9999      */
10000     removable : false,
10001     
10002     /** @cfg {Boolean} grow @hide */
10003     /** @cfg {Number} growMin @hide */
10004     /** @cfg {Number} growMax @hide */
10005
10006     /**
10007      * @hide 
10008      * @method
10009      */
10010     autoSize: Roo.emptyFn,
10011     // private
10012     monitorTab : true,
10013     // private
10014     deferHeight : true,
10015
10016     
10017     actionMode : 'wrap',
10018     
10019     caret : false,
10020     
10021     
10022     getAutoCreate : function(){
10023        
10024         var align = this.labelAlign || this.parentLabelAlign();
10025         
10026         var id = Roo.id();
10027         
10028         var cfg = {
10029             cls: 'form-group' //input-group
10030         };
10031         
10032         
10033         var input =  {
10034             tag: 'input',
10035             id : id,
10036             type : this.inputType,
10037             cls : 'form-control',
10038             autocomplete: 'new-password',
10039             placeholder : this.placeholder || '' 
10040             
10041         };
10042         if (this.name) {
10043             input.name = this.name;
10044         }
10045         if (this.size) {
10046             input.cls += ' input-' + this.size;
10047         }
10048         
10049         if (this.disabled) {
10050             input.disabled=true;
10051         }
10052         
10053         var inputblock = input;
10054         
10055         if(this.hasFeedback && !this.allowBlank){
10056             
10057             var feedback = {
10058                 tag: 'span',
10059                 cls: 'glyphicon form-control-feedback'
10060             };
10061             
10062             if(this.removable && !this.editable && !this.tickable){
10063                 inputblock = {
10064                     cls : 'has-feedback',
10065                     cn :  [
10066                         inputblock,
10067                         {
10068                             tag: 'button',
10069                             html : 'x',
10070                             cls : 'roo-combo-removable-btn close'
10071                         },
10072                         feedback
10073                     ] 
10074                 };
10075             } else {
10076                 inputblock = {
10077                     cls : 'has-feedback',
10078                     cn :  [
10079                         inputblock,
10080                         feedback
10081                     ] 
10082                 };
10083             }
10084
10085         } else {
10086             if(this.removable && !this.editable && !this.tickable){
10087                 inputblock = {
10088                     cls : 'roo-removable',
10089                     cn :  [
10090                         inputblock,
10091                         {
10092                             tag: 'button',
10093                             html : 'x',
10094                             cls : 'roo-combo-removable-btn close'
10095                         }
10096                     ] 
10097                 };
10098             }
10099         }
10100         
10101         if (this.before || this.after) {
10102             
10103             inputblock = {
10104                 cls : 'input-group',
10105                 cn :  [] 
10106             };
10107             if (this.before) {
10108                 inputblock.cn.push({
10109                     tag :'span',
10110                     cls : 'input-group-addon',
10111                     html : this.before
10112                 });
10113             }
10114             
10115             inputblock.cn.push(input);
10116             
10117             if(this.hasFeedback && !this.allowBlank){
10118                 inputblock.cls += ' has-feedback';
10119                 inputblock.cn.push(feedback);
10120             }
10121             
10122             if (this.after) {
10123                 inputblock.cn.push({
10124                     tag :'span',
10125                     cls : 'input-group-addon',
10126                     html : this.after
10127                 });
10128             }
10129             
10130         };
10131         
10132         var box = {
10133             tag: 'div',
10134             cn: [
10135                 {
10136                     tag: 'input',
10137                     type : 'hidden',
10138                     cls: 'form-hidden-field'
10139                 },
10140                 inputblock
10141             ]
10142             
10143         };
10144         
10145         if(this.multiple){
10146             box = {
10147                 tag: 'div',
10148                 cn: [
10149                     {
10150                         tag: 'input',
10151                         type : 'hidden',
10152                         cls: 'form-hidden-field'
10153                     },
10154                     {
10155                         tag: 'ul',
10156                         cls: 'roo-select2-choices',
10157                         cn:[
10158                             {
10159                                 tag: 'li',
10160                                 cls: 'roo-select2-search-field',
10161                                 cn: [
10162
10163                                     inputblock
10164                                 ]
10165                             }
10166                         ]
10167                     }
10168                 ]
10169             }
10170         };
10171         
10172         var combobox = {
10173             cls: 'roo-select2-container input-group',
10174             cn: [
10175                 box
10176 //                {
10177 //                    tag: 'ul',
10178 //                    cls: 'typeahead typeahead-long dropdown-menu',
10179 //                    style: 'display:none'
10180 //                }
10181             ]
10182         };
10183         
10184         if(!this.multiple && this.showToggleBtn){
10185             
10186             var caret = {
10187                         tag: 'span',
10188                         cls: 'caret'
10189              };
10190             if (this.caret != false) {
10191                 caret = {
10192                      tag: 'i',
10193                      cls: 'fa fa-' + this.caret
10194                 };
10195                 
10196             }
10197             
10198             combobox.cn.push({
10199                 tag :'span',
10200                 cls : 'input-group-addon btn dropdown-toggle',
10201                 cn : [
10202                     caret,
10203                     {
10204                         tag: 'span',
10205                         cls: 'combobox-clear',
10206                         cn  : [
10207                             {
10208                                 tag : 'i',
10209                                 cls: 'icon-remove'
10210                             }
10211                         ]
10212                     }
10213                 ]
10214
10215             })
10216         }
10217         
10218         if(this.multiple){
10219             combobox.cls += ' roo-select2-container-multi';
10220         }
10221         
10222         if (align ==='left' && this.fieldLabel.length) {
10223             
10224             cfg.cls += ' roo-form-group-label-left';
10225
10226             cfg.cn = [
10227                 {
10228                     tag : 'i',
10229                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10230                     tooltip : 'This field is required'
10231                 },
10232                 {
10233                     tag: 'label',
10234                     'for' :  id,
10235                     cls : 'control-label',
10236                     html : this.fieldLabel
10237
10238                 },
10239                 {
10240                     cls : "", 
10241                     cn: [
10242                         combobox
10243                     ]
10244                 }
10245
10246             ];
10247             
10248             var labelCfg = cfg.cn[1];
10249             var contentCfg = cfg.cn[2];
10250             
10251             if(this.indicatorpos == 'right'){
10252                 cfg.cn = [
10253                     {
10254                         tag: 'label',
10255                         'for' :  id,
10256                         cls : 'control-label',
10257                         cn : [
10258                             {
10259                                 tag : 'span',
10260                                 html : this.fieldLabel
10261                             },
10262                             {
10263                                 tag : 'i',
10264                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10265                                 tooltip : 'This field is required'
10266                             }
10267                         ]
10268                     },
10269                     {
10270                         cls : "", 
10271                         cn: [
10272                             combobox
10273                         ]
10274                     }
10275
10276                 ];
10277                 
10278                 labelCfg = cfg.cn[0];
10279                 contentCfg = cfg.cn[1];
10280             }
10281             
10282             if(this.labelWidth > 12){
10283                 labelCfg.style = "width: " + this.labelWidth + 'px';
10284             }
10285             
10286             if(this.labelWidth < 13 && this.labelmd == 0){
10287                 this.labelmd = this.labelWidth;
10288             }
10289             
10290             if(this.labellg > 0){
10291                 labelCfg.cls += ' col-lg-' + this.labellg;
10292                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10293             }
10294             
10295             if(this.labelmd > 0){
10296                 labelCfg.cls += ' col-md-' + this.labelmd;
10297                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10298             }
10299             
10300             if(this.labelsm > 0){
10301                 labelCfg.cls += ' col-sm-' + this.labelsm;
10302                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10303             }
10304             
10305             if(this.labelxs > 0){
10306                 labelCfg.cls += ' col-xs-' + this.labelxs;
10307                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10308             }
10309             
10310         } else if ( this.fieldLabel.length) {
10311 //                Roo.log(" label");
10312             cfg.cn = [
10313                 {
10314                    tag : 'i',
10315                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10316                    tooltip : 'This field is required'
10317                },
10318                {
10319                    tag: 'label',
10320                    //cls : 'input-group-addon',
10321                    html : this.fieldLabel
10322
10323                },
10324
10325                combobox
10326
10327             ];
10328             
10329             if(this.indicatorpos == 'right'){
10330                 
10331                 cfg.cn = [
10332                     {
10333                        tag: 'label',
10334                        cn : [
10335                            {
10336                                tag : 'span',
10337                                html : this.fieldLabel
10338                            },
10339                            {
10340                               tag : 'i',
10341                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10342                               tooltip : 'This field is required'
10343                            }
10344                        ]
10345
10346                     },
10347                     combobox
10348
10349                 ];
10350
10351             }
10352
10353         } else {
10354             
10355 //                Roo.log(" no label && no align");
10356                 cfg = combobox
10357                      
10358                 
10359         }
10360         
10361         var settings=this;
10362         ['xs','sm','md','lg'].map(function(size){
10363             if (settings[size]) {
10364                 cfg.cls += ' col-' + size + '-' + settings[size];
10365             }
10366         });
10367         
10368         return cfg;
10369         
10370     },
10371     
10372     
10373     
10374     // private
10375     onResize : function(w, h){
10376 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10377 //        if(typeof w == 'number'){
10378 //            var x = w - this.trigger.getWidth();
10379 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10380 //            this.trigger.setStyle('left', x+'px');
10381 //        }
10382     },
10383
10384     // private
10385     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10386
10387     // private
10388     getResizeEl : function(){
10389         return this.inputEl();
10390     },
10391
10392     // private
10393     getPositionEl : function(){
10394         return this.inputEl();
10395     },
10396
10397     // private
10398     alignErrorIcon : function(){
10399         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10400     },
10401
10402     // private
10403     initEvents : function(){
10404         
10405         this.createList();
10406         
10407         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10408         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10409         if(!this.multiple && this.showToggleBtn){
10410             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10411             if(this.hideTrigger){
10412                 this.trigger.setDisplayed(false);
10413             }
10414             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10415         }
10416         
10417         if(this.multiple){
10418             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10419         }
10420         
10421         if(this.removable && !this.editable && !this.tickable){
10422             var close = this.closeTriggerEl();
10423             
10424             if(close){
10425                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10426                 close.on('click', this.removeBtnClick, this, close);
10427             }
10428         }
10429         
10430         //this.trigger.addClassOnOver('x-form-trigger-over');
10431         //this.trigger.addClassOnClick('x-form-trigger-click');
10432         
10433         //if(!this.width){
10434         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10435         //}
10436     },
10437     
10438     closeTriggerEl : function()
10439     {
10440         var close = this.el.select('.roo-combo-removable-btn', true).first();
10441         return close ? close : false;
10442     },
10443     
10444     removeBtnClick : function(e, h, el)
10445     {
10446         e.preventDefault();
10447         
10448         if(this.fireEvent("remove", this) !== false){
10449             this.reset();
10450             this.fireEvent("afterremove", this)
10451         }
10452     },
10453     
10454     createList : function()
10455     {
10456         this.list = Roo.get(document.body).createChild({
10457             tag: 'ul',
10458             cls: 'typeahead typeahead-long dropdown-menu',
10459             style: 'display:none'
10460         });
10461         
10462         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10463         
10464     },
10465
10466     // private
10467     initTrigger : function(){
10468        
10469     },
10470
10471     // private
10472     onDestroy : function(){
10473         if(this.trigger){
10474             this.trigger.removeAllListeners();
10475           //  this.trigger.remove();
10476         }
10477         //if(this.wrap){
10478         //    this.wrap.remove();
10479         //}
10480         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10481     },
10482
10483     // private
10484     onFocus : function(){
10485         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10486         /*
10487         if(!this.mimicing){
10488             this.wrap.addClass('x-trigger-wrap-focus');
10489             this.mimicing = true;
10490             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10491             if(this.monitorTab){
10492                 this.el.on("keydown", this.checkTab, this);
10493             }
10494         }
10495         */
10496     },
10497
10498     // private
10499     checkTab : function(e){
10500         if(e.getKey() == e.TAB){
10501             this.triggerBlur();
10502         }
10503     },
10504
10505     // private
10506     onBlur : function(){
10507         // do nothing
10508     },
10509
10510     // private
10511     mimicBlur : function(e, t){
10512         /*
10513         if(!this.wrap.contains(t) && this.validateBlur()){
10514             this.triggerBlur();
10515         }
10516         */
10517     },
10518
10519     // private
10520     triggerBlur : function(){
10521         this.mimicing = false;
10522         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10523         if(this.monitorTab){
10524             this.el.un("keydown", this.checkTab, this);
10525         }
10526         //this.wrap.removeClass('x-trigger-wrap-focus');
10527         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10528     },
10529
10530     // private
10531     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10532     validateBlur : function(e, t){
10533         return true;
10534     },
10535
10536     // private
10537     onDisable : function(){
10538         this.inputEl().dom.disabled = true;
10539         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10540         //if(this.wrap){
10541         //    this.wrap.addClass('x-item-disabled');
10542         //}
10543     },
10544
10545     // private
10546     onEnable : function(){
10547         this.inputEl().dom.disabled = false;
10548         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10549         //if(this.wrap){
10550         //    this.el.removeClass('x-item-disabled');
10551         //}
10552     },
10553
10554     // private
10555     onShow : function(){
10556         var ae = this.getActionEl();
10557         
10558         if(ae){
10559             ae.dom.style.display = '';
10560             ae.dom.style.visibility = 'visible';
10561         }
10562     },
10563
10564     // private
10565     
10566     onHide : function(){
10567         var ae = this.getActionEl();
10568         ae.dom.style.display = 'none';
10569     },
10570
10571     /**
10572      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10573      * by an implementing function.
10574      * @method
10575      * @param {EventObject} e
10576      */
10577     onTriggerClick : Roo.emptyFn
10578 });
10579  /*
10580  * Based on:
10581  * Ext JS Library 1.1.1
10582  * Copyright(c) 2006-2007, Ext JS, LLC.
10583  *
10584  * Originally Released Under LGPL - original licence link has changed is not relivant.
10585  *
10586  * Fork - LGPL
10587  * <script type="text/javascript">
10588  */
10589
10590
10591 /**
10592  * @class Roo.data.SortTypes
10593  * @singleton
10594  * Defines the default sorting (casting?) comparison functions used when sorting data.
10595  */
10596 Roo.data.SortTypes = {
10597     /**
10598      * Default sort that does nothing
10599      * @param {Mixed} s The value being converted
10600      * @return {Mixed} The comparison value
10601      */
10602     none : function(s){
10603         return s;
10604     },
10605     
10606     /**
10607      * The regular expression used to strip tags
10608      * @type {RegExp}
10609      * @property
10610      */
10611     stripTagsRE : /<\/?[^>]+>/gi,
10612     
10613     /**
10614      * Strips all HTML tags to sort on text only
10615      * @param {Mixed} s The value being converted
10616      * @return {String} The comparison value
10617      */
10618     asText : function(s){
10619         return String(s).replace(this.stripTagsRE, "");
10620     },
10621     
10622     /**
10623      * Strips all HTML tags to sort on text only - Case insensitive
10624      * @param {Mixed} s The value being converted
10625      * @return {String} The comparison value
10626      */
10627     asUCText : function(s){
10628         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10629     },
10630     
10631     /**
10632      * Case insensitive string
10633      * @param {Mixed} s The value being converted
10634      * @return {String} The comparison value
10635      */
10636     asUCString : function(s) {
10637         return String(s).toUpperCase();
10638     },
10639     
10640     /**
10641      * Date sorting
10642      * @param {Mixed} s The value being converted
10643      * @return {Number} The comparison value
10644      */
10645     asDate : function(s) {
10646         if(!s){
10647             return 0;
10648         }
10649         if(s instanceof Date){
10650             return s.getTime();
10651         }
10652         return Date.parse(String(s));
10653     },
10654     
10655     /**
10656      * Float sorting
10657      * @param {Mixed} s The value being converted
10658      * @return {Float} The comparison value
10659      */
10660     asFloat : function(s) {
10661         var val = parseFloat(String(s).replace(/,/g, ""));
10662         if(isNaN(val)) {
10663             val = 0;
10664         }
10665         return val;
10666     },
10667     
10668     /**
10669      * Integer sorting
10670      * @param {Mixed} s The value being converted
10671      * @return {Number} The comparison value
10672      */
10673     asInt : function(s) {
10674         var val = parseInt(String(s).replace(/,/g, ""));
10675         if(isNaN(val)) {
10676             val = 0;
10677         }
10678         return val;
10679     }
10680 };/*
10681  * Based on:
10682  * Ext JS Library 1.1.1
10683  * Copyright(c) 2006-2007, Ext JS, LLC.
10684  *
10685  * Originally Released Under LGPL - original licence link has changed is not relivant.
10686  *
10687  * Fork - LGPL
10688  * <script type="text/javascript">
10689  */
10690
10691 /**
10692 * @class Roo.data.Record
10693  * Instances of this class encapsulate both record <em>definition</em> information, and record
10694  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10695  * to access Records cached in an {@link Roo.data.Store} object.<br>
10696  * <p>
10697  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10698  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10699  * objects.<br>
10700  * <p>
10701  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10702  * @constructor
10703  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10704  * {@link #create}. The parameters are the same.
10705  * @param {Array} data An associative Array of data values keyed by the field name.
10706  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10707  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10708  * not specified an integer id is generated.
10709  */
10710 Roo.data.Record = function(data, id){
10711     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10712     this.data = data;
10713 };
10714
10715 /**
10716  * Generate a constructor for a specific record layout.
10717  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10718  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10719  * Each field definition object may contain the following properties: <ul>
10720  * <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,
10721  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10722  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10723  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10724  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10725  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10726  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10727  * this may be omitted.</p></li>
10728  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10729  * <ul><li>auto (Default, implies no conversion)</li>
10730  * <li>string</li>
10731  * <li>int</li>
10732  * <li>float</li>
10733  * <li>boolean</li>
10734  * <li>date</li></ul></p></li>
10735  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10736  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10737  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10738  * by the Reader into an object that will be stored in the Record. It is passed the
10739  * following parameters:<ul>
10740  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10741  * </ul></p></li>
10742  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10743  * </ul>
10744  * <br>usage:<br><pre><code>
10745 var TopicRecord = Roo.data.Record.create(
10746     {name: 'title', mapping: 'topic_title'},
10747     {name: 'author', mapping: 'username'},
10748     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10749     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10750     {name: 'lastPoster', mapping: 'user2'},
10751     {name: 'excerpt', mapping: 'post_text'}
10752 );
10753
10754 var myNewRecord = new TopicRecord({
10755     title: 'Do my job please',
10756     author: 'noobie',
10757     totalPosts: 1,
10758     lastPost: new Date(),
10759     lastPoster: 'Animal',
10760     excerpt: 'No way dude!'
10761 });
10762 myStore.add(myNewRecord);
10763 </code></pre>
10764  * @method create
10765  * @static
10766  */
10767 Roo.data.Record.create = function(o){
10768     var f = function(){
10769         f.superclass.constructor.apply(this, arguments);
10770     };
10771     Roo.extend(f, Roo.data.Record);
10772     var p = f.prototype;
10773     p.fields = new Roo.util.MixedCollection(false, function(field){
10774         return field.name;
10775     });
10776     for(var i = 0, len = o.length; i < len; i++){
10777         p.fields.add(new Roo.data.Field(o[i]));
10778     }
10779     f.getField = function(name){
10780         return p.fields.get(name);  
10781     };
10782     return f;
10783 };
10784
10785 Roo.data.Record.AUTO_ID = 1000;
10786 Roo.data.Record.EDIT = 'edit';
10787 Roo.data.Record.REJECT = 'reject';
10788 Roo.data.Record.COMMIT = 'commit';
10789
10790 Roo.data.Record.prototype = {
10791     /**
10792      * Readonly flag - true if this record has been modified.
10793      * @type Boolean
10794      */
10795     dirty : false,
10796     editing : false,
10797     error: null,
10798     modified: null,
10799
10800     // private
10801     join : function(store){
10802         this.store = store;
10803     },
10804
10805     /**
10806      * Set the named field to the specified value.
10807      * @param {String} name The name of the field to set.
10808      * @param {Object} value The value to set the field to.
10809      */
10810     set : function(name, value){
10811         if(this.data[name] == value){
10812             return;
10813         }
10814         this.dirty = true;
10815         if(!this.modified){
10816             this.modified = {};
10817         }
10818         if(typeof this.modified[name] == 'undefined'){
10819             this.modified[name] = this.data[name];
10820         }
10821         this.data[name] = value;
10822         if(!this.editing && this.store){
10823             this.store.afterEdit(this);
10824         }       
10825     },
10826
10827     /**
10828      * Get the value of the named field.
10829      * @param {String} name The name of the field to get the value of.
10830      * @return {Object} The value of the field.
10831      */
10832     get : function(name){
10833         return this.data[name]; 
10834     },
10835
10836     // private
10837     beginEdit : function(){
10838         this.editing = true;
10839         this.modified = {}; 
10840     },
10841
10842     // private
10843     cancelEdit : function(){
10844         this.editing = false;
10845         delete this.modified;
10846     },
10847
10848     // private
10849     endEdit : function(){
10850         this.editing = false;
10851         if(this.dirty && this.store){
10852             this.store.afterEdit(this);
10853         }
10854     },
10855
10856     /**
10857      * Usually called by the {@link Roo.data.Store} which owns the Record.
10858      * Rejects all changes made to the Record since either creation, or the last commit operation.
10859      * Modified fields are reverted to their original values.
10860      * <p>
10861      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10862      * of reject operations.
10863      */
10864     reject : function(){
10865         var m = this.modified;
10866         for(var n in m){
10867             if(typeof m[n] != "function"){
10868                 this.data[n] = m[n];
10869             }
10870         }
10871         this.dirty = false;
10872         delete this.modified;
10873         this.editing = false;
10874         if(this.store){
10875             this.store.afterReject(this);
10876         }
10877     },
10878
10879     /**
10880      * Usually called by the {@link Roo.data.Store} which owns the Record.
10881      * Commits all changes made to the Record since either creation, or the last commit operation.
10882      * <p>
10883      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10884      * of commit operations.
10885      */
10886     commit : function(){
10887         this.dirty = false;
10888         delete this.modified;
10889         this.editing = false;
10890         if(this.store){
10891             this.store.afterCommit(this);
10892         }
10893     },
10894
10895     // private
10896     hasError : function(){
10897         return this.error != null;
10898     },
10899
10900     // private
10901     clearError : function(){
10902         this.error = null;
10903     },
10904
10905     /**
10906      * Creates a copy of this record.
10907      * @param {String} id (optional) A new record id if you don't want to use this record's id
10908      * @return {Record}
10909      */
10910     copy : function(newId) {
10911         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10912     }
10913 };/*
10914  * Based on:
10915  * Ext JS Library 1.1.1
10916  * Copyright(c) 2006-2007, Ext JS, LLC.
10917  *
10918  * Originally Released Under LGPL - original licence link has changed is not relivant.
10919  *
10920  * Fork - LGPL
10921  * <script type="text/javascript">
10922  */
10923
10924
10925
10926 /**
10927  * @class Roo.data.Store
10928  * @extends Roo.util.Observable
10929  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10930  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10931  * <p>
10932  * 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
10933  * has no knowledge of the format of the data returned by the Proxy.<br>
10934  * <p>
10935  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10936  * instances from the data object. These records are cached and made available through accessor functions.
10937  * @constructor
10938  * Creates a new Store.
10939  * @param {Object} config A config object containing the objects needed for the Store to access data,
10940  * and read the data into Records.
10941  */
10942 Roo.data.Store = function(config){
10943     this.data = new Roo.util.MixedCollection(false);
10944     this.data.getKey = function(o){
10945         return o.id;
10946     };
10947     this.baseParams = {};
10948     // private
10949     this.paramNames = {
10950         "start" : "start",
10951         "limit" : "limit",
10952         "sort" : "sort",
10953         "dir" : "dir",
10954         "multisort" : "_multisort"
10955     };
10956
10957     if(config && config.data){
10958         this.inlineData = config.data;
10959         delete config.data;
10960     }
10961
10962     Roo.apply(this, config);
10963     
10964     if(this.reader){ // reader passed
10965         this.reader = Roo.factory(this.reader, Roo.data);
10966         this.reader.xmodule = this.xmodule || false;
10967         if(!this.recordType){
10968             this.recordType = this.reader.recordType;
10969         }
10970         if(this.reader.onMetaChange){
10971             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10972         }
10973     }
10974
10975     if(this.recordType){
10976         this.fields = this.recordType.prototype.fields;
10977     }
10978     this.modified = [];
10979
10980     this.addEvents({
10981         /**
10982          * @event datachanged
10983          * Fires when the data cache has changed, and a widget which is using this Store
10984          * as a Record cache should refresh its view.
10985          * @param {Store} this
10986          */
10987         datachanged : true,
10988         /**
10989          * @event metachange
10990          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10991          * @param {Store} this
10992          * @param {Object} meta The JSON metadata
10993          */
10994         metachange : true,
10995         /**
10996          * @event add
10997          * Fires when Records have been added to the Store
10998          * @param {Store} this
10999          * @param {Roo.data.Record[]} records The array of Records added
11000          * @param {Number} index The index at which the record(s) were added
11001          */
11002         add : true,
11003         /**
11004          * @event remove
11005          * Fires when a Record has been removed from the Store
11006          * @param {Store} this
11007          * @param {Roo.data.Record} record The Record that was removed
11008          * @param {Number} index The index at which the record was removed
11009          */
11010         remove : true,
11011         /**
11012          * @event update
11013          * Fires when a Record has been updated
11014          * @param {Store} this
11015          * @param {Roo.data.Record} record The Record that was updated
11016          * @param {String} operation The update operation being performed.  Value may be one of:
11017          * <pre><code>
11018  Roo.data.Record.EDIT
11019  Roo.data.Record.REJECT
11020  Roo.data.Record.COMMIT
11021          * </code></pre>
11022          */
11023         update : true,
11024         /**
11025          * @event clear
11026          * Fires when the data cache has been cleared.
11027          * @param {Store} this
11028          */
11029         clear : true,
11030         /**
11031          * @event beforeload
11032          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11033          * the load action will be canceled.
11034          * @param {Store} this
11035          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11036          */
11037         beforeload : true,
11038         /**
11039          * @event beforeloadadd
11040          * Fires after a new set of Records has been loaded.
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          */
11045         beforeloadadd : true,
11046         /**
11047          * @event load
11048          * Fires after a new set of Records has been loaded, before they are added to the store.
11049          * @param {Store} this
11050          * @param {Roo.data.Record[]} records The Records that were loaded
11051          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11052          * @params {Object} return from reader
11053          */
11054         load : true,
11055         /**
11056          * @event loadexception
11057          * Fires if an exception occurs in the Proxy during loading.
11058          * Called with the signature of the Proxy's "loadexception" event.
11059          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11060          * 
11061          * @param {Proxy} 
11062          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11063          * @param {Object} load options 
11064          * @param {Object} jsonData from your request (normally this contains the Exception)
11065          */
11066         loadexception : true
11067     });
11068     
11069     if(this.proxy){
11070         this.proxy = Roo.factory(this.proxy, Roo.data);
11071         this.proxy.xmodule = this.xmodule || false;
11072         this.relayEvents(this.proxy,  ["loadexception"]);
11073     }
11074     this.sortToggle = {};
11075     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11076
11077     Roo.data.Store.superclass.constructor.call(this);
11078
11079     if(this.inlineData){
11080         this.loadData(this.inlineData);
11081         delete this.inlineData;
11082     }
11083 };
11084
11085 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11086      /**
11087     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11088     * without a remote query - used by combo/forms at present.
11089     */
11090     
11091     /**
11092     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11093     */
11094     /**
11095     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11096     */
11097     /**
11098     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11099     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11100     */
11101     /**
11102     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11103     * on any HTTP request
11104     */
11105     /**
11106     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11107     */
11108     /**
11109     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11110     */
11111     multiSort: false,
11112     /**
11113     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11114     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11115     */
11116     remoteSort : false,
11117
11118     /**
11119     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11120      * loaded or when a record is removed. (defaults to false).
11121     */
11122     pruneModifiedRecords : false,
11123
11124     // private
11125     lastOptions : null,
11126
11127     /**
11128      * Add Records to the Store and fires the add event.
11129      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11130      */
11131     add : function(records){
11132         records = [].concat(records);
11133         for(var i = 0, len = records.length; i < len; i++){
11134             records[i].join(this);
11135         }
11136         var index = this.data.length;
11137         this.data.addAll(records);
11138         this.fireEvent("add", this, records, index);
11139     },
11140
11141     /**
11142      * Remove a Record from the Store and fires the remove event.
11143      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11144      */
11145     remove : function(record){
11146         var index = this.data.indexOf(record);
11147         this.data.removeAt(index);
11148  
11149         if(this.pruneModifiedRecords){
11150             this.modified.remove(record);
11151         }
11152         this.fireEvent("remove", this, record, index);
11153     },
11154
11155     /**
11156      * Remove all Records from the Store and fires the clear event.
11157      */
11158     removeAll : function(){
11159         this.data.clear();
11160         if(this.pruneModifiedRecords){
11161             this.modified = [];
11162         }
11163         this.fireEvent("clear", this);
11164     },
11165
11166     /**
11167      * Inserts Records to the Store at the given index and fires the add event.
11168      * @param {Number} index The start index at which to insert the passed Records.
11169      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11170      */
11171     insert : function(index, records){
11172         records = [].concat(records);
11173         for(var i = 0, len = records.length; i < len; i++){
11174             this.data.insert(index, records[i]);
11175             records[i].join(this);
11176         }
11177         this.fireEvent("add", this, records, index);
11178     },
11179
11180     /**
11181      * Get the index within the cache of the passed Record.
11182      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11183      * @return {Number} The index of the passed Record. Returns -1 if not found.
11184      */
11185     indexOf : function(record){
11186         return this.data.indexOf(record);
11187     },
11188
11189     /**
11190      * Get the index within the cache of the Record with the passed id.
11191      * @param {String} id The id of the Record to find.
11192      * @return {Number} The index of the Record. Returns -1 if not found.
11193      */
11194     indexOfId : function(id){
11195         return this.data.indexOfKey(id);
11196     },
11197
11198     /**
11199      * Get the Record with the specified id.
11200      * @param {String} id The id of the Record to find.
11201      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11202      */
11203     getById : function(id){
11204         return this.data.key(id);
11205     },
11206
11207     /**
11208      * Get the Record at the specified index.
11209      * @param {Number} index The index of the Record to find.
11210      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11211      */
11212     getAt : function(index){
11213         return this.data.itemAt(index);
11214     },
11215
11216     /**
11217      * Returns a range of Records between specified indices.
11218      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11219      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11220      * @return {Roo.data.Record[]} An array of Records
11221      */
11222     getRange : function(start, end){
11223         return this.data.getRange(start, end);
11224     },
11225
11226     // private
11227     storeOptions : function(o){
11228         o = Roo.apply({}, o);
11229         delete o.callback;
11230         delete o.scope;
11231         this.lastOptions = o;
11232     },
11233
11234     /**
11235      * Loads the Record cache from the configured Proxy using the configured Reader.
11236      * <p>
11237      * If using remote paging, then the first load call must specify the <em>start</em>
11238      * and <em>limit</em> properties in the options.params property to establish the initial
11239      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11240      * <p>
11241      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11242      * and this call will return before the new data has been loaded. Perform any post-processing
11243      * in a callback function, or in a "load" event handler.</strong>
11244      * <p>
11245      * @param {Object} options An object containing properties which control loading options:<ul>
11246      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11247      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11248      * passed the following arguments:<ul>
11249      * <li>r : Roo.data.Record[]</li>
11250      * <li>options: Options object from the load call</li>
11251      * <li>success: Boolean success indicator</li></ul></li>
11252      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11253      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11254      * </ul>
11255      */
11256     load : function(options){
11257         options = options || {};
11258         if(this.fireEvent("beforeload", this, options) !== false){
11259             this.storeOptions(options);
11260             var p = Roo.apply(options.params || {}, this.baseParams);
11261             // if meta was not loaded from remote source.. try requesting it.
11262             if (!this.reader.metaFromRemote) {
11263                 p._requestMeta = 1;
11264             }
11265             if(this.sortInfo && this.remoteSort){
11266                 var pn = this.paramNames;
11267                 p[pn["sort"]] = this.sortInfo.field;
11268                 p[pn["dir"]] = this.sortInfo.direction;
11269             }
11270             if (this.multiSort) {
11271                 var pn = this.paramNames;
11272                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11273             }
11274             
11275             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11276         }
11277     },
11278
11279     /**
11280      * Reloads the Record cache from the configured Proxy using the configured Reader and
11281      * the options from the last load operation performed.
11282      * @param {Object} options (optional) An object containing properties which may override the options
11283      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11284      * the most recently used options are reused).
11285      */
11286     reload : function(options){
11287         this.load(Roo.applyIf(options||{}, this.lastOptions));
11288     },
11289
11290     // private
11291     // Called as a callback by the Reader during a load operation.
11292     loadRecords : function(o, options, success){
11293         if(!o || success === false){
11294             if(success !== false){
11295                 this.fireEvent("load", this, [], options, o);
11296             }
11297             if(options.callback){
11298                 options.callback.call(options.scope || this, [], options, false);
11299             }
11300             return;
11301         }
11302         // if data returned failure - throw an exception.
11303         if (o.success === false) {
11304             // show a message if no listener is registered.
11305             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11306                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11307             }
11308             // loadmask wil be hooked into this..
11309             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11310             return;
11311         }
11312         var r = o.records, t = o.totalRecords || r.length;
11313         
11314         this.fireEvent("beforeloadadd", this, r, options, o);
11315         
11316         if(!options || options.add !== true){
11317             if(this.pruneModifiedRecords){
11318                 this.modified = [];
11319             }
11320             for(var i = 0, len = r.length; i < len; i++){
11321                 r[i].join(this);
11322             }
11323             if(this.snapshot){
11324                 this.data = this.snapshot;
11325                 delete this.snapshot;
11326             }
11327             this.data.clear();
11328             this.data.addAll(r);
11329             this.totalLength = t;
11330             this.applySort();
11331             this.fireEvent("datachanged", this);
11332         }else{
11333             this.totalLength = Math.max(t, this.data.length+r.length);
11334             this.add(r);
11335         }
11336         
11337         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11338                 
11339             var e = new Roo.data.Record({});
11340
11341             e.set(this.parent.displayField, this.parent.emptyTitle);
11342             e.set(this.parent.valueField, '');
11343
11344             this.insert(0, e);
11345         }
11346             
11347         this.fireEvent("load", this, r, options, o);
11348         if(options.callback){
11349             options.callback.call(options.scope || this, r, options, true);
11350         }
11351     },
11352
11353
11354     /**
11355      * Loads data from a passed data block. A Reader which understands the format of the data
11356      * must have been configured in the constructor.
11357      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11358      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11359      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11360      */
11361     loadData : function(o, append){
11362         var r = this.reader.readRecords(o);
11363         this.loadRecords(r, {add: append}, true);
11364     },
11365
11366     /**
11367      * Gets the number of cached records.
11368      * <p>
11369      * <em>If using paging, this may not be the total size of the dataset. If the data object
11370      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11371      * the data set size</em>
11372      */
11373     getCount : function(){
11374         return this.data.length || 0;
11375     },
11376
11377     /**
11378      * Gets the total number of records in the dataset as returned by the server.
11379      * <p>
11380      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11381      * the dataset size</em>
11382      */
11383     getTotalCount : function(){
11384         return this.totalLength || 0;
11385     },
11386
11387     /**
11388      * Returns the sort state of the Store as an object with two properties:
11389      * <pre><code>
11390  field {String} The name of the field by which the Records are sorted
11391  direction {String} The sort order, "ASC" or "DESC"
11392      * </code></pre>
11393      */
11394     getSortState : function(){
11395         return this.sortInfo;
11396     },
11397
11398     // private
11399     applySort : function(){
11400         if(this.sortInfo && !this.remoteSort){
11401             var s = this.sortInfo, f = s.field;
11402             var st = this.fields.get(f).sortType;
11403             var fn = function(r1, r2){
11404                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11405                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11406             };
11407             this.data.sort(s.direction, fn);
11408             if(this.snapshot && this.snapshot != this.data){
11409                 this.snapshot.sort(s.direction, fn);
11410             }
11411         }
11412     },
11413
11414     /**
11415      * Sets the default sort column and order to be used by the next load operation.
11416      * @param {String} fieldName The name of the field to sort by.
11417      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11418      */
11419     setDefaultSort : function(field, dir){
11420         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11421     },
11422
11423     /**
11424      * Sort the Records.
11425      * If remote sorting is used, the sort is performed on the server, and the cache is
11426      * reloaded. If local sorting is used, the cache is sorted internally.
11427      * @param {String} fieldName The name of the field to sort by.
11428      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11429      */
11430     sort : function(fieldName, dir){
11431         var f = this.fields.get(fieldName);
11432         if(!dir){
11433             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11434             
11435             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11436                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11437             }else{
11438                 dir = f.sortDir;
11439             }
11440         }
11441         this.sortToggle[f.name] = dir;
11442         this.sortInfo = {field: f.name, direction: dir};
11443         if(!this.remoteSort){
11444             this.applySort();
11445             this.fireEvent("datachanged", this);
11446         }else{
11447             this.load(this.lastOptions);
11448         }
11449     },
11450
11451     /**
11452      * Calls the specified function for each of the Records in the cache.
11453      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11454      * Returning <em>false</em> aborts and exits the iteration.
11455      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11456      */
11457     each : function(fn, scope){
11458         this.data.each(fn, scope);
11459     },
11460
11461     /**
11462      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11463      * (e.g., during paging).
11464      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11465      */
11466     getModifiedRecords : function(){
11467         return this.modified;
11468     },
11469
11470     // private
11471     createFilterFn : function(property, value, anyMatch){
11472         if(!value.exec){ // not a regex
11473             value = String(value);
11474             if(value.length == 0){
11475                 return false;
11476             }
11477             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11478         }
11479         return function(r){
11480             return value.test(r.data[property]);
11481         };
11482     },
11483
11484     /**
11485      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11486      * @param {String} property A field on your records
11487      * @param {Number} start The record index to start at (defaults to 0)
11488      * @param {Number} end The last record index to include (defaults to length - 1)
11489      * @return {Number} The sum
11490      */
11491     sum : function(property, start, end){
11492         var rs = this.data.items, v = 0;
11493         start = start || 0;
11494         end = (end || end === 0) ? end : rs.length-1;
11495
11496         for(var i = start; i <= end; i++){
11497             v += (rs[i].data[property] || 0);
11498         }
11499         return v;
11500     },
11501
11502     /**
11503      * Filter the records by a specified property.
11504      * @param {String} field A field on your records
11505      * @param {String/RegExp} value Either a string that the field
11506      * should start with or a RegExp to test against the field
11507      * @param {Boolean} anyMatch True to match any part not just the beginning
11508      */
11509     filter : function(property, value, anyMatch){
11510         var fn = this.createFilterFn(property, value, anyMatch);
11511         return fn ? this.filterBy(fn) : this.clearFilter();
11512     },
11513
11514     /**
11515      * Filter by a function. The specified function will be called with each
11516      * record in this data source. If the function returns true the record is included,
11517      * otherwise it is filtered.
11518      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11519      * @param {Object} scope (optional) The scope of the function (defaults to this)
11520      */
11521     filterBy : function(fn, scope){
11522         this.snapshot = this.snapshot || this.data;
11523         this.data = this.queryBy(fn, scope||this);
11524         this.fireEvent("datachanged", this);
11525     },
11526
11527     /**
11528      * Query the records by a specified property.
11529      * @param {String} field A field on your records
11530      * @param {String/RegExp} value Either a string that the field
11531      * should start with or a RegExp to test against the field
11532      * @param {Boolean} anyMatch True to match any part not just the beginning
11533      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11534      */
11535     query : function(property, value, anyMatch){
11536         var fn = this.createFilterFn(property, value, anyMatch);
11537         return fn ? this.queryBy(fn) : this.data.clone();
11538     },
11539
11540     /**
11541      * Query by a function. The specified function will be called with each
11542      * record in this data source. If the function returns true the record is included
11543      * in the results.
11544      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11545      * @param {Object} scope (optional) The scope of the function (defaults to this)
11546       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11547      **/
11548     queryBy : function(fn, scope){
11549         var data = this.snapshot || this.data;
11550         return data.filterBy(fn, scope||this);
11551     },
11552
11553     /**
11554      * Collects unique values for a particular dataIndex from this store.
11555      * @param {String} dataIndex The property to collect
11556      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11557      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11558      * @return {Array} An array of the unique values
11559      **/
11560     collect : function(dataIndex, allowNull, bypassFilter){
11561         var d = (bypassFilter === true && this.snapshot) ?
11562                 this.snapshot.items : this.data.items;
11563         var v, sv, r = [], l = {};
11564         for(var i = 0, len = d.length; i < len; i++){
11565             v = d[i].data[dataIndex];
11566             sv = String(v);
11567             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11568                 l[sv] = true;
11569                 r[r.length] = v;
11570             }
11571         }
11572         return r;
11573     },
11574
11575     /**
11576      * Revert to a view of the Record cache with no filtering applied.
11577      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11578      */
11579     clearFilter : function(suppressEvent){
11580         if(this.snapshot && this.snapshot != this.data){
11581             this.data = this.snapshot;
11582             delete this.snapshot;
11583             if(suppressEvent !== true){
11584                 this.fireEvent("datachanged", this);
11585             }
11586         }
11587     },
11588
11589     // private
11590     afterEdit : function(record){
11591         if(this.modified.indexOf(record) == -1){
11592             this.modified.push(record);
11593         }
11594         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11595     },
11596     
11597     // private
11598     afterReject : function(record){
11599         this.modified.remove(record);
11600         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11601     },
11602
11603     // private
11604     afterCommit : function(record){
11605         this.modified.remove(record);
11606         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11607     },
11608
11609     /**
11610      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11611      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11612      */
11613     commitChanges : function(){
11614         var m = this.modified.slice(0);
11615         this.modified = [];
11616         for(var i = 0, len = m.length; i < len; i++){
11617             m[i].commit();
11618         }
11619     },
11620
11621     /**
11622      * Cancel outstanding changes on all changed records.
11623      */
11624     rejectChanges : function(){
11625         var m = this.modified.slice(0);
11626         this.modified = [];
11627         for(var i = 0, len = m.length; i < len; i++){
11628             m[i].reject();
11629         }
11630     },
11631
11632     onMetaChange : function(meta, rtype, o){
11633         this.recordType = rtype;
11634         this.fields = rtype.prototype.fields;
11635         delete this.snapshot;
11636         this.sortInfo = meta.sortInfo || this.sortInfo;
11637         this.modified = [];
11638         this.fireEvent('metachange', this, this.reader.meta);
11639     },
11640     
11641     moveIndex : function(data, type)
11642     {
11643         var index = this.indexOf(data);
11644         
11645         var newIndex = index + type;
11646         
11647         this.remove(data);
11648         
11649         this.insert(newIndex, data);
11650         
11651     }
11652 });/*
11653  * Based on:
11654  * Ext JS Library 1.1.1
11655  * Copyright(c) 2006-2007, Ext JS, LLC.
11656  *
11657  * Originally Released Under LGPL - original licence link has changed is not relivant.
11658  *
11659  * Fork - LGPL
11660  * <script type="text/javascript">
11661  */
11662
11663 /**
11664  * @class Roo.data.SimpleStore
11665  * @extends Roo.data.Store
11666  * Small helper class to make creating Stores from Array data easier.
11667  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11668  * @cfg {Array} fields An array of field definition objects, or field name strings.
11669  * @cfg {Array} data The multi-dimensional array of data
11670  * @constructor
11671  * @param {Object} config
11672  */
11673 Roo.data.SimpleStore = function(config){
11674     Roo.data.SimpleStore.superclass.constructor.call(this, {
11675         isLocal : true,
11676         reader: new Roo.data.ArrayReader({
11677                 id: config.id
11678             },
11679             Roo.data.Record.create(config.fields)
11680         ),
11681         proxy : new Roo.data.MemoryProxy(config.data)
11682     });
11683     this.load();
11684 };
11685 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11686  * Based on:
11687  * Ext JS Library 1.1.1
11688  * Copyright(c) 2006-2007, Ext JS, LLC.
11689  *
11690  * Originally Released Under LGPL - original licence link has changed is not relivant.
11691  *
11692  * Fork - LGPL
11693  * <script type="text/javascript">
11694  */
11695
11696 /**
11697 /**
11698  * @extends Roo.data.Store
11699  * @class Roo.data.JsonStore
11700  * Small helper class to make creating Stores for JSON data easier. <br/>
11701 <pre><code>
11702 var store = new Roo.data.JsonStore({
11703     url: 'get-images.php',
11704     root: 'images',
11705     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11706 });
11707 </code></pre>
11708  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11709  * JsonReader and HttpProxy (unless inline data is provided).</b>
11710  * @cfg {Array} fields An array of field definition objects, or field name strings.
11711  * @constructor
11712  * @param {Object} config
11713  */
11714 Roo.data.JsonStore = function(c){
11715     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11716         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11717         reader: new Roo.data.JsonReader(c, c.fields)
11718     }));
11719 };
11720 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11721  * Based on:
11722  * Ext JS Library 1.1.1
11723  * Copyright(c) 2006-2007, Ext JS, LLC.
11724  *
11725  * Originally Released Under LGPL - original licence link has changed is not relivant.
11726  *
11727  * Fork - LGPL
11728  * <script type="text/javascript">
11729  */
11730
11731  
11732 Roo.data.Field = function(config){
11733     if(typeof config == "string"){
11734         config = {name: config};
11735     }
11736     Roo.apply(this, config);
11737     
11738     if(!this.type){
11739         this.type = "auto";
11740     }
11741     
11742     var st = Roo.data.SortTypes;
11743     // named sortTypes are supported, here we look them up
11744     if(typeof this.sortType == "string"){
11745         this.sortType = st[this.sortType];
11746     }
11747     
11748     // set default sortType for strings and dates
11749     if(!this.sortType){
11750         switch(this.type){
11751             case "string":
11752                 this.sortType = st.asUCString;
11753                 break;
11754             case "date":
11755                 this.sortType = st.asDate;
11756                 break;
11757             default:
11758                 this.sortType = st.none;
11759         }
11760     }
11761
11762     // define once
11763     var stripRe = /[\$,%]/g;
11764
11765     // prebuilt conversion function for this field, instead of
11766     // switching every time we're reading a value
11767     if(!this.convert){
11768         var cv, dateFormat = this.dateFormat;
11769         switch(this.type){
11770             case "":
11771             case "auto":
11772             case undefined:
11773                 cv = function(v){ return v; };
11774                 break;
11775             case "string":
11776                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11777                 break;
11778             case "int":
11779                 cv = function(v){
11780                     return v !== undefined && v !== null && v !== '' ?
11781                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11782                     };
11783                 break;
11784             case "float":
11785                 cv = function(v){
11786                     return v !== undefined && v !== null && v !== '' ?
11787                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11788                     };
11789                 break;
11790             case "bool":
11791             case "boolean":
11792                 cv = function(v){ return v === true || v === "true" || v == 1; };
11793                 break;
11794             case "date":
11795                 cv = function(v){
11796                     if(!v){
11797                         return '';
11798                     }
11799                     if(v instanceof Date){
11800                         return v;
11801                     }
11802                     if(dateFormat){
11803                         if(dateFormat == "timestamp"){
11804                             return new Date(v*1000);
11805                         }
11806                         return Date.parseDate(v, dateFormat);
11807                     }
11808                     var parsed = Date.parse(v);
11809                     return parsed ? new Date(parsed) : null;
11810                 };
11811              break;
11812             
11813         }
11814         this.convert = cv;
11815     }
11816 };
11817
11818 Roo.data.Field.prototype = {
11819     dateFormat: null,
11820     defaultValue: "",
11821     mapping: null,
11822     sortType : null,
11823     sortDir : "ASC"
11824 };/*
11825  * Based on:
11826  * Ext JS Library 1.1.1
11827  * Copyright(c) 2006-2007, Ext JS, LLC.
11828  *
11829  * Originally Released Under LGPL - original licence link has changed is not relivant.
11830  *
11831  * Fork - LGPL
11832  * <script type="text/javascript">
11833  */
11834  
11835 // Base class for reading structured data from a data source.  This class is intended to be
11836 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11837
11838 /**
11839  * @class Roo.data.DataReader
11840  * Base class for reading structured data from a data source.  This class is intended to be
11841  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11842  */
11843
11844 Roo.data.DataReader = function(meta, recordType){
11845     
11846     this.meta = meta;
11847     
11848     this.recordType = recordType instanceof Array ? 
11849         Roo.data.Record.create(recordType) : recordType;
11850 };
11851
11852 Roo.data.DataReader.prototype = {
11853      /**
11854      * Create an empty record
11855      * @param {Object} data (optional) - overlay some values
11856      * @return {Roo.data.Record} record created.
11857      */
11858     newRow :  function(d) {
11859         var da =  {};
11860         this.recordType.prototype.fields.each(function(c) {
11861             switch( c.type) {
11862                 case 'int' : da[c.name] = 0; break;
11863                 case 'date' : da[c.name] = new Date(); break;
11864                 case 'float' : da[c.name] = 0.0; break;
11865                 case 'boolean' : da[c.name] = false; break;
11866                 default : da[c.name] = ""; break;
11867             }
11868             
11869         });
11870         return new this.recordType(Roo.apply(da, d));
11871     }
11872     
11873 };/*
11874  * Based on:
11875  * Ext JS Library 1.1.1
11876  * Copyright(c) 2006-2007, Ext JS, LLC.
11877  *
11878  * Originally Released Under LGPL - original licence link has changed is not relivant.
11879  *
11880  * Fork - LGPL
11881  * <script type="text/javascript">
11882  */
11883
11884 /**
11885  * @class Roo.data.DataProxy
11886  * @extends Roo.data.Observable
11887  * This class is an abstract base class for implementations which provide retrieval of
11888  * unformatted data objects.<br>
11889  * <p>
11890  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11891  * (of the appropriate type which knows how to parse the data object) to provide a block of
11892  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11893  * <p>
11894  * Custom implementations must implement the load method as described in
11895  * {@link Roo.data.HttpProxy#load}.
11896  */
11897 Roo.data.DataProxy = function(){
11898     this.addEvents({
11899         /**
11900          * @event beforeload
11901          * Fires before a network request is made to retrieve a data object.
11902          * @param {Object} This DataProxy object.
11903          * @param {Object} params The params parameter to the load function.
11904          */
11905         beforeload : true,
11906         /**
11907          * @event load
11908          * Fires before the load method's callback is called.
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          */
11913         load : true,
11914         /**
11915          * @event loadexception
11916          * Fires if an Exception occurs during data retrieval.
11917          * @param {Object} This DataProxy object.
11918          * @param {Object} o The data object.
11919          * @param {Object} arg The callback argument object passed to the load function.
11920          * @param {Object} e The Exception.
11921          */
11922         loadexception : true
11923     });
11924     Roo.data.DataProxy.superclass.constructor.call(this);
11925 };
11926
11927 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11928
11929     /**
11930      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11931      */
11932 /*
11933  * Based on:
11934  * Ext JS Library 1.1.1
11935  * Copyright(c) 2006-2007, Ext JS, LLC.
11936  *
11937  * Originally Released Under LGPL - original licence link has changed is not relivant.
11938  *
11939  * Fork - LGPL
11940  * <script type="text/javascript">
11941  */
11942 /**
11943  * @class Roo.data.MemoryProxy
11944  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11945  * to the Reader when its load method is called.
11946  * @constructor
11947  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11948  */
11949 Roo.data.MemoryProxy = function(data){
11950     if (data.data) {
11951         data = data.data;
11952     }
11953     Roo.data.MemoryProxy.superclass.constructor.call(this);
11954     this.data = data;
11955 };
11956
11957 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11958     
11959     /**
11960      * Load data from the requested source (in this case an in-memory
11961      * data object passed to the constructor), read the data object into
11962      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11963      * process that block using the passed callback.
11964      * @param {Object} params This parameter is not used by the MemoryProxy class.
11965      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11966      * object into a block of Roo.data.Records.
11967      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11968      * The function must be passed <ul>
11969      * <li>The Record block object</li>
11970      * <li>The "arg" argument from the load function</li>
11971      * <li>A boolean success indicator</li>
11972      * </ul>
11973      * @param {Object} scope The scope in which to call the callback
11974      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11975      */
11976     load : function(params, reader, callback, scope, arg){
11977         params = params || {};
11978         var result;
11979         try {
11980             result = reader.readRecords(this.data);
11981         }catch(e){
11982             this.fireEvent("loadexception", this, arg, null, e);
11983             callback.call(scope, null, arg, false);
11984             return;
11985         }
11986         callback.call(scope, result, arg, true);
11987     },
11988     
11989     // private
11990     update : function(params, records){
11991         
11992     }
11993 });/*
11994  * Based on:
11995  * Ext JS Library 1.1.1
11996  * Copyright(c) 2006-2007, Ext JS, LLC.
11997  *
11998  * Originally Released Under LGPL - original licence link has changed is not relivant.
11999  *
12000  * Fork - LGPL
12001  * <script type="text/javascript">
12002  */
12003 /**
12004  * @class Roo.data.HttpProxy
12005  * @extends Roo.data.DataProxy
12006  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12007  * configured to reference a certain URL.<br><br>
12008  * <p>
12009  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12010  * from which the running page was served.<br><br>
12011  * <p>
12012  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12013  * <p>
12014  * Be aware that to enable the browser to parse an XML document, the server must set
12015  * the Content-Type header in the HTTP response to "text/xml".
12016  * @constructor
12017  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12018  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12019  * will be used to make the request.
12020  */
12021 Roo.data.HttpProxy = function(conn){
12022     Roo.data.HttpProxy.superclass.constructor.call(this);
12023     // is conn a conn config or a real conn?
12024     this.conn = conn;
12025     this.useAjax = !conn || !conn.events;
12026   
12027 };
12028
12029 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12030     // thse are take from connection...
12031     
12032     /**
12033      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12034      */
12035     /**
12036      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12037      * extra parameters to each request made by this object. (defaults to undefined)
12038      */
12039     /**
12040      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12041      *  to each request made by this object. (defaults to undefined)
12042      */
12043     /**
12044      * @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)
12045      */
12046     /**
12047      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12048      */
12049      /**
12050      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12051      * @type Boolean
12052      */
12053   
12054
12055     /**
12056      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12057      * @type Boolean
12058      */
12059     /**
12060      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12061      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12062      * a finer-grained basis than the DataProxy events.
12063      */
12064     getConnection : function(){
12065         return this.useAjax ? Roo.Ajax : this.conn;
12066     },
12067
12068     /**
12069      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12070      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12071      * process that block using the passed callback.
12072      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12073      * for the request to the remote server.
12074      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12075      * object into a block of Roo.data.Records.
12076      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12077      * The function must be passed <ul>
12078      * <li>The Record block object</li>
12079      * <li>The "arg" argument from the load function</li>
12080      * <li>A boolean success indicator</li>
12081      * </ul>
12082      * @param {Object} scope The scope in which to call the callback
12083      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12084      */
12085     load : function(params, reader, callback, scope, arg){
12086         if(this.fireEvent("beforeload", this, params) !== false){
12087             var  o = {
12088                 params : params || {},
12089                 request: {
12090                     callback : callback,
12091                     scope : scope,
12092                     arg : arg
12093                 },
12094                 reader: reader,
12095                 callback : this.loadResponse,
12096                 scope: this
12097             };
12098             if(this.useAjax){
12099                 Roo.applyIf(o, this.conn);
12100                 if(this.activeRequest){
12101                     Roo.Ajax.abort(this.activeRequest);
12102                 }
12103                 this.activeRequest = Roo.Ajax.request(o);
12104             }else{
12105                 this.conn.request(o);
12106             }
12107         }else{
12108             callback.call(scope||this, null, arg, false);
12109         }
12110     },
12111
12112     // private
12113     loadResponse : function(o, success, response){
12114         delete this.activeRequest;
12115         if(!success){
12116             this.fireEvent("loadexception", this, o, response);
12117             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12118             return;
12119         }
12120         var result;
12121         try {
12122             result = o.reader.read(response);
12123         }catch(e){
12124             this.fireEvent("loadexception", this, o, response, e);
12125             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12126             return;
12127         }
12128         
12129         this.fireEvent("load", this, o, o.request.arg);
12130         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12131     },
12132
12133     // private
12134     update : function(dataSet){
12135
12136     },
12137
12138     // private
12139     updateResponse : function(dataSet){
12140
12141     }
12142 });/*
12143  * Based on:
12144  * Ext JS Library 1.1.1
12145  * Copyright(c) 2006-2007, Ext JS, LLC.
12146  *
12147  * Originally Released Under LGPL - original licence link has changed is not relivant.
12148  *
12149  * Fork - LGPL
12150  * <script type="text/javascript">
12151  */
12152
12153 /**
12154  * @class Roo.data.ScriptTagProxy
12155  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12156  * other than the originating domain of the running page.<br><br>
12157  * <p>
12158  * <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
12159  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12160  * <p>
12161  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12162  * source code that is used as the source inside a &lt;script> tag.<br><br>
12163  * <p>
12164  * In order for the browser to process the returned data, the server must wrap the data object
12165  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12166  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12167  * depending on whether the callback name was passed:
12168  * <p>
12169  * <pre><code>
12170 boolean scriptTag = false;
12171 String cb = request.getParameter("callback");
12172 if (cb != null) {
12173     scriptTag = true;
12174     response.setContentType("text/javascript");
12175 } else {
12176     response.setContentType("application/x-json");
12177 }
12178 Writer out = response.getWriter();
12179 if (scriptTag) {
12180     out.write(cb + "(");
12181 }
12182 out.print(dataBlock.toJsonString());
12183 if (scriptTag) {
12184     out.write(");");
12185 }
12186 </pre></code>
12187  *
12188  * @constructor
12189  * @param {Object} config A configuration object.
12190  */
12191 Roo.data.ScriptTagProxy = function(config){
12192     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12193     Roo.apply(this, config);
12194     this.head = document.getElementsByTagName("head")[0];
12195 };
12196
12197 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12198
12199 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12200     /**
12201      * @cfg {String} url The URL from which to request the data object.
12202      */
12203     /**
12204      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12205      */
12206     timeout : 30000,
12207     /**
12208      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12209      * the server the name of the callback function set up by the load call to process the returned data object.
12210      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12211      * javascript output which calls this named function passing the data object as its only parameter.
12212      */
12213     callbackParam : "callback",
12214     /**
12215      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12216      * name to the request.
12217      */
12218     nocache : true,
12219
12220     /**
12221      * Load data from the configured URL, read the data object into
12222      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12223      * process that block using the passed callback.
12224      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12225      * for the request to the remote server.
12226      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12227      * object into a block of Roo.data.Records.
12228      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12229      * The function must be passed <ul>
12230      * <li>The Record block object</li>
12231      * <li>The "arg" argument from the load function</li>
12232      * <li>A boolean success indicator</li>
12233      * </ul>
12234      * @param {Object} scope The scope in which to call the callback
12235      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12236      */
12237     load : function(params, reader, callback, scope, arg){
12238         if(this.fireEvent("beforeload", this, params) !== false){
12239
12240             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12241
12242             var url = this.url;
12243             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12244             if(this.nocache){
12245                 url += "&_dc=" + (new Date().getTime());
12246             }
12247             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12248             var trans = {
12249                 id : transId,
12250                 cb : "stcCallback"+transId,
12251                 scriptId : "stcScript"+transId,
12252                 params : params,
12253                 arg : arg,
12254                 url : url,
12255                 callback : callback,
12256                 scope : scope,
12257                 reader : reader
12258             };
12259             var conn = this;
12260
12261             window[trans.cb] = function(o){
12262                 conn.handleResponse(o, trans);
12263             };
12264
12265             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12266
12267             if(this.autoAbort !== false){
12268                 this.abort();
12269             }
12270
12271             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12272
12273             var script = document.createElement("script");
12274             script.setAttribute("src", url);
12275             script.setAttribute("type", "text/javascript");
12276             script.setAttribute("id", trans.scriptId);
12277             this.head.appendChild(script);
12278
12279             this.trans = trans;
12280         }else{
12281             callback.call(scope||this, null, arg, false);
12282         }
12283     },
12284
12285     // private
12286     isLoading : function(){
12287         return this.trans ? true : false;
12288     },
12289
12290     /**
12291      * Abort the current server request.
12292      */
12293     abort : function(){
12294         if(this.isLoading()){
12295             this.destroyTrans(this.trans);
12296         }
12297     },
12298
12299     // private
12300     destroyTrans : function(trans, isLoaded){
12301         this.head.removeChild(document.getElementById(trans.scriptId));
12302         clearTimeout(trans.timeoutId);
12303         if(isLoaded){
12304             window[trans.cb] = undefined;
12305             try{
12306                 delete window[trans.cb];
12307             }catch(e){}
12308         }else{
12309             // if hasn't been loaded, wait for load to remove it to prevent script error
12310             window[trans.cb] = function(){
12311                 window[trans.cb] = undefined;
12312                 try{
12313                     delete window[trans.cb];
12314                 }catch(e){}
12315             };
12316         }
12317     },
12318
12319     // private
12320     handleResponse : function(o, trans){
12321         this.trans = false;
12322         this.destroyTrans(trans, true);
12323         var result;
12324         try {
12325             result = trans.reader.readRecords(o);
12326         }catch(e){
12327             this.fireEvent("loadexception", this, o, trans.arg, e);
12328             trans.callback.call(trans.scope||window, null, trans.arg, false);
12329             return;
12330         }
12331         this.fireEvent("load", this, o, trans.arg);
12332         trans.callback.call(trans.scope||window, result, trans.arg, true);
12333     },
12334
12335     // private
12336     handleFailure : function(trans){
12337         this.trans = false;
12338         this.destroyTrans(trans, false);
12339         this.fireEvent("loadexception", this, null, trans.arg);
12340         trans.callback.call(trans.scope||window, null, trans.arg, false);
12341     }
12342 });/*
12343  * Based on:
12344  * Ext JS Library 1.1.1
12345  * Copyright(c) 2006-2007, Ext JS, LLC.
12346  *
12347  * Originally Released Under LGPL - original licence link has changed is not relivant.
12348  *
12349  * Fork - LGPL
12350  * <script type="text/javascript">
12351  */
12352
12353 /**
12354  * @class Roo.data.JsonReader
12355  * @extends Roo.data.DataReader
12356  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12357  * based on mappings in a provided Roo.data.Record constructor.
12358  * 
12359  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12360  * in the reply previously. 
12361  * 
12362  * <p>
12363  * Example code:
12364  * <pre><code>
12365 var RecordDef = Roo.data.Record.create([
12366     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12367     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12368 ]);
12369 var myReader = new Roo.data.JsonReader({
12370     totalProperty: "results",    // The property which contains the total dataset size (optional)
12371     root: "rows",                // The property which contains an Array of row objects
12372     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12373 }, RecordDef);
12374 </code></pre>
12375  * <p>
12376  * This would consume a JSON file like this:
12377  * <pre><code>
12378 { 'results': 2, 'rows': [
12379     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12380     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12381 }
12382 </code></pre>
12383  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12384  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12385  * paged from the remote server.
12386  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12387  * @cfg {String} root name of the property which contains the Array of row objects.
12388  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12389  * @cfg {Array} fields Array of field definition objects
12390  * @constructor
12391  * Create a new JsonReader
12392  * @param {Object} meta Metadata configuration options
12393  * @param {Object} recordType Either an Array of field definition objects,
12394  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12395  */
12396 Roo.data.JsonReader = function(meta, recordType){
12397     
12398     meta = meta || {};
12399     // set some defaults:
12400     Roo.applyIf(meta, {
12401         totalProperty: 'total',
12402         successProperty : 'success',
12403         root : 'data',
12404         id : 'id'
12405     });
12406     
12407     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12408 };
12409 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12410     
12411     /**
12412      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12413      * Used by Store query builder to append _requestMeta to params.
12414      * 
12415      */
12416     metaFromRemote : false,
12417     /**
12418      * This method is only used by a DataProxy which has retrieved data from a remote server.
12419      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12420      * @return {Object} data A data block which is used by an Roo.data.Store object as
12421      * a cache of Roo.data.Records.
12422      */
12423     read : function(response){
12424         var json = response.responseText;
12425        
12426         var o = /* eval:var:o */ eval("("+json+")");
12427         if(!o) {
12428             throw {message: "JsonReader.read: Json object not found"};
12429         }
12430         
12431         if(o.metaData){
12432             
12433             delete this.ef;
12434             this.metaFromRemote = true;
12435             this.meta = o.metaData;
12436             this.recordType = Roo.data.Record.create(o.metaData.fields);
12437             this.onMetaChange(this.meta, this.recordType, o);
12438         }
12439         return this.readRecords(o);
12440     },
12441
12442     // private function a store will implement
12443     onMetaChange : function(meta, recordType, o){
12444
12445     },
12446
12447     /**
12448          * @ignore
12449          */
12450     simpleAccess: function(obj, subsc) {
12451         return obj[subsc];
12452     },
12453
12454         /**
12455          * @ignore
12456          */
12457     getJsonAccessor: function(){
12458         var re = /[\[\.]/;
12459         return function(expr) {
12460             try {
12461                 return(re.test(expr))
12462                     ? new Function("obj", "return obj." + expr)
12463                     : function(obj){
12464                         return obj[expr];
12465                     };
12466             } catch(e){}
12467             return Roo.emptyFn;
12468         };
12469     }(),
12470
12471     /**
12472      * Create a data block containing Roo.data.Records from an XML document.
12473      * @param {Object} o An object which contains an Array of row objects in the property specified
12474      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12475      * which contains the total size of the dataset.
12476      * @return {Object} data A data block which is used by an Roo.data.Store object as
12477      * a cache of Roo.data.Records.
12478      */
12479     readRecords : function(o){
12480         /**
12481          * After any data loads, the raw JSON data is available for further custom processing.
12482          * @type Object
12483          */
12484         this.o = o;
12485         var s = this.meta, Record = this.recordType,
12486             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12487
12488 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12489         if (!this.ef) {
12490             if(s.totalProperty) {
12491                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12492                 }
12493                 if(s.successProperty) {
12494                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12495                 }
12496                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12497                 if (s.id) {
12498                         var g = this.getJsonAccessor(s.id);
12499                         this.getId = function(rec) {
12500                                 var r = g(rec);  
12501                                 return (r === undefined || r === "") ? null : r;
12502                         };
12503                 } else {
12504                         this.getId = function(){return null;};
12505                 }
12506             this.ef = [];
12507             for(var jj = 0; jj < fl; jj++){
12508                 f = fi[jj];
12509                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12510                 this.ef[jj] = this.getJsonAccessor(map);
12511             }
12512         }
12513
12514         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12515         if(s.totalProperty){
12516             var vt = parseInt(this.getTotal(o), 10);
12517             if(!isNaN(vt)){
12518                 totalRecords = vt;
12519             }
12520         }
12521         if(s.successProperty){
12522             var vs = this.getSuccess(o);
12523             if(vs === false || vs === 'false'){
12524                 success = false;
12525             }
12526         }
12527         var records = [];
12528         for(var i = 0; i < c; i++){
12529                 var n = root[i];
12530             var values = {};
12531             var id = this.getId(n);
12532             for(var j = 0; j < fl; j++){
12533                 f = fi[j];
12534             var v = this.ef[j](n);
12535             if (!f.convert) {
12536                 Roo.log('missing convert for ' + f.name);
12537                 Roo.log(f);
12538                 continue;
12539             }
12540             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12541             }
12542             var record = new Record(values, id);
12543             record.json = n;
12544             records[i] = record;
12545         }
12546         return {
12547             raw : o,
12548             success : success,
12549             records : records,
12550             totalRecords : totalRecords
12551         };
12552     }
12553 });/*
12554  * Based on:
12555  * Ext JS Library 1.1.1
12556  * Copyright(c) 2006-2007, Ext JS, LLC.
12557  *
12558  * Originally Released Under LGPL - original licence link has changed is not relivant.
12559  *
12560  * Fork - LGPL
12561  * <script type="text/javascript">
12562  */
12563
12564 /**
12565  * @class Roo.data.ArrayReader
12566  * @extends Roo.data.DataReader
12567  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12568  * Each element of that Array represents a row of data fields. The
12569  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12570  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12571  * <p>
12572  * Example code:.
12573  * <pre><code>
12574 var RecordDef = Roo.data.Record.create([
12575     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12576     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12577 ]);
12578 var myReader = new Roo.data.ArrayReader({
12579     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12580 }, RecordDef);
12581 </code></pre>
12582  * <p>
12583  * This would consume an Array like this:
12584  * <pre><code>
12585 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12586   </code></pre>
12587  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12588  * @constructor
12589  * Create a new JsonReader
12590  * @param {Object} meta Metadata configuration options.
12591  * @param {Object} recordType Either an Array of field definition objects
12592  * as specified to {@link Roo.data.Record#create},
12593  * or an {@link Roo.data.Record} object
12594  * created using {@link Roo.data.Record#create}.
12595  */
12596 Roo.data.ArrayReader = function(meta, recordType){
12597     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12598 };
12599
12600 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12601     /**
12602      * Create a data block containing Roo.data.Records from an XML document.
12603      * @param {Object} o An Array of row objects which represents the dataset.
12604      * @return {Object} data A data block which is used by an Roo.data.Store object as
12605      * a cache of Roo.data.Records.
12606      */
12607     readRecords : function(o){
12608         var sid = this.meta ? this.meta.id : null;
12609         var recordType = this.recordType, fields = recordType.prototype.fields;
12610         var records = [];
12611         var root = o;
12612             for(var i = 0; i < root.length; i++){
12613                     var n = root[i];
12614                 var values = {};
12615                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12616                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12617                 var f = fields.items[j];
12618                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12619                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12620                 v = f.convert(v);
12621                 values[f.name] = v;
12622             }
12623                 var record = new recordType(values, id);
12624                 record.json = n;
12625                 records[records.length] = record;
12626             }
12627             return {
12628                 records : records,
12629                 totalRecords : records.length
12630             };
12631     }
12632 });/*
12633  * - LGPL
12634  * * 
12635  */
12636
12637 /**
12638  * @class Roo.bootstrap.ComboBox
12639  * @extends Roo.bootstrap.TriggerField
12640  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12641  * @cfg {Boolean} append (true|false) default false
12642  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12643  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12644  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12645  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12646  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12647  * @cfg {Boolean} animate default true
12648  * @cfg {Boolean} emptyResultText only for touch device
12649  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12650  * @cfg {String} emptyTitle default ''
12651  * @constructor
12652  * Create a new ComboBox.
12653  * @param {Object} config Configuration options
12654  */
12655 Roo.bootstrap.ComboBox = function(config){
12656     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12657     this.addEvents({
12658         /**
12659          * @event expand
12660          * Fires when the dropdown list is expanded
12661         * @param {Roo.bootstrap.ComboBox} combo This combo box
12662         */
12663         'expand' : true,
12664         /**
12665          * @event collapse
12666          * Fires when the dropdown list is collapsed
12667         * @param {Roo.bootstrap.ComboBox} combo This combo box
12668         */
12669         'collapse' : true,
12670         /**
12671          * @event beforeselect
12672          * Fires before a list item is selected. Return false to cancel the selection.
12673         * @param {Roo.bootstrap.ComboBox} combo This combo box
12674         * @param {Roo.data.Record} record The data record returned from the underlying store
12675         * @param {Number} index The index of the selected item in the dropdown list
12676         */
12677         'beforeselect' : true,
12678         /**
12679          * @event select
12680          * Fires when a list item is selected
12681         * @param {Roo.bootstrap.ComboBox} combo This combo box
12682         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12683         * @param {Number} index The index of the selected item in the dropdown list
12684         */
12685         'select' : true,
12686         /**
12687          * @event beforequery
12688          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12689          * The event object passed has these properties:
12690         * @param {Roo.bootstrap.ComboBox} combo This combo box
12691         * @param {String} query The query
12692         * @param {Boolean} forceAll true to force "all" query
12693         * @param {Boolean} cancel true to cancel the query
12694         * @param {Object} e The query event object
12695         */
12696         'beforequery': true,
12697          /**
12698          * @event add
12699          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12700         * @param {Roo.bootstrap.ComboBox} combo This combo box
12701         */
12702         'add' : true,
12703         /**
12704          * @event edit
12705          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12706         * @param {Roo.bootstrap.ComboBox} combo This combo box
12707         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12708         */
12709         'edit' : true,
12710         /**
12711          * @event remove
12712          * Fires when the remove value from the combobox array
12713         * @param {Roo.bootstrap.ComboBox} combo This combo box
12714         */
12715         'remove' : true,
12716         /**
12717          * @event afterremove
12718          * Fires when the remove value from the combobox array
12719         * @param {Roo.bootstrap.ComboBox} combo This combo box
12720         */
12721         'afterremove' : true,
12722         /**
12723          * @event specialfilter
12724          * Fires when specialfilter
12725             * @param {Roo.bootstrap.ComboBox} combo This combo box
12726             */
12727         'specialfilter' : true,
12728         /**
12729          * @event tick
12730          * Fires when tick the element
12731             * @param {Roo.bootstrap.ComboBox} combo This combo box
12732             */
12733         'tick' : true,
12734         /**
12735          * @event touchviewdisplay
12736          * Fires when touch view require special display (default is using displayField)
12737             * @param {Roo.bootstrap.ComboBox} combo This combo box
12738             * @param {Object} cfg set html .
12739             */
12740         'touchviewdisplay' : true
12741         
12742     });
12743     
12744     this.item = [];
12745     this.tickItems = [];
12746     
12747     this.selectedIndex = -1;
12748     if(this.mode == 'local'){
12749         if(config.queryDelay === undefined){
12750             this.queryDelay = 10;
12751         }
12752         if(config.minChars === undefined){
12753             this.minChars = 0;
12754         }
12755     }
12756 };
12757
12758 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12759      
12760     /**
12761      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12762      * rendering into an Roo.Editor, defaults to false)
12763      */
12764     /**
12765      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12766      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12767      */
12768     /**
12769      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12770      */
12771     /**
12772      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12773      * the dropdown list (defaults to undefined, with no header element)
12774      */
12775
12776      /**
12777      * @cfg {String/Roo.Template} tpl The template to use to render the output
12778      */
12779      
12780      /**
12781      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12782      */
12783     listWidth: undefined,
12784     /**
12785      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12786      * mode = 'remote' or 'text' if mode = 'local')
12787      */
12788     displayField: undefined,
12789     
12790     /**
12791      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12792      * mode = 'remote' or 'value' if mode = 'local'). 
12793      * Note: use of a valueField requires the user make a selection
12794      * in order for a value to be mapped.
12795      */
12796     valueField: undefined,
12797     /**
12798      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12799      */
12800     modalTitle : '',
12801     
12802     /**
12803      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12804      * field's data value (defaults to the underlying DOM element's name)
12805      */
12806     hiddenName: undefined,
12807     /**
12808      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12809      */
12810     listClass: '',
12811     /**
12812      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12813      */
12814     selectedClass: 'active',
12815     
12816     /**
12817      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12818      */
12819     shadow:'sides',
12820     /**
12821      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12822      * anchor positions (defaults to 'tl-bl')
12823      */
12824     listAlign: 'tl-bl?',
12825     /**
12826      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12827      */
12828     maxHeight: 300,
12829     /**
12830      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12831      * query specified by the allQuery config option (defaults to 'query')
12832      */
12833     triggerAction: 'query',
12834     /**
12835      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12836      * (defaults to 4, does not apply if editable = false)
12837      */
12838     minChars : 4,
12839     /**
12840      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12841      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12842      */
12843     typeAhead: false,
12844     /**
12845      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12846      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12847      */
12848     queryDelay: 500,
12849     /**
12850      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12851      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12852      */
12853     pageSize: 0,
12854     /**
12855      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12856      * when editable = true (defaults to false)
12857      */
12858     selectOnFocus:false,
12859     /**
12860      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12861      */
12862     queryParam: 'query',
12863     /**
12864      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12865      * when mode = 'remote' (defaults to 'Loading...')
12866      */
12867     loadingText: 'Loading...',
12868     /**
12869      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12870      */
12871     resizable: false,
12872     /**
12873      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12874      */
12875     handleHeight : 8,
12876     /**
12877      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12878      * traditional select (defaults to true)
12879      */
12880     editable: true,
12881     /**
12882      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12883      */
12884     allQuery: '',
12885     /**
12886      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12887      */
12888     mode: 'remote',
12889     /**
12890      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12891      * listWidth has a higher value)
12892      */
12893     minListWidth : 70,
12894     /**
12895      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12896      * allow the user to set arbitrary text into the field (defaults to false)
12897      */
12898     forceSelection:false,
12899     /**
12900      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12901      * if typeAhead = true (defaults to 250)
12902      */
12903     typeAheadDelay : 250,
12904     /**
12905      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12906      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12907      */
12908     valueNotFoundText : undefined,
12909     /**
12910      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12911      */
12912     blockFocus : false,
12913     
12914     /**
12915      * @cfg {Boolean} disableClear Disable showing of clear button.
12916      */
12917     disableClear : false,
12918     /**
12919      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12920      */
12921     alwaysQuery : false,
12922     
12923     /**
12924      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12925      */
12926     multiple : false,
12927     
12928     /**
12929      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12930      */
12931     invalidClass : "has-warning",
12932     
12933     /**
12934      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12935      */
12936     validClass : "has-success",
12937     
12938     /**
12939      * @cfg {Boolean} specialFilter (true|false) special filter default false
12940      */
12941     specialFilter : false,
12942     
12943     /**
12944      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12945      */
12946     mobileTouchView : true,
12947     
12948     /**
12949      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12950      */
12951     useNativeIOS : false,
12952     
12953     ios_options : false,
12954     
12955     //private
12956     addicon : false,
12957     editicon: false,
12958     
12959     page: 0,
12960     hasQuery: false,
12961     append: false,
12962     loadNext: false,
12963     autoFocus : true,
12964     tickable : false,
12965     btnPosition : 'right',
12966     triggerList : true,
12967     showToggleBtn : true,
12968     animate : true,
12969     emptyResultText: 'Empty',
12970     triggerText : 'Select',
12971     emptyTitle : '',
12972     
12973     // element that contains real text value.. (when hidden is used..)
12974     
12975     getAutoCreate : function()
12976     {   
12977         var cfg = false;
12978         //render
12979         /*
12980          * Render classic select for iso
12981          */
12982         
12983         if(Roo.isIOS && this.useNativeIOS){
12984             cfg = this.getAutoCreateNativeIOS();
12985             return cfg;
12986         }
12987         
12988         /*
12989          * Touch Devices
12990          */
12991         
12992         if(Roo.isTouch && this.mobileTouchView){
12993             cfg = this.getAutoCreateTouchView();
12994             return cfg;;
12995         }
12996         
12997         /*
12998          *  Normal ComboBox
12999          */
13000         if(!this.tickable){
13001             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13002             return cfg;
13003         }
13004         
13005         /*
13006          *  ComboBox with tickable selections
13007          */
13008              
13009         var align = this.labelAlign || this.parentLabelAlign();
13010         
13011         cfg = {
13012             cls : 'form-group roo-combobox-tickable' //input-group
13013         };
13014         
13015         var btn_text_select = '';
13016         var btn_text_done = '';
13017         var btn_text_cancel = '';
13018         
13019         if (this.btn_text_show) {
13020             btn_text_select = 'Select';
13021             btn_text_done = 'Done';
13022             btn_text_cancel = 'Cancel'; 
13023         }
13024         
13025         var buttons = {
13026             tag : 'div',
13027             cls : 'tickable-buttons',
13028             cn : [
13029                 {
13030                     tag : 'button',
13031                     type : 'button',
13032                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13033                     //html : this.triggerText
13034                     html: btn_text_select
13035                 },
13036                 {
13037                     tag : 'button',
13038                     type : 'button',
13039                     name : 'ok',
13040                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13041                     //html : 'Done'
13042                     html: btn_text_done
13043                 },
13044                 {
13045                     tag : 'button',
13046                     type : 'button',
13047                     name : 'cancel',
13048                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13049                     //html : 'Cancel'
13050                     html: btn_text_cancel
13051                 }
13052             ]
13053         };
13054         
13055         if(this.editable){
13056             buttons.cn.unshift({
13057                 tag: 'input',
13058                 cls: 'roo-select2-search-field-input'
13059             });
13060         }
13061         
13062         var _this = this;
13063         
13064         Roo.each(buttons.cn, function(c){
13065             if (_this.size) {
13066                 c.cls += ' btn-' + _this.size;
13067             }
13068
13069             if (_this.disabled) {
13070                 c.disabled = true;
13071             }
13072         });
13073         
13074         var box = {
13075             tag: 'div',
13076             cn: [
13077                 {
13078                     tag: 'input',
13079                     type : 'hidden',
13080                     cls: 'form-hidden-field'
13081                 },
13082                 {
13083                     tag: 'ul',
13084                     cls: 'roo-select2-choices',
13085                     cn:[
13086                         {
13087                             tag: 'li',
13088                             cls: 'roo-select2-search-field',
13089                             cn: [
13090                                 buttons
13091                             ]
13092                         }
13093                     ]
13094                 }
13095             ]
13096         };
13097         
13098         var combobox = {
13099             cls: 'roo-select2-container input-group roo-select2-container-multi',
13100             cn: [
13101                 box
13102 //                {
13103 //                    tag: 'ul',
13104 //                    cls: 'typeahead typeahead-long dropdown-menu',
13105 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13106 //                }
13107             ]
13108         };
13109         
13110         if(this.hasFeedback && !this.allowBlank){
13111             
13112             var feedback = {
13113                 tag: 'span',
13114                 cls: 'glyphicon form-control-feedback'
13115             };
13116
13117             combobox.cn.push(feedback);
13118         }
13119         
13120         
13121         if (align ==='left' && this.fieldLabel.length) {
13122             
13123             cfg.cls += ' roo-form-group-label-left';
13124             
13125             cfg.cn = [
13126                 {
13127                     tag : 'i',
13128                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13129                     tooltip : 'This field is required'
13130                 },
13131                 {
13132                     tag: 'label',
13133                     'for' :  id,
13134                     cls : 'control-label',
13135                     html : this.fieldLabel
13136
13137                 },
13138                 {
13139                     cls : "", 
13140                     cn: [
13141                         combobox
13142                     ]
13143                 }
13144
13145             ];
13146             
13147             var labelCfg = cfg.cn[1];
13148             var contentCfg = cfg.cn[2];
13149             
13150
13151             if(this.indicatorpos == 'right'){
13152                 
13153                 cfg.cn = [
13154                     {
13155                         tag: 'label',
13156                         'for' :  id,
13157                         cls : 'control-label',
13158                         cn : [
13159                             {
13160                                 tag : 'span',
13161                                 html : this.fieldLabel
13162                             },
13163                             {
13164                                 tag : 'i',
13165                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13166                                 tooltip : 'This field is required'
13167                             }
13168                         ]
13169                     },
13170                     {
13171                         cls : "",
13172                         cn: [
13173                             combobox
13174                         ]
13175                     }
13176
13177                 ];
13178                 
13179                 
13180                 
13181                 labelCfg = cfg.cn[0];
13182                 contentCfg = cfg.cn[1];
13183             
13184             }
13185             
13186             if(this.labelWidth > 12){
13187                 labelCfg.style = "width: " + this.labelWidth + 'px';
13188             }
13189             
13190             if(this.labelWidth < 13 && this.labelmd == 0){
13191                 this.labelmd = this.labelWidth;
13192             }
13193             
13194             if(this.labellg > 0){
13195                 labelCfg.cls += ' col-lg-' + this.labellg;
13196                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13197             }
13198             
13199             if(this.labelmd > 0){
13200                 labelCfg.cls += ' col-md-' + this.labelmd;
13201                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13202             }
13203             
13204             if(this.labelsm > 0){
13205                 labelCfg.cls += ' col-sm-' + this.labelsm;
13206                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13207             }
13208             
13209             if(this.labelxs > 0){
13210                 labelCfg.cls += ' col-xs-' + this.labelxs;
13211                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13212             }
13213                 
13214                 
13215         } else if ( this.fieldLabel.length) {
13216 //                Roo.log(" label");
13217                  cfg.cn = [
13218                     {
13219                         tag : 'i',
13220                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13221                         tooltip : 'This field is required'
13222                     },
13223                     {
13224                         tag: 'label',
13225                         //cls : 'input-group-addon',
13226                         html : this.fieldLabel
13227                     },
13228                     combobox
13229                 ];
13230                 
13231                 if(this.indicatorpos == 'right'){
13232                     cfg.cn = [
13233                         {
13234                             tag: 'label',
13235                             //cls : 'input-group-addon',
13236                             html : this.fieldLabel
13237                         },
13238                         {
13239                             tag : 'i',
13240                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13241                             tooltip : 'This field is required'
13242                         },
13243                         combobox
13244                     ];
13245                     
13246                 }
13247
13248         } else {
13249             
13250 //                Roo.log(" no label && no align");
13251                 cfg = combobox
13252                      
13253                 
13254         }
13255          
13256         var settings=this;
13257         ['xs','sm','md','lg'].map(function(size){
13258             if (settings[size]) {
13259                 cfg.cls += ' col-' + size + '-' + settings[size];
13260             }
13261         });
13262         
13263         return cfg;
13264         
13265     },
13266     
13267     _initEventsCalled : false,
13268     
13269     // private
13270     initEvents: function()
13271     {   
13272         if (this._initEventsCalled) { // as we call render... prevent looping...
13273             return;
13274         }
13275         this._initEventsCalled = true;
13276         
13277         if (!this.store) {
13278             throw "can not find store for combo";
13279         }
13280         
13281         this.indicator = this.indicatorEl();
13282         
13283         this.store = Roo.factory(this.store, Roo.data);
13284         this.store.parent = this;
13285         
13286         // if we are building from html. then this element is so complex, that we can not really
13287         // use the rendered HTML.
13288         // so we have to trash and replace the previous code.
13289         if (Roo.XComponent.build_from_html) {
13290             // remove this element....
13291             var e = this.el.dom, k=0;
13292             while (e ) { e = e.previousSibling;  ++k;}
13293
13294             this.el.remove();
13295             
13296             this.el=false;
13297             this.rendered = false;
13298             
13299             this.render(this.parent().getChildContainer(true), k);
13300         }
13301         
13302         if(Roo.isIOS && this.useNativeIOS){
13303             this.initIOSView();
13304             return;
13305         }
13306         
13307         /*
13308          * Touch Devices
13309          */
13310         
13311         if(Roo.isTouch && this.mobileTouchView){
13312             this.initTouchView();
13313             return;
13314         }
13315         
13316         if(this.tickable){
13317             this.initTickableEvents();
13318             return;
13319         }
13320         
13321         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13322         
13323         if(this.hiddenName){
13324             
13325             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13326             
13327             this.hiddenField.dom.value =
13328                 this.hiddenValue !== undefined ? this.hiddenValue :
13329                 this.value !== undefined ? this.value : '';
13330
13331             // prevent input submission
13332             this.el.dom.removeAttribute('name');
13333             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13334              
13335              
13336         }
13337         //if(Roo.isGecko){
13338         //    this.el.dom.setAttribute('autocomplete', 'off');
13339         //}
13340         
13341         var cls = 'x-combo-list';
13342         
13343         //this.list = new Roo.Layer({
13344         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13345         //});
13346         
13347         var _this = this;
13348         
13349         (function(){
13350             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13351             _this.list.setWidth(lw);
13352         }).defer(100);
13353         
13354         this.list.on('mouseover', this.onViewOver, this);
13355         this.list.on('mousemove', this.onViewMove, this);
13356         this.list.on('scroll', this.onViewScroll, this);
13357         
13358         /*
13359         this.list.swallowEvent('mousewheel');
13360         this.assetHeight = 0;
13361
13362         if(this.title){
13363             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13364             this.assetHeight += this.header.getHeight();
13365         }
13366
13367         this.innerList = this.list.createChild({cls:cls+'-inner'});
13368         this.innerList.on('mouseover', this.onViewOver, this);
13369         this.innerList.on('mousemove', this.onViewMove, this);
13370         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13371         
13372         if(this.allowBlank && !this.pageSize && !this.disableClear){
13373             this.footer = this.list.createChild({cls:cls+'-ft'});
13374             this.pageTb = new Roo.Toolbar(this.footer);
13375            
13376         }
13377         if(this.pageSize){
13378             this.footer = this.list.createChild({cls:cls+'-ft'});
13379             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13380                     {pageSize: this.pageSize});
13381             
13382         }
13383         
13384         if (this.pageTb && this.allowBlank && !this.disableClear) {
13385             var _this = this;
13386             this.pageTb.add(new Roo.Toolbar.Fill(), {
13387                 cls: 'x-btn-icon x-btn-clear',
13388                 text: '&#160;',
13389                 handler: function()
13390                 {
13391                     _this.collapse();
13392                     _this.clearValue();
13393                     _this.onSelect(false, -1);
13394                 }
13395             });
13396         }
13397         if (this.footer) {
13398             this.assetHeight += this.footer.getHeight();
13399         }
13400         */
13401             
13402         if(!this.tpl){
13403             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13404         }
13405
13406         this.view = new Roo.View(this.list, this.tpl, {
13407             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13408         });
13409         //this.view.wrapEl.setDisplayed(false);
13410         this.view.on('click', this.onViewClick, this);
13411         
13412         
13413         this.store.on('beforeload', this.onBeforeLoad, this);
13414         this.store.on('load', this.onLoad, this);
13415         this.store.on('loadexception', this.onLoadException, this);
13416         /*
13417         if(this.resizable){
13418             this.resizer = new Roo.Resizable(this.list,  {
13419                pinned:true, handles:'se'
13420             });
13421             this.resizer.on('resize', function(r, w, h){
13422                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13423                 this.listWidth = w;
13424                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13425                 this.restrictHeight();
13426             }, this);
13427             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13428         }
13429         */
13430         if(!this.editable){
13431             this.editable = true;
13432             this.setEditable(false);
13433         }
13434         
13435         /*
13436         
13437         if (typeof(this.events.add.listeners) != 'undefined') {
13438             
13439             this.addicon = this.wrap.createChild(
13440                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13441        
13442             this.addicon.on('click', function(e) {
13443                 this.fireEvent('add', this);
13444             }, this);
13445         }
13446         if (typeof(this.events.edit.listeners) != 'undefined') {
13447             
13448             this.editicon = this.wrap.createChild(
13449                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13450             if (this.addicon) {
13451                 this.editicon.setStyle('margin-left', '40px');
13452             }
13453             this.editicon.on('click', function(e) {
13454                 
13455                 // we fire even  if inothing is selected..
13456                 this.fireEvent('edit', this, this.lastData );
13457                 
13458             }, this);
13459         }
13460         */
13461         
13462         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13463             "up" : function(e){
13464                 this.inKeyMode = true;
13465                 this.selectPrev();
13466             },
13467
13468             "down" : function(e){
13469                 if(!this.isExpanded()){
13470                     this.onTriggerClick();
13471                 }else{
13472                     this.inKeyMode = true;
13473                     this.selectNext();
13474                 }
13475             },
13476
13477             "enter" : function(e){
13478 //                this.onViewClick();
13479                 //return true;
13480                 this.collapse();
13481                 
13482                 if(this.fireEvent("specialkey", this, e)){
13483                     this.onViewClick(false);
13484                 }
13485                 
13486                 return true;
13487             },
13488
13489             "esc" : function(e){
13490                 this.collapse();
13491             },
13492
13493             "tab" : function(e){
13494                 this.collapse();
13495                 
13496                 if(this.fireEvent("specialkey", this, e)){
13497                     this.onViewClick(false);
13498                 }
13499                 
13500                 return true;
13501             },
13502
13503             scope : this,
13504
13505             doRelay : function(foo, bar, hname){
13506                 if(hname == 'down' || this.scope.isExpanded()){
13507                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13508                 }
13509                 return true;
13510             },
13511
13512             forceKeyDown: true
13513         });
13514         
13515         
13516         this.queryDelay = Math.max(this.queryDelay || 10,
13517                 this.mode == 'local' ? 10 : 250);
13518         
13519         
13520         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13521         
13522         if(this.typeAhead){
13523             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13524         }
13525         if(this.editable !== false){
13526             this.inputEl().on("keyup", this.onKeyUp, this);
13527         }
13528         if(this.forceSelection){
13529             this.inputEl().on('blur', this.doForce, this);
13530         }
13531         
13532         if(this.multiple){
13533             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13534             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13535         }
13536     },
13537     
13538     initTickableEvents: function()
13539     {   
13540         this.createList();
13541         
13542         if(this.hiddenName){
13543             
13544             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13545             
13546             this.hiddenField.dom.value =
13547                 this.hiddenValue !== undefined ? this.hiddenValue :
13548                 this.value !== undefined ? this.value : '';
13549
13550             // prevent input submission
13551             this.el.dom.removeAttribute('name');
13552             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13553              
13554              
13555         }
13556         
13557 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13558         
13559         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13560         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13561         if(this.triggerList){
13562             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13563         }
13564          
13565         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13566         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13567         
13568         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13569         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13570         
13571         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13572         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13573         
13574         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13575         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13576         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13577         
13578         this.okBtn.hide();
13579         this.cancelBtn.hide();
13580         
13581         var _this = this;
13582         
13583         (function(){
13584             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13585             _this.list.setWidth(lw);
13586         }).defer(100);
13587         
13588         this.list.on('mouseover', this.onViewOver, this);
13589         this.list.on('mousemove', this.onViewMove, this);
13590         
13591         this.list.on('scroll', this.onViewScroll, this);
13592         
13593         if(!this.tpl){
13594             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>';
13595         }
13596
13597         this.view = new Roo.View(this.list, this.tpl, {
13598             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13599         });
13600         
13601         //this.view.wrapEl.setDisplayed(false);
13602         this.view.on('click', this.onViewClick, this);
13603         
13604         
13605         
13606         this.store.on('beforeload', this.onBeforeLoad, this);
13607         this.store.on('load', this.onLoad, this);
13608         this.store.on('loadexception', this.onLoadException, this);
13609         
13610         if(this.editable){
13611             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13612                 "up" : function(e){
13613                     this.inKeyMode = true;
13614                     this.selectPrev();
13615                 },
13616
13617                 "down" : function(e){
13618                     this.inKeyMode = true;
13619                     this.selectNext();
13620                 },
13621
13622                 "enter" : function(e){
13623                     if(this.fireEvent("specialkey", this, e)){
13624                         this.onViewClick(false);
13625                     }
13626                     
13627                     return true;
13628                 },
13629
13630                 "esc" : function(e){
13631                     this.onTickableFooterButtonClick(e, false, false);
13632                 },
13633
13634                 "tab" : function(e){
13635                     this.fireEvent("specialkey", this, e);
13636                     
13637                     this.onTickableFooterButtonClick(e, false, false);
13638                     
13639                     return true;
13640                 },
13641
13642                 scope : this,
13643
13644                 doRelay : function(e, fn, key){
13645                     if(this.scope.isExpanded()){
13646                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13647                     }
13648                     return true;
13649                 },
13650
13651                 forceKeyDown: true
13652             });
13653         }
13654         
13655         this.queryDelay = Math.max(this.queryDelay || 10,
13656                 this.mode == 'local' ? 10 : 250);
13657         
13658         
13659         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13660         
13661         if(this.typeAhead){
13662             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13663         }
13664         
13665         if(this.editable !== false){
13666             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13667         }
13668         
13669         this.indicator = this.indicatorEl();
13670         
13671         if(this.indicator){
13672             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13673             this.indicator.hide();
13674         }
13675         
13676     },
13677
13678     onDestroy : function(){
13679         if(this.view){
13680             this.view.setStore(null);
13681             this.view.el.removeAllListeners();
13682             this.view.el.remove();
13683             this.view.purgeListeners();
13684         }
13685         if(this.list){
13686             this.list.dom.innerHTML  = '';
13687         }
13688         
13689         if(this.store){
13690             this.store.un('beforeload', this.onBeforeLoad, this);
13691             this.store.un('load', this.onLoad, this);
13692             this.store.un('loadexception', this.onLoadException, this);
13693         }
13694         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13695     },
13696
13697     // private
13698     fireKey : function(e){
13699         if(e.isNavKeyPress() && !this.list.isVisible()){
13700             this.fireEvent("specialkey", this, e);
13701         }
13702     },
13703
13704     // private
13705     onResize: function(w, h){
13706 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13707 //        
13708 //        if(typeof w != 'number'){
13709 //            // we do not handle it!?!?
13710 //            return;
13711 //        }
13712 //        var tw = this.trigger.getWidth();
13713 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13714 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13715 //        var x = w - tw;
13716 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13717 //            
13718 //        //this.trigger.setStyle('left', x+'px');
13719 //        
13720 //        if(this.list && this.listWidth === undefined){
13721 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13722 //            this.list.setWidth(lw);
13723 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13724 //        }
13725         
13726     
13727         
13728     },
13729
13730     /**
13731      * Allow or prevent the user from directly editing the field text.  If false is passed,
13732      * the user will only be able to select from the items defined in the dropdown list.  This method
13733      * is the runtime equivalent of setting the 'editable' config option at config time.
13734      * @param {Boolean} value True to allow the user to directly edit the field text
13735      */
13736     setEditable : function(value){
13737         if(value == this.editable){
13738             return;
13739         }
13740         this.editable = value;
13741         if(!value){
13742             this.inputEl().dom.setAttribute('readOnly', true);
13743             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13744             this.inputEl().addClass('x-combo-noedit');
13745         }else{
13746             this.inputEl().dom.setAttribute('readOnly', false);
13747             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13748             this.inputEl().removeClass('x-combo-noedit');
13749         }
13750     },
13751
13752     // private
13753     
13754     onBeforeLoad : function(combo,opts){
13755         if(!this.hasFocus){
13756             return;
13757         }
13758          if (!opts.add) {
13759             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13760          }
13761         this.restrictHeight();
13762         this.selectedIndex = -1;
13763     },
13764
13765     // private
13766     onLoad : function(){
13767         
13768         this.hasQuery = false;
13769         
13770         if(!this.hasFocus){
13771             return;
13772         }
13773         
13774         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13775             this.loading.hide();
13776         }
13777         
13778         if(this.store.getCount() > 0){
13779             
13780             this.expand();
13781             this.restrictHeight();
13782             if(this.lastQuery == this.allQuery){
13783                 if(this.editable && !this.tickable){
13784                     this.inputEl().dom.select();
13785                 }
13786                 
13787                 if(
13788                     !this.selectByValue(this.value, true) &&
13789                     this.autoFocus && 
13790                     (
13791                         !this.store.lastOptions ||
13792                         typeof(this.store.lastOptions.add) == 'undefined' || 
13793                         this.store.lastOptions.add != true
13794                     )
13795                 ){
13796                     this.select(0, true);
13797                 }
13798             }else{
13799                 if(this.autoFocus){
13800                     this.selectNext();
13801                 }
13802                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13803                     this.taTask.delay(this.typeAheadDelay);
13804                 }
13805             }
13806         }else{
13807             this.onEmptyResults();
13808         }
13809         
13810         //this.el.focus();
13811     },
13812     // private
13813     onLoadException : function()
13814     {
13815         this.hasQuery = false;
13816         
13817         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13818             this.loading.hide();
13819         }
13820         
13821         if(this.tickable && this.editable){
13822             return;
13823         }
13824         
13825         this.collapse();
13826         // only causes errors at present
13827         //Roo.log(this.store.reader.jsonData);
13828         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13829             // fixme
13830             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13831         //}
13832         
13833         
13834     },
13835     // private
13836     onTypeAhead : function(){
13837         if(this.store.getCount() > 0){
13838             var r = this.store.getAt(0);
13839             var newValue = r.data[this.displayField];
13840             var len = newValue.length;
13841             var selStart = this.getRawValue().length;
13842             
13843             if(selStart != len){
13844                 this.setRawValue(newValue);
13845                 this.selectText(selStart, newValue.length);
13846             }
13847         }
13848     },
13849
13850     // private
13851     onSelect : function(record, index){
13852         
13853         if(this.fireEvent('beforeselect', this, record, index) !== false){
13854         
13855             this.setFromData(index > -1 ? record.data : false);
13856             
13857             this.collapse();
13858             this.fireEvent('select', this, record, index);
13859         }
13860     },
13861
13862     /**
13863      * Returns the currently selected field value or empty string if no value is set.
13864      * @return {String} value The selected value
13865      */
13866     getValue : function()
13867     {
13868         if(Roo.isIOS && this.useNativeIOS){
13869             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13870         }
13871         
13872         if(this.multiple){
13873             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13874         }
13875         
13876         if(this.valueField){
13877             return typeof this.value != 'undefined' ? this.value : '';
13878         }else{
13879             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13880         }
13881     },
13882     
13883     getRawValue : function()
13884     {
13885         if(Roo.isIOS && this.useNativeIOS){
13886             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13887         }
13888         
13889         var v = this.inputEl().getValue();
13890         
13891         return v;
13892     },
13893
13894     /**
13895      * Clears any text/value currently set in the field
13896      */
13897     clearValue : function(){
13898         
13899         if(this.hiddenField){
13900             this.hiddenField.dom.value = '';
13901         }
13902         this.value = '';
13903         this.setRawValue('');
13904         this.lastSelectionText = '';
13905         this.lastData = false;
13906         
13907         var close = this.closeTriggerEl();
13908         
13909         if(close){
13910             close.hide();
13911         }
13912         
13913         this.validate();
13914         
13915     },
13916
13917     /**
13918      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13919      * will be displayed in the field.  If the value does not match the data value of an existing item,
13920      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13921      * Otherwise the field will be blank (although the value will still be set).
13922      * @param {String} value The value to match
13923      */
13924     setValue : function(v)
13925     {
13926         if(Roo.isIOS && this.useNativeIOS){
13927             this.setIOSValue(v);
13928             return;
13929         }
13930         
13931         if(this.multiple){
13932             this.syncValue();
13933             return;
13934         }
13935         
13936         var text = v;
13937         if(this.valueField){
13938             var r = this.findRecord(this.valueField, v);
13939             if(r){
13940                 text = r.data[this.displayField];
13941             }else if(this.valueNotFoundText !== undefined){
13942                 text = this.valueNotFoundText;
13943             }
13944         }
13945         this.lastSelectionText = text;
13946         if(this.hiddenField){
13947             this.hiddenField.dom.value = v;
13948         }
13949         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13950         this.value = v;
13951         
13952         var close = this.closeTriggerEl();
13953         
13954         if(close){
13955             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13956         }
13957         
13958         this.validate();
13959     },
13960     /**
13961      * @property {Object} the last set data for the element
13962      */
13963     
13964     lastData : false,
13965     /**
13966      * Sets the value of the field based on a object which is related to the record format for the store.
13967      * @param {Object} value the value to set as. or false on reset?
13968      */
13969     setFromData : function(o){
13970         
13971         if(this.multiple){
13972             this.addItem(o);
13973             return;
13974         }
13975             
13976         var dv = ''; // display value
13977         var vv = ''; // value value..
13978         this.lastData = o;
13979         if (this.displayField) {
13980             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13981         } else {
13982             // this is an error condition!!!
13983             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13984         }
13985         
13986         if(this.valueField){
13987             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13988         }
13989         
13990         var close = this.closeTriggerEl();
13991         
13992         if(close){
13993             if(dv.length || vv * 1 > 0){
13994                 close.show() ;
13995                 this.blockFocus=true;
13996             } else {
13997                 close.hide();
13998             }             
13999         }
14000         
14001         if(this.hiddenField){
14002             this.hiddenField.dom.value = vv;
14003             
14004             this.lastSelectionText = dv;
14005             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14006             this.value = vv;
14007             return;
14008         }
14009         // no hidden field.. - we store the value in 'value', but still display
14010         // display field!!!!
14011         this.lastSelectionText = dv;
14012         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14013         this.value = vv;
14014         
14015         
14016         
14017     },
14018     // private
14019     reset : function(){
14020         // overridden so that last data is reset..
14021         
14022         if(this.multiple){
14023             this.clearItem();
14024             return;
14025         }
14026         
14027         this.setValue(this.originalValue);
14028         //this.clearInvalid();
14029         this.lastData = false;
14030         if (this.view) {
14031             this.view.clearSelections();
14032         }
14033         
14034         this.validate();
14035     },
14036     // private
14037     findRecord : function(prop, value){
14038         var record;
14039         if(this.store.getCount() > 0){
14040             this.store.each(function(r){
14041                 if(r.data[prop] == value){
14042                     record = r;
14043                     return false;
14044                 }
14045                 return true;
14046             });
14047         }
14048         return record;
14049     },
14050     
14051     getName: function()
14052     {
14053         // returns hidden if it's set..
14054         if (!this.rendered) {return ''};
14055         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14056         
14057     },
14058     // private
14059     onViewMove : function(e, t){
14060         this.inKeyMode = false;
14061     },
14062
14063     // private
14064     onViewOver : function(e, t){
14065         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14066             return;
14067         }
14068         var item = this.view.findItemFromChild(t);
14069         
14070         if(item){
14071             var index = this.view.indexOf(item);
14072             this.select(index, false);
14073         }
14074     },
14075
14076     // private
14077     onViewClick : function(view, doFocus, el, e)
14078     {
14079         var index = this.view.getSelectedIndexes()[0];
14080         
14081         var r = this.store.getAt(index);
14082         
14083         if(this.tickable){
14084             
14085             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14086                 return;
14087             }
14088             
14089             var rm = false;
14090             var _this = this;
14091             
14092             Roo.each(this.tickItems, function(v,k){
14093                 
14094                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14095                     Roo.log(v);
14096                     _this.tickItems.splice(k, 1);
14097                     
14098                     if(typeof(e) == 'undefined' && view == false){
14099                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14100                     }
14101                     
14102                     rm = true;
14103                     return;
14104                 }
14105             });
14106             
14107             if(rm){
14108                 return;
14109             }
14110             
14111             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14112                 this.tickItems.push(r.data);
14113             }
14114             
14115             if(typeof(e) == 'undefined' && view == false){
14116                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14117             }
14118                     
14119             return;
14120         }
14121         
14122         if(r){
14123             this.onSelect(r, index);
14124         }
14125         if(doFocus !== false && !this.blockFocus){
14126             this.inputEl().focus();
14127         }
14128     },
14129
14130     // private
14131     restrictHeight : function(){
14132         //this.innerList.dom.style.height = '';
14133         //var inner = this.innerList.dom;
14134         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14135         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14136         //this.list.beginUpdate();
14137         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14138         this.list.alignTo(this.inputEl(), this.listAlign);
14139         this.list.alignTo(this.inputEl(), this.listAlign);
14140         //this.list.endUpdate();
14141     },
14142
14143     // private
14144     onEmptyResults : function(){
14145         
14146         if(this.tickable && this.editable){
14147             this.hasFocus = false;
14148             this.restrictHeight();
14149             return;
14150         }
14151         
14152         this.collapse();
14153     },
14154
14155     /**
14156      * Returns true if the dropdown list is expanded, else false.
14157      */
14158     isExpanded : function(){
14159         return this.list.isVisible();
14160     },
14161
14162     /**
14163      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14164      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14165      * @param {String} value The data value of the item to select
14166      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14167      * selected item if it is not currently in view (defaults to true)
14168      * @return {Boolean} True if the value matched an item in the list, else false
14169      */
14170     selectByValue : function(v, scrollIntoView){
14171         if(v !== undefined && v !== null){
14172             var r = this.findRecord(this.valueField || this.displayField, v);
14173             if(r){
14174                 this.select(this.store.indexOf(r), scrollIntoView);
14175                 return true;
14176             }
14177         }
14178         return false;
14179     },
14180
14181     /**
14182      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14183      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14184      * @param {Number} index The zero-based index of the list item to select
14185      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14186      * selected item if it is not currently in view (defaults to true)
14187      */
14188     select : function(index, scrollIntoView){
14189         this.selectedIndex = index;
14190         this.view.select(index);
14191         if(scrollIntoView !== false){
14192             var el = this.view.getNode(index);
14193             /*
14194              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14195              */
14196             if(el){
14197                 this.list.scrollChildIntoView(el, false);
14198             }
14199         }
14200     },
14201
14202     // private
14203     selectNext : function(){
14204         var ct = this.store.getCount();
14205         if(ct > 0){
14206             if(this.selectedIndex == -1){
14207                 this.select(0);
14208             }else if(this.selectedIndex < ct-1){
14209                 this.select(this.selectedIndex+1);
14210             }
14211         }
14212     },
14213
14214     // private
14215     selectPrev : function(){
14216         var ct = this.store.getCount();
14217         if(ct > 0){
14218             if(this.selectedIndex == -1){
14219                 this.select(0);
14220             }else if(this.selectedIndex != 0){
14221                 this.select(this.selectedIndex-1);
14222             }
14223         }
14224     },
14225
14226     // private
14227     onKeyUp : function(e){
14228         if(this.editable !== false && !e.isSpecialKey()){
14229             this.lastKey = e.getKey();
14230             this.dqTask.delay(this.queryDelay);
14231         }
14232     },
14233
14234     // private
14235     validateBlur : function(){
14236         return !this.list || !this.list.isVisible();   
14237     },
14238
14239     // private
14240     initQuery : function(){
14241         
14242         var v = this.getRawValue();
14243         
14244         if(this.tickable && this.editable){
14245             v = this.tickableInputEl().getValue();
14246         }
14247         
14248         this.doQuery(v);
14249     },
14250
14251     // private
14252     doForce : function(){
14253         if(this.inputEl().dom.value.length > 0){
14254             this.inputEl().dom.value =
14255                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14256              
14257         }
14258     },
14259
14260     /**
14261      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14262      * query allowing the query action to be canceled if needed.
14263      * @param {String} query The SQL query to execute
14264      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14265      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14266      * saved in the current store (defaults to false)
14267      */
14268     doQuery : function(q, forceAll){
14269         
14270         if(q === undefined || q === null){
14271             q = '';
14272         }
14273         var qe = {
14274             query: q,
14275             forceAll: forceAll,
14276             combo: this,
14277             cancel:false
14278         };
14279         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14280             return false;
14281         }
14282         q = qe.query;
14283         
14284         forceAll = qe.forceAll;
14285         if(forceAll === true || (q.length >= this.minChars)){
14286             
14287             this.hasQuery = true;
14288             
14289             if(this.lastQuery != q || this.alwaysQuery){
14290                 this.lastQuery = q;
14291                 if(this.mode == 'local'){
14292                     this.selectedIndex = -1;
14293                     if(forceAll){
14294                         this.store.clearFilter();
14295                     }else{
14296                         
14297                         if(this.specialFilter){
14298                             this.fireEvent('specialfilter', this);
14299                             this.onLoad();
14300                             return;
14301                         }
14302                         
14303                         this.store.filter(this.displayField, q);
14304                     }
14305                     
14306                     this.store.fireEvent("datachanged", this.store);
14307                     
14308                     this.onLoad();
14309                     
14310                     
14311                 }else{
14312                     
14313                     this.store.baseParams[this.queryParam] = q;
14314                     
14315                     var options = {params : this.getParams(q)};
14316                     
14317                     if(this.loadNext){
14318                         options.add = true;
14319                         options.params.start = this.page * this.pageSize;
14320                     }
14321                     
14322                     this.store.load(options);
14323                     
14324                     /*
14325                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14326                      *  we should expand the list on onLoad
14327                      *  so command out it
14328                      */
14329 //                    this.expand();
14330                 }
14331             }else{
14332                 this.selectedIndex = -1;
14333                 this.onLoad();   
14334             }
14335         }
14336         
14337         this.loadNext = false;
14338     },
14339     
14340     // private
14341     getParams : function(q){
14342         var p = {};
14343         //p[this.queryParam] = q;
14344         
14345         if(this.pageSize){
14346             p.start = 0;
14347             p.limit = this.pageSize;
14348         }
14349         return p;
14350     },
14351
14352     /**
14353      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14354      */
14355     collapse : function(){
14356         if(!this.isExpanded()){
14357             return;
14358         }
14359         
14360         this.list.hide();
14361         
14362         this.hasFocus = false;
14363         
14364         if(this.tickable){
14365             this.okBtn.hide();
14366             this.cancelBtn.hide();
14367             this.trigger.show();
14368             
14369             if(this.editable){
14370                 this.tickableInputEl().dom.value = '';
14371                 this.tickableInputEl().blur();
14372             }
14373             
14374         }
14375         
14376         Roo.get(document).un('mousedown', this.collapseIf, this);
14377         Roo.get(document).un('mousewheel', this.collapseIf, this);
14378         if (!this.editable) {
14379             Roo.get(document).un('keydown', this.listKeyPress, this);
14380         }
14381         this.fireEvent('collapse', this);
14382         
14383         this.validate();
14384     },
14385
14386     // private
14387     collapseIf : function(e){
14388         var in_combo  = e.within(this.el);
14389         var in_list =  e.within(this.list);
14390         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14391         
14392         if (in_combo || in_list || is_list) {
14393             //e.stopPropagation();
14394             return;
14395         }
14396         
14397         if(this.tickable){
14398             this.onTickableFooterButtonClick(e, false, false);
14399         }
14400
14401         this.collapse();
14402         
14403     },
14404
14405     /**
14406      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14407      */
14408     expand : function(){
14409        
14410         if(this.isExpanded() || !this.hasFocus){
14411             return;
14412         }
14413         
14414         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14415         this.list.setWidth(lw);
14416         
14417         Roo.log('expand');
14418         
14419         this.list.show();
14420         
14421         this.restrictHeight();
14422         
14423         if(this.tickable){
14424             
14425             this.tickItems = Roo.apply([], this.item);
14426             
14427             this.okBtn.show();
14428             this.cancelBtn.show();
14429             this.trigger.hide();
14430             
14431             if(this.editable){
14432                 this.tickableInputEl().focus();
14433             }
14434             
14435         }
14436         
14437         Roo.get(document).on('mousedown', this.collapseIf, this);
14438         Roo.get(document).on('mousewheel', this.collapseIf, this);
14439         if (!this.editable) {
14440             Roo.get(document).on('keydown', this.listKeyPress, this);
14441         }
14442         
14443         this.fireEvent('expand', this);
14444     },
14445
14446     // private
14447     // Implements the default empty TriggerField.onTriggerClick function
14448     onTriggerClick : function(e)
14449     {
14450         Roo.log('trigger click');
14451         
14452         if(this.disabled || !this.triggerList){
14453             return;
14454         }
14455         
14456         this.page = 0;
14457         this.loadNext = false;
14458         
14459         if(this.isExpanded()){
14460             this.collapse();
14461             if (!this.blockFocus) {
14462                 this.inputEl().focus();
14463             }
14464             
14465         }else {
14466             this.hasFocus = true;
14467             if(this.triggerAction == 'all') {
14468                 this.doQuery(this.allQuery, true);
14469             } else {
14470                 this.doQuery(this.getRawValue());
14471             }
14472             if (!this.blockFocus) {
14473                 this.inputEl().focus();
14474             }
14475         }
14476     },
14477     
14478     onTickableTriggerClick : function(e)
14479     {
14480         if(this.disabled){
14481             return;
14482         }
14483         
14484         this.page = 0;
14485         this.loadNext = false;
14486         this.hasFocus = true;
14487         
14488         if(this.triggerAction == 'all') {
14489             this.doQuery(this.allQuery, true);
14490         } else {
14491             this.doQuery(this.getRawValue());
14492         }
14493     },
14494     
14495     onSearchFieldClick : function(e)
14496     {
14497         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14498             this.onTickableFooterButtonClick(e, false, false);
14499             return;
14500         }
14501         
14502         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14503             return;
14504         }
14505         
14506         this.page = 0;
14507         this.loadNext = false;
14508         this.hasFocus = true;
14509         
14510         if(this.triggerAction == 'all') {
14511             this.doQuery(this.allQuery, true);
14512         } else {
14513             this.doQuery(this.getRawValue());
14514         }
14515     },
14516     
14517     listKeyPress : function(e)
14518     {
14519         //Roo.log('listkeypress');
14520         // scroll to first matching element based on key pres..
14521         if (e.isSpecialKey()) {
14522             return false;
14523         }
14524         var k = String.fromCharCode(e.getKey()).toUpperCase();
14525         //Roo.log(k);
14526         var match  = false;
14527         var csel = this.view.getSelectedNodes();
14528         var cselitem = false;
14529         if (csel.length) {
14530             var ix = this.view.indexOf(csel[0]);
14531             cselitem  = this.store.getAt(ix);
14532             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14533                 cselitem = false;
14534             }
14535             
14536         }
14537         
14538         this.store.each(function(v) { 
14539             if (cselitem) {
14540                 // start at existing selection.
14541                 if (cselitem.id == v.id) {
14542                     cselitem = false;
14543                 }
14544                 return true;
14545             }
14546                 
14547             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14548                 match = this.store.indexOf(v);
14549                 return false;
14550             }
14551             return true;
14552         }, this);
14553         
14554         if (match === false) {
14555             return true; // no more action?
14556         }
14557         // scroll to?
14558         this.view.select(match);
14559         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14560         sn.scrollIntoView(sn.dom.parentNode, false);
14561     },
14562     
14563     onViewScroll : function(e, t){
14564         
14565         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){
14566             return;
14567         }
14568         
14569         this.hasQuery = true;
14570         
14571         this.loading = this.list.select('.loading', true).first();
14572         
14573         if(this.loading === null){
14574             this.list.createChild({
14575                 tag: 'div',
14576                 cls: 'loading roo-select2-more-results roo-select2-active',
14577                 html: 'Loading more results...'
14578             });
14579             
14580             this.loading = this.list.select('.loading', true).first();
14581             
14582             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14583             
14584             this.loading.hide();
14585         }
14586         
14587         this.loading.show();
14588         
14589         var _combo = this;
14590         
14591         this.page++;
14592         this.loadNext = true;
14593         
14594         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14595         
14596         return;
14597     },
14598     
14599     addItem : function(o)
14600     {   
14601         var dv = ''; // display value
14602         
14603         if (this.displayField) {
14604             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14605         } else {
14606             // this is an error condition!!!
14607             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14608         }
14609         
14610         if(!dv.length){
14611             return;
14612         }
14613         
14614         var choice = this.choices.createChild({
14615             tag: 'li',
14616             cls: 'roo-select2-search-choice',
14617             cn: [
14618                 {
14619                     tag: 'div',
14620                     html: dv
14621                 },
14622                 {
14623                     tag: 'a',
14624                     href: '#',
14625                     cls: 'roo-select2-search-choice-close fa fa-times',
14626                     tabindex: '-1'
14627                 }
14628             ]
14629             
14630         }, this.searchField);
14631         
14632         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14633         
14634         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14635         
14636         this.item.push(o);
14637         
14638         this.lastData = o;
14639         
14640         this.syncValue();
14641         
14642         this.inputEl().dom.value = '';
14643         
14644         this.validate();
14645     },
14646     
14647     onRemoveItem : function(e, _self, o)
14648     {
14649         e.preventDefault();
14650         
14651         this.lastItem = Roo.apply([], this.item);
14652         
14653         var index = this.item.indexOf(o.data) * 1;
14654         
14655         if( index < 0){
14656             Roo.log('not this item?!');
14657             return;
14658         }
14659         
14660         this.item.splice(index, 1);
14661         o.item.remove();
14662         
14663         this.syncValue();
14664         
14665         this.fireEvent('remove', this, e);
14666         
14667         this.validate();
14668         
14669     },
14670     
14671     syncValue : function()
14672     {
14673         if(!this.item.length){
14674             this.clearValue();
14675             return;
14676         }
14677             
14678         var value = [];
14679         var _this = this;
14680         Roo.each(this.item, function(i){
14681             if(_this.valueField){
14682                 value.push(i[_this.valueField]);
14683                 return;
14684             }
14685
14686             value.push(i);
14687         });
14688
14689         this.value = value.join(',');
14690
14691         if(this.hiddenField){
14692             this.hiddenField.dom.value = this.value;
14693         }
14694         
14695         this.store.fireEvent("datachanged", this.store);
14696         
14697         this.validate();
14698     },
14699     
14700     clearItem : function()
14701     {
14702         if(!this.multiple){
14703             return;
14704         }
14705         
14706         this.item = [];
14707         
14708         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14709            c.remove();
14710         });
14711         
14712         this.syncValue();
14713         
14714         this.validate();
14715         
14716         if(this.tickable && !Roo.isTouch){
14717             this.view.refresh();
14718         }
14719     },
14720     
14721     inputEl: function ()
14722     {
14723         if(Roo.isIOS && this.useNativeIOS){
14724             return this.el.select('select.roo-ios-select', true).first();
14725         }
14726         
14727         if(Roo.isTouch && this.mobileTouchView){
14728             return this.el.select('input.form-control',true).first();
14729         }
14730         
14731         if(this.tickable){
14732             return this.searchField;
14733         }
14734         
14735         return this.el.select('input.form-control',true).first();
14736     },
14737     
14738     onTickableFooterButtonClick : function(e, btn, el)
14739     {
14740         e.preventDefault();
14741         
14742         this.lastItem = Roo.apply([], this.item);
14743         
14744         if(btn && btn.name == 'cancel'){
14745             this.tickItems = Roo.apply([], this.item);
14746             this.collapse();
14747             return;
14748         }
14749         
14750         this.clearItem();
14751         
14752         var _this = this;
14753         
14754         Roo.each(this.tickItems, function(o){
14755             _this.addItem(o);
14756         });
14757         
14758         this.collapse();
14759         
14760     },
14761     
14762     validate : function()
14763     {
14764         if(this.getVisibilityEl().hasClass('hidden')){
14765             return true;
14766         }
14767         
14768         var v = this.getRawValue();
14769         
14770         if(this.multiple){
14771             v = this.getValue();
14772         }
14773         
14774         if(this.disabled || this.allowBlank || v.length){
14775             this.markValid();
14776             return true;
14777         }
14778         
14779         this.markInvalid();
14780         return false;
14781     },
14782     
14783     tickableInputEl : function()
14784     {
14785         if(!this.tickable || !this.editable){
14786             return this.inputEl();
14787         }
14788         
14789         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14790     },
14791     
14792     
14793     getAutoCreateTouchView : function()
14794     {
14795         var id = Roo.id();
14796         
14797         var cfg = {
14798             cls: 'form-group' //input-group
14799         };
14800         
14801         var input =  {
14802             tag: 'input',
14803             id : id,
14804             type : this.inputType,
14805             cls : 'form-control x-combo-noedit',
14806             autocomplete: 'new-password',
14807             placeholder : this.placeholder || '',
14808             readonly : true
14809         };
14810         
14811         if (this.name) {
14812             input.name = this.name;
14813         }
14814         
14815         if (this.size) {
14816             input.cls += ' input-' + this.size;
14817         }
14818         
14819         if (this.disabled) {
14820             input.disabled = true;
14821         }
14822         
14823         var inputblock = {
14824             cls : '',
14825             cn : [
14826                 input
14827             ]
14828         };
14829         
14830         if(this.before){
14831             inputblock.cls += ' input-group';
14832             
14833             inputblock.cn.unshift({
14834                 tag :'span',
14835                 cls : 'input-group-addon',
14836                 html : this.before
14837             });
14838         }
14839         
14840         if(this.removable && !this.multiple){
14841             inputblock.cls += ' roo-removable';
14842             
14843             inputblock.cn.push({
14844                 tag: 'button',
14845                 html : 'x',
14846                 cls : 'roo-combo-removable-btn close'
14847             });
14848         }
14849
14850         if(this.hasFeedback && !this.allowBlank){
14851             
14852             inputblock.cls += ' has-feedback';
14853             
14854             inputblock.cn.push({
14855                 tag: 'span',
14856                 cls: 'glyphicon form-control-feedback'
14857             });
14858             
14859         }
14860         
14861         if (this.after) {
14862             
14863             inputblock.cls += (this.before) ? '' : ' input-group';
14864             
14865             inputblock.cn.push({
14866                 tag :'span',
14867                 cls : 'input-group-addon',
14868                 html : this.after
14869             });
14870         }
14871
14872         var box = {
14873             tag: 'div',
14874             cn: [
14875                 {
14876                     tag: 'input',
14877                     type : 'hidden',
14878                     cls: 'form-hidden-field'
14879                 },
14880                 inputblock
14881             ]
14882             
14883         };
14884         
14885         if(this.multiple){
14886             box = {
14887                 tag: 'div',
14888                 cn: [
14889                     {
14890                         tag: 'input',
14891                         type : 'hidden',
14892                         cls: 'form-hidden-field'
14893                     },
14894                     {
14895                         tag: 'ul',
14896                         cls: 'roo-select2-choices',
14897                         cn:[
14898                             {
14899                                 tag: 'li',
14900                                 cls: 'roo-select2-search-field',
14901                                 cn: [
14902
14903                                     inputblock
14904                                 ]
14905                             }
14906                         ]
14907                     }
14908                 ]
14909             }
14910         };
14911         
14912         var combobox = {
14913             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14914             cn: [
14915                 box
14916             ]
14917         };
14918         
14919         if(!this.multiple && this.showToggleBtn){
14920             
14921             var caret = {
14922                         tag: 'span',
14923                         cls: 'caret'
14924             };
14925             
14926             if (this.caret != false) {
14927                 caret = {
14928                      tag: 'i',
14929                      cls: 'fa fa-' + this.caret
14930                 };
14931                 
14932             }
14933             
14934             combobox.cn.push({
14935                 tag :'span',
14936                 cls : 'input-group-addon btn dropdown-toggle',
14937                 cn : [
14938                     caret,
14939                     {
14940                         tag: 'span',
14941                         cls: 'combobox-clear',
14942                         cn  : [
14943                             {
14944                                 tag : 'i',
14945                                 cls: 'icon-remove'
14946                             }
14947                         ]
14948                     }
14949                 ]
14950
14951             })
14952         }
14953         
14954         if(this.multiple){
14955             combobox.cls += ' roo-select2-container-multi';
14956         }
14957         
14958         var align = this.labelAlign || this.parentLabelAlign();
14959         
14960         if (align ==='left' && this.fieldLabel.length) {
14961
14962             cfg.cn = [
14963                 {
14964                    tag : 'i',
14965                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14966                    tooltip : 'This field is required'
14967                 },
14968                 {
14969                     tag: 'label',
14970                     cls : 'control-label',
14971                     html : this.fieldLabel
14972
14973                 },
14974                 {
14975                     cls : '', 
14976                     cn: [
14977                         combobox
14978                     ]
14979                 }
14980             ];
14981             
14982             var labelCfg = cfg.cn[1];
14983             var contentCfg = cfg.cn[2];
14984             
14985
14986             if(this.indicatorpos == 'right'){
14987                 cfg.cn = [
14988                     {
14989                         tag: 'label',
14990                         'for' :  id,
14991                         cls : 'control-label',
14992                         cn : [
14993                             {
14994                                 tag : 'span',
14995                                 html : this.fieldLabel
14996                             },
14997                             {
14998                                 tag : 'i',
14999                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15000                                 tooltip : 'This field is required'
15001                             }
15002                         ]
15003                     },
15004                     {
15005                         cls : "",
15006                         cn: [
15007                             combobox
15008                         ]
15009                     }
15010
15011                 ];
15012                 
15013                 labelCfg = cfg.cn[0];
15014                 contentCfg = cfg.cn[1];
15015             }
15016             
15017            
15018             
15019             if(this.labelWidth > 12){
15020                 labelCfg.style = "width: " + this.labelWidth + 'px';
15021             }
15022             
15023             if(this.labelWidth < 13 && this.labelmd == 0){
15024                 this.labelmd = this.labelWidth;
15025             }
15026             
15027             if(this.labellg > 0){
15028                 labelCfg.cls += ' col-lg-' + this.labellg;
15029                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15030             }
15031             
15032             if(this.labelmd > 0){
15033                 labelCfg.cls += ' col-md-' + this.labelmd;
15034                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15035             }
15036             
15037             if(this.labelsm > 0){
15038                 labelCfg.cls += ' col-sm-' + this.labelsm;
15039                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15040             }
15041             
15042             if(this.labelxs > 0){
15043                 labelCfg.cls += ' col-xs-' + this.labelxs;
15044                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15045             }
15046                 
15047                 
15048         } else if ( this.fieldLabel.length) {
15049             cfg.cn = [
15050                 {
15051                    tag : 'i',
15052                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15053                    tooltip : 'This field is required'
15054                 },
15055                 {
15056                     tag: 'label',
15057                     cls : 'control-label',
15058                     html : this.fieldLabel
15059
15060                 },
15061                 {
15062                     cls : '', 
15063                     cn: [
15064                         combobox
15065                     ]
15066                 }
15067             ];
15068             
15069             if(this.indicatorpos == 'right'){
15070                 cfg.cn = [
15071                     {
15072                         tag: 'label',
15073                         cls : 'control-label',
15074                         html : this.fieldLabel,
15075                         cn : [
15076                             {
15077                                tag : 'i',
15078                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15079                                tooltip : 'This field is required'
15080                             }
15081                         ]
15082                     },
15083                     {
15084                         cls : '', 
15085                         cn: [
15086                             combobox
15087                         ]
15088                     }
15089                 ];
15090             }
15091         } else {
15092             cfg.cn = combobox;    
15093         }
15094         
15095         
15096         var settings = this;
15097         
15098         ['xs','sm','md','lg'].map(function(size){
15099             if (settings[size]) {
15100                 cfg.cls += ' col-' + size + '-' + settings[size];
15101             }
15102         });
15103         
15104         return cfg;
15105     },
15106     
15107     initTouchView : function()
15108     {
15109         this.renderTouchView();
15110         
15111         this.touchViewEl.on('scroll', function(){
15112             this.el.dom.scrollTop = 0;
15113         }, this);
15114         
15115         this.originalValue = this.getValue();
15116         
15117         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15118         
15119         this.inputEl().on("click", this.showTouchView, this);
15120         if (this.triggerEl) {
15121             this.triggerEl.on("click", this.showTouchView, this);
15122         }
15123         
15124         
15125         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15126         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15127         
15128         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15129         
15130         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15131         this.store.on('load', this.onTouchViewLoad, this);
15132         this.store.on('loadexception', this.onTouchViewLoadException, this);
15133         
15134         if(this.hiddenName){
15135             
15136             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15137             
15138             this.hiddenField.dom.value =
15139                 this.hiddenValue !== undefined ? this.hiddenValue :
15140                 this.value !== undefined ? this.value : '';
15141         
15142             this.el.dom.removeAttribute('name');
15143             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15144         }
15145         
15146         if(this.multiple){
15147             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15148             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15149         }
15150         
15151         if(this.removable && !this.multiple){
15152             var close = this.closeTriggerEl();
15153             if(close){
15154                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15155                 close.on('click', this.removeBtnClick, this, close);
15156             }
15157         }
15158         /*
15159          * fix the bug in Safari iOS8
15160          */
15161         this.inputEl().on("focus", function(e){
15162             document.activeElement.blur();
15163         }, this);
15164         
15165         return;
15166         
15167         
15168     },
15169     
15170     renderTouchView : function()
15171     {
15172         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15173         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15174         
15175         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15176         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15177         
15178         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15179         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15180         this.touchViewBodyEl.setStyle('overflow', 'auto');
15181         
15182         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15183         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15184         
15185         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15186         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15187         
15188     },
15189     
15190     showTouchView : function()
15191     {
15192         if(this.disabled){
15193             return;
15194         }
15195         
15196         this.touchViewHeaderEl.hide();
15197
15198         if(this.modalTitle.length){
15199             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15200             this.touchViewHeaderEl.show();
15201         }
15202
15203         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15204         this.touchViewEl.show();
15205
15206         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15207         
15208         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15209         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15210
15211         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15212
15213         if(this.modalTitle.length){
15214             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15215         }
15216         
15217         this.touchViewBodyEl.setHeight(bodyHeight);
15218
15219         if(this.animate){
15220             var _this = this;
15221             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15222         }else{
15223             this.touchViewEl.addClass('in');
15224         }
15225
15226         this.doTouchViewQuery();
15227         
15228     },
15229     
15230     hideTouchView : function()
15231     {
15232         this.touchViewEl.removeClass('in');
15233
15234         if(this.animate){
15235             var _this = this;
15236             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15237         }else{
15238             this.touchViewEl.setStyle('display', 'none');
15239         }
15240         
15241     },
15242     
15243     setTouchViewValue : function()
15244     {
15245         if(this.multiple){
15246             this.clearItem();
15247         
15248             var _this = this;
15249
15250             Roo.each(this.tickItems, function(o){
15251                 this.addItem(o);
15252             }, this);
15253         }
15254         
15255         this.hideTouchView();
15256     },
15257     
15258     doTouchViewQuery : function()
15259     {
15260         var qe = {
15261             query: '',
15262             forceAll: true,
15263             combo: this,
15264             cancel:false
15265         };
15266         
15267         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15268             return false;
15269         }
15270         
15271         if(!this.alwaysQuery || this.mode == 'local'){
15272             this.onTouchViewLoad();
15273             return;
15274         }
15275         
15276         this.store.load();
15277     },
15278     
15279     onTouchViewBeforeLoad : function(combo,opts)
15280     {
15281         return;
15282     },
15283
15284     // private
15285     onTouchViewLoad : function()
15286     {
15287         if(this.store.getCount() < 1){
15288             this.onTouchViewEmptyResults();
15289             return;
15290         }
15291         
15292         this.clearTouchView();
15293         
15294         var rawValue = this.getRawValue();
15295         
15296         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15297         
15298         this.tickItems = [];
15299         
15300         this.store.data.each(function(d, rowIndex){
15301             var row = this.touchViewListGroup.createChild(template);
15302             
15303             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15304                 row.addClass(d.data.cls);
15305             }
15306             
15307             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15308                 var cfg = {
15309                     data : d.data,
15310                     html : d.data[this.displayField]
15311                 };
15312                 
15313                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15314                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15315                 }
15316             }
15317             row.removeClass('selected');
15318             if(!this.multiple && this.valueField &&
15319                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15320             {
15321                 // radio buttons..
15322                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15323                 row.addClass('selected');
15324             }
15325             
15326             if(this.multiple && this.valueField &&
15327                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15328             {
15329                 
15330                 // checkboxes...
15331                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15332                 this.tickItems.push(d.data);
15333             }
15334             
15335             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15336             
15337         }, this);
15338         
15339         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15340         
15341         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15342
15343         if(this.modalTitle.length){
15344             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15345         }
15346
15347         var listHeight = this.touchViewListGroup.getHeight();
15348         
15349         var _this = this;
15350         
15351         if(firstChecked && listHeight > bodyHeight){
15352             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15353         }
15354         
15355     },
15356     
15357     onTouchViewLoadException : function()
15358     {
15359         this.hideTouchView();
15360     },
15361     
15362     onTouchViewEmptyResults : function()
15363     {
15364         this.clearTouchView();
15365         
15366         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15367         
15368         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15369         
15370     },
15371     
15372     clearTouchView : function()
15373     {
15374         this.touchViewListGroup.dom.innerHTML = '';
15375     },
15376     
15377     onTouchViewClick : function(e, el, o)
15378     {
15379         e.preventDefault();
15380         
15381         var row = o.row;
15382         var rowIndex = o.rowIndex;
15383         
15384         var r = this.store.getAt(rowIndex);
15385         
15386         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15387             
15388             if(!this.multiple){
15389                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15390                     c.dom.removeAttribute('checked');
15391                 }, this);
15392
15393                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15394
15395                 this.setFromData(r.data);
15396
15397                 var close = this.closeTriggerEl();
15398
15399                 if(close){
15400                     close.show();
15401                 }
15402
15403                 this.hideTouchView();
15404
15405                 this.fireEvent('select', this, r, rowIndex);
15406
15407                 return;
15408             }
15409
15410             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15411                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15412                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15413                 return;
15414             }
15415
15416             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15417             this.addItem(r.data);
15418             this.tickItems.push(r.data);
15419         }
15420     },
15421     
15422     getAutoCreateNativeIOS : function()
15423     {
15424         var cfg = {
15425             cls: 'form-group' //input-group,
15426         };
15427         
15428         var combobox =  {
15429             tag: 'select',
15430             cls : 'roo-ios-select'
15431         };
15432         
15433         if (this.name) {
15434             combobox.name = this.name;
15435         }
15436         
15437         if (this.disabled) {
15438             combobox.disabled = true;
15439         }
15440         
15441         var settings = this;
15442         
15443         ['xs','sm','md','lg'].map(function(size){
15444             if (settings[size]) {
15445                 cfg.cls += ' col-' + size + '-' + settings[size];
15446             }
15447         });
15448         
15449         cfg.cn = combobox;
15450         
15451         return cfg;
15452         
15453     },
15454     
15455     initIOSView : function()
15456     {
15457         this.store.on('load', this.onIOSViewLoad, this);
15458         
15459         return;
15460     },
15461     
15462     onIOSViewLoad : function()
15463     {
15464         if(this.store.getCount() < 1){
15465             return;
15466         }
15467         
15468         this.clearIOSView();
15469         
15470         if(this.allowBlank) {
15471             
15472             var default_text = '-- SELECT --';
15473             
15474             if(this.placeholder.length){
15475                 default_text = this.placeholder;
15476             }
15477             
15478             if(this.emptyTitle.length){
15479                 default_text += ' - ' + this.emptyTitle + ' -';
15480             }
15481             
15482             var opt = this.inputEl().createChild({
15483                 tag: 'option',
15484                 value : 0,
15485                 html : default_text
15486             });
15487             
15488             var o = {};
15489             o[this.valueField] = 0;
15490             o[this.displayField] = default_text;
15491             
15492             this.ios_options.push({
15493                 data : o,
15494                 el : opt
15495             });
15496             
15497         }
15498         
15499         this.store.data.each(function(d, rowIndex){
15500             
15501             var html = '';
15502             
15503             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15504                 html = d.data[this.displayField];
15505             }
15506             
15507             var value = '';
15508             
15509             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15510                 value = d.data[this.valueField];
15511             }
15512             
15513             var option = {
15514                 tag: 'option',
15515                 value : value,
15516                 html : html
15517             };
15518             
15519             if(this.value == d.data[this.valueField]){
15520                 option['selected'] = true;
15521             }
15522             
15523             var opt = this.inputEl().createChild(option);
15524             
15525             this.ios_options.push({
15526                 data : d.data,
15527                 el : opt
15528             });
15529             
15530         }, this);
15531         
15532         this.inputEl().on('change', function(){
15533            this.fireEvent('select', this);
15534         }, this);
15535         
15536     },
15537     
15538     clearIOSView: function()
15539     {
15540         this.inputEl().dom.innerHTML = '';
15541         
15542         this.ios_options = [];
15543     },
15544     
15545     setIOSValue: function(v)
15546     {
15547         this.value = v;
15548         
15549         if(!this.ios_options){
15550             return;
15551         }
15552         
15553         Roo.each(this.ios_options, function(opts){
15554            
15555            opts.el.dom.removeAttribute('selected');
15556            
15557            if(opts.data[this.valueField] != v){
15558                return;
15559            }
15560            
15561            opts.el.dom.setAttribute('selected', true);
15562            
15563         }, this);
15564     }
15565
15566     /** 
15567     * @cfg {Boolean} grow 
15568     * @hide 
15569     */
15570     /** 
15571     * @cfg {Number} growMin 
15572     * @hide 
15573     */
15574     /** 
15575     * @cfg {Number} growMax 
15576     * @hide 
15577     */
15578     /**
15579      * @hide
15580      * @method autoSize
15581      */
15582 });
15583
15584 Roo.apply(Roo.bootstrap.ComboBox,  {
15585     
15586     header : {
15587         tag: 'div',
15588         cls: 'modal-header',
15589         cn: [
15590             {
15591                 tag: 'h4',
15592                 cls: 'modal-title'
15593             }
15594         ]
15595     },
15596     
15597     body : {
15598         tag: 'div',
15599         cls: 'modal-body',
15600         cn: [
15601             {
15602                 tag: 'ul',
15603                 cls: 'list-group'
15604             }
15605         ]
15606     },
15607     
15608     listItemRadio : {
15609         tag: 'li',
15610         cls: 'list-group-item',
15611         cn: [
15612             {
15613                 tag: 'span',
15614                 cls: 'roo-combobox-list-group-item-value'
15615             },
15616             {
15617                 tag: 'div',
15618                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15619                 cn: [
15620                     {
15621                         tag: 'input',
15622                         type: 'radio'
15623                     },
15624                     {
15625                         tag: 'label'
15626                     }
15627                 ]
15628             }
15629         ]
15630     },
15631     
15632     listItemCheckbox : {
15633         tag: 'li',
15634         cls: 'list-group-item',
15635         cn: [
15636             {
15637                 tag: 'span',
15638                 cls: 'roo-combobox-list-group-item-value'
15639             },
15640             {
15641                 tag: 'div',
15642                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15643                 cn: [
15644                     {
15645                         tag: 'input',
15646                         type: 'checkbox'
15647                     },
15648                     {
15649                         tag: 'label'
15650                     }
15651                 ]
15652             }
15653         ]
15654     },
15655     
15656     emptyResult : {
15657         tag: 'div',
15658         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15659     },
15660     
15661     footer : {
15662         tag: 'div',
15663         cls: 'modal-footer',
15664         cn: [
15665             {
15666                 tag: 'div',
15667                 cls: 'row',
15668                 cn: [
15669                     {
15670                         tag: 'div',
15671                         cls: 'col-xs-6 text-left',
15672                         cn: {
15673                             tag: 'button',
15674                             cls: 'btn btn-danger roo-touch-view-cancel',
15675                             html: 'Cancel'
15676                         }
15677                     },
15678                     {
15679                         tag: 'div',
15680                         cls: 'col-xs-6 text-right',
15681                         cn: {
15682                             tag: 'button',
15683                             cls: 'btn btn-success roo-touch-view-ok',
15684                             html: 'OK'
15685                         }
15686                     }
15687                 ]
15688             }
15689         ]
15690         
15691     }
15692 });
15693
15694 Roo.apply(Roo.bootstrap.ComboBox,  {
15695     
15696     touchViewTemplate : {
15697         tag: 'div',
15698         cls: 'modal fade roo-combobox-touch-view',
15699         cn: [
15700             {
15701                 tag: 'div',
15702                 cls: 'modal-dialog',
15703                 style : 'position:fixed', // we have to fix position....
15704                 cn: [
15705                     {
15706                         tag: 'div',
15707                         cls: 'modal-content',
15708                         cn: [
15709                             Roo.bootstrap.ComboBox.header,
15710                             Roo.bootstrap.ComboBox.body,
15711                             Roo.bootstrap.ComboBox.footer
15712                         ]
15713                     }
15714                 ]
15715             }
15716         ]
15717     }
15718 });/*
15719  * Based on:
15720  * Ext JS Library 1.1.1
15721  * Copyright(c) 2006-2007, Ext JS, LLC.
15722  *
15723  * Originally Released Under LGPL - original licence link has changed is not relivant.
15724  *
15725  * Fork - LGPL
15726  * <script type="text/javascript">
15727  */
15728
15729 /**
15730  * @class Roo.View
15731  * @extends Roo.util.Observable
15732  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15733  * This class also supports single and multi selection modes. <br>
15734  * Create a data model bound view:
15735  <pre><code>
15736  var store = new Roo.data.Store(...);
15737
15738  var view = new Roo.View({
15739     el : "my-element",
15740     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15741  
15742     singleSelect: true,
15743     selectedClass: "ydataview-selected",
15744     store: store
15745  });
15746
15747  // listen for node click?
15748  view.on("click", function(vw, index, node, e){
15749  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15750  });
15751
15752  // load XML data
15753  dataModel.load("foobar.xml");
15754  </code></pre>
15755  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15756  * <br><br>
15757  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15758  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15759  * 
15760  * Note: old style constructor is still suported (container, template, config)
15761  * 
15762  * @constructor
15763  * Create a new View
15764  * @param {Object} config The config object
15765  * 
15766  */
15767 Roo.View = function(config, depreciated_tpl, depreciated_config){
15768     
15769     this.parent = false;
15770     
15771     if (typeof(depreciated_tpl) == 'undefined') {
15772         // new way.. - universal constructor.
15773         Roo.apply(this, config);
15774         this.el  = Roo.get(this.el);
15775     } else {
15776         // old format..
15777         this.el  = Roo.get(config);
15778         this.tpl = depreciated_tpl;
15779         Roo.apply(this, depreciated_config);
15780     }
15781     this.wrapEl  = this.el.wrap().wrap();
15782     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15783     
15784     
15785     if(typeof(this.tpl) == "string"){
15786         this.tpl = new Roo.Template(this.tpl);
15787     } else {
15788         // support xtype ctors..
15789         this.tpl = new Roo.factory(this.tpl, Roo);
15790     }
15791     
15792     
15793     this.tpl.compile();
15794     
15795     /** @private */
15796     this.addEvents({
15797         /**
15798          * @event beforeclick
15799          * Fires before a click is processed. Returns false to cancel the default action.
15800          * @param {Roo.View} this
15801          * @param {Number} index The index of the target node
15802          * @param {HTMLElement} node The target node
15803          * @param {Roo.EventObject} e The raw event object
15804          */
15805             "beforeclick" : true,
15806         /**
15807          * @event click
15808          * Fires when a template node is clicked.
15809          * @param {Roo.View} this
15810          * @param {Number} index The index of the target node
15811          * @param {HTMLElement} node The target node
15812          * @param {Roo.EventObject} e The raw event object
15813          */
15814             "click" : true,
15815         /**
15816          * @event dblclick
15817          * Fires when a template node is double clicked.
15818          * @param {Roo.View} this
15819          * @param {Number} index The index of the target node
15820          * @param {HTMLElement} node The target node
15821          * @param {Roo.EventObject} e The raw event object
15822          */
15823             "dblclick" : true,
15824         /**
15825          * @event contextmenu
15826          * Fires when a template node is right clicked.
15827          * @param {Roo.View} this
15828          * @param {Number} index The index of the target node
15829          * @param {HTMLElement} node The target node
15830          * @param {Roo.EventObject} e The raw event object
15831          */
15832             "contextmenu" : true,
15833         /**
15834          * @event selectionchange
15835          * Fires when the selected nodes change.
15836          * @param {Roo.View} this
15837          * @param {Array} selections Array of the selected nodes
15838          */
15839             "selectionchange" : true,
15840     
15841         /**
15842          * @event beforeselect
15843          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15844          * @param {Roo.View} this
15845          * @param {HTMLElement} node The node to be selected
15846          * @param {Array} selections Array of currently selected nodes
15847          */
15848             "beforeselect" : true,
15849         /**
15850          * @event preparedata
15851          * Fires on every row to render, to allow you to change the data.
15852          * @param {Roo.View} this
15853          * @param {Object} data to be rendered (change this)
15854          */
15855           "preparedata" : true
15856           
15857           
15858         });
15859
15860
15861
15862     this.el.on({
15863         "click": this.onClick,
15864         "dblclick": this.onDblClick,
15865         "contextmenu": this.onContextMenu,
15866         scope:this
15867     });
15868
15869     this.selections = [];
15870     this.nodes = [];
15871     this.cmp = new Roo.CompositeElementLite([]);
15872     if(this.store){
15873         this.store = Roo.factory(this.store, Roo.data);
15874         this.setStore(this.store, true);
15875     }
15876     
15877     if ( this.footer && this.footer.xtype) {
15878            
15879          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15880         
15881         this.footer.dataSource = this.store;
15882         this.footer.container = fctr;
15883         this.footer = Roo.factory(this.footer, Roo);
15884         fctr.insertFirst(this.el);
15885         
15886         // this is a bit insane - as the paging toolbar seems to detach the el..
15887 //        dom.parentNode.parentNode.parentNode
15888          // they get detached?
15889     }
15890     
15891     
15892     Roo.View.superclass.constructor.call(this);
15893     
15894     
15895 };
15896
15897 Roo.extend(Roo.View, Roo.util.Observable, {
15898     
15899      /**
15900      * @cfg {Roo.data.Store} store Data store to load data from.
15901      */
15902     store : false,
15903     
15904     /**
15905      * @cfg {String|Roo.Element} el The container element.
15906      */
15907     el : '',
15908     
15909     /**
15910      * @cfg {String|Roo.Template} tpl The template used by this View 
15911      */
15912     tpl : false,
15913     /**
15914      * @cfg {String} dataName the named area of the template to use as the data area
15915      *                          Works with domtemplates roo-name="name"
15916      */
15917     dataName: false,
15918     /**
15919      * @cfg {String} selectedClass The css class to add to selected nodes
15920      */
15921     selectedClass : "x-view-selected",
15922      /**
15923      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15924      */
15925     emptyText : "",
15926     
15927     /**
15928      * @cfg {String} text to display on mask (default Loading)
15929      */
15930     mask : false,
15931     /**
15932      * @cfg {Boolean} multiSelect Allow multiple selection
15933      */
15934     multiSelect : false,
15935     /**
15936      * @cfg {Boolean} singleSelect Allow single selection
15937      */
15938     singleSelect:  false,
15939     
15940     /**
15941      * @cfg {Boolean} toggleSelect - selecting 
15942      */
15943     toggleSelect : false,
15944     
15945     /**
15946      * @cfg {Boolean} tickable - selecting 
15947      */
15948     tickable : false,
15949     
15950     /**
15951      * Returns the element this view is bound to.
15952      * @return {Roo.Element}
15953      */
15954     getEl : function(){
15955         return this.wrapEl;
15956     },
15957     
15958     
15959
15960     /**
15961      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15962      */
15963     refresh : function(){
15964         //Roo.log('refresh');
15965         var t = this.tpl;
15966         
15967         // if we are using something like 'domtemplate', then
15968         // the what gets used is:
15969         // t.applySubtemplate(NAME, data, wrapping data..)
15970         // the outer template then get' applied with
15971         //     the store 'extra data'
15972         // and the body get's added to the
15973         //      roo-name="data" node?
15974         //      <span class='roo-tpl-{name}'></span> ?????
15975         
15976         
15977         
15978         this.clearSelections();
15979         this.el.update("");
15980         var html = [];
15981         var records = this.store.getRange();
15982         if(records.length < 1) {
15983             
15984             // is this valid??  = should it render a template??
15985             
15986             this.el.update(this.emptyText);
15987             return;
15988         }
15989         var el = this.el;
15990         if (this.dataName) {
15991             this.el.update(t.apply(this.store.meta)); //????
15992             el = this.el.child('.roo-tpl-' + this.dataName);
15993         }
15994         
15995         for(var i = 0, len = records.length; i < len; i++){
15996             var data = this.prepareData(records[i].data, i, records[i]);
15997             this.fireEvent("preparedata", this, data, i, records[i]);
15998             
15999             var d = Roo.apply({}, data);
16000             
16001             if(this.tickable){
16002                 Roo.apply(d, {'roo-id' : Roo.id()});
16003                 
16004                 var _this = this;
16005             
16006                 Roo.each(this.parent.item, function(item){
16007                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16008                         return;
16009                     }
16010                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16011                 });
16012             }
16013             
16014             html[html.length] = Roo.util.Format.trim(
16015                 this.dataName ?
16016                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16017                     t.apply(d)
16018             );
16019         }
16020         
16021         
16022         
16023         el.update(html.join(""));
16024         this.nodes = el.dom.childNodes;
16025         this.updateIndexes(0);
16026     },
16027     
16028
16029     /**
16030      * Function to override to reformat the data that is sent to
16031      * the template for each node.
16032      * DEPRICATED - use the preparedata event handler.
16033      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16034      * a JSON object for an UpdateManager bound view).
16035      */
16036     prepareData : function(data, index, record)
16037     {
16038         this.fireEvent("preparedata", this, data, index, record);
16039         return data;
16040     },
16041
16042     onUpdate : function(ds, record){
16043         // Roo.log('on update');   
16044         this.clearSelections();
16045         var index = this.store.indexOf(record);
16046         var n = this.nodes[index];
16047         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16048         n.parentNode.removeChild(n);
16049         this.updateIndexes(index, index);
16050     },
16051
16052     
16053     
16054 // --------- FIXME     
16055     onAdd : function(ds, records, index)
16056     {
16057         //Roo.log(['on Add', ds, records, index] );        
16058         this.clearSelections();
16059         if(this.nodes.length == 0){
16060             this.refresh();
16061             return;
16062         }
16063         var n = this.nodes[index];
16064         for(var i = 0, len = records.length; i < len; i++){
16065             var d = this.prepareData(records[i].data, i, records[i]);
16066             if(n){
16067                 this.tpl.insertBefore(n, d);
16068             }else{
16069                 
16070                 this.tpl.append(this.el, d);
16071             }
16072         }
16073         this.updateIndexes(index);
16074     },
16075
16076     onRemove : function(ds, record, index){
16077        // Roo.log('onRemove');
16078         this.clearSelections();
16079         var el = this.dataName  ?
16080             this.el.child('.roo-tpl-' + this.dataName) :
16081             this.el; 
16082         
16083         el.dom.removeChild(this.nodes[index]);
16084         this.updateIndexes(index);
16085     },
16086
16087     /**
16088      * Refresh an individual node.
16089      * @param {Number} index
16090      */
16091     refreshNode : function(index){
16092         this.onUpdate(this.store, this.store.getAt(index));
16093     },
16094
16095     updateIndexes : function(startIndex, endIndex){
16096         var ns = this.nodes;
16097         startIndex = startIndex || 0;
16098         endIndex = endIndex || ns.length - 1;
16099         for(var i = startIndex; i <= endIndex; i++){
16100             ns[i].nodeIndex = i;
16101         }
16102     },
16103
16104     /**
16105      * Changes the data store this view uses and refresh the view.
16106      * @param {Store} store
16107      */
16108     setStore : function(store, initial){
16109         if(!initial && this.store){
16110             this.store.un("datachanged", this.refresh);
16111             this.store.un("add", this.onAdd);
16112             this.store.un("remove", this.onRemove);
16113             this.store.un("update", this.onUpdate);
16114             this.store.un("clear", this.refresh);
16115             this.store.un("beforeload", this.onBeforeLoad);
16116             this.store.un("load", this.onLoad);
16117             this.store.un("loadexception", this.onLoad);
16118         }
16119         if(store){
16120           
16121             store.on("datachanged", this.refresh, this);
16122             store.on("add", this.onAdd, this);
16123             store.on("remove", this.onRemove, this);
16124             store.on("update", this.onUpdate, this);
16125             store.on("clear", this.refresh, this);
16126             store.on("beforeload", this.onBeforeLoad, this);
16127             store.on("load", this.onLoad, this);
16128             store.on("loadexception", this.onLoad, this);
16129         }
16130         
16131         if(store){
16132             this.refresh();
16133         }
16134     },
16135     /**
16136      * onbeforeLoad - masks the loading area.
16137      *
16138      */
16139     onBeforeLoad : function(store,opts)
16140     {
16141          //Roo.log('onBeforeLoad');   
16142         if (!opts.add) {
16143             this.el.update("");
16144         }
16145         this.el.mask(this.mask ? this.mask : "Loading" ); 
16146     },
16147     onLoad : function ()
16148     {
16149         this.el.unmask();
16150     },
16151     
16152
16153     /**
16154      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16155      * @param {HTMLElement} node
16156      * @return {HTMLElement} The template node
16157      */
16158     findItemFromChild : function(node){
16159         var el = this.dataName  ?
16160             this.el.child('.roo-tpl-' + this.dataName,true) :
16161             this.el.dom; 
16162         
16163         if(!node || node.parentNode == el){
16164                     return node;
16165             }
16166             var p = node.parentNode;
16167             while(p && p != el){
16168             if(p.parentNode == el){
16169                 return p;
16170             }
16171             p = p.parentNode;
16172         }
16173             return null;
16174     },
16175
16176     /** @ignore */
16177     onClick : function(e){
16178         var item = this.findItemFromChild(e.getTarget());
16179         if(item){
16180             var index = this.indexOf(item);
16181             if(this.onItemClick(item, index, e) !== false){
16182                 this.fireEvent("click", this, index, item, e);
16183             }
16184         }else{
16185             this.clearSelections();
16186         }
16187     },
16188
16189     /** @ignore */
16190     onContextMenu : function(e){
16191         var item = this.findItemFromChild(e.getTarget());
16192         if(item){
16193             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16194         }
16195     },
16196
16197     /** @ignore */
16198     onDblClick : function(e){
16199         var item = this.findItemFromChild(e.getTarget());
16200         if(item){
16201             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16202         }
16203     },
16204
16205     onItemClick : function(item, index, e)
16206     {
16207         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16208             return false;
16209         }
16210         if (this.toggleSelect) {
16211             var m = this.isSelected(item) ? 'unselect' : 'select';
16212             //Roo.log(m);
16213             var _t = this;
16214             _t[m](item, true, false);
16215             return true;
16216         }
16217         if(this.multiSelect || this.singleSelect){
16218             if(this.multiSelect && e.shiftKey && this.lastSelection){
16219                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16220             }else{
16221                 this.select(item, this.multiSelect && e.ctrlKey);
16222                 this.lastSelection = item;
16223             }
16224             
16225             if(!this.tickable){
16226                 e.preventDefault();
16227             }
16228             
16229         }
16230         return true;
16231     },
16232
16233     /**
16234      * Get the number of selected nodes.
16235      * @return {Number}
16236      */
16237     getSelectionCount : function(){
16238         return this.selections.length;
16239     },
16240
16241     /**
16242      * Get the currently selected nodes.
16243      * @return {Array} An array of HTMLElements
16244      */
16245     getSelectedNodes : function(){
16246         return this.selections;
16247     },
16248
16249     /**
16250      * Get the indexes of the selected nodes.
16251      * @return {Array}
16252      */
16253     getSelectedIndexes : function(){
16254         var indexes = [], s = this.selections;
16255         for(var i = 0, len = s.length; i < len; i++){
16256             indexes.push(s[i].nodeIndex);
16257         }
16258         return indexes;
16259     },
16260
16261     /**
16262      * Clear all selections
16263      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16264      */
16265     clearSelections : function(suppressEvent){
16266         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16267             this.cmp.elements = this.selections;
16268             this.cmp.removeClass(this.selectedClass);
16269             this.selections = [];
16270             if(!suppressEvent){
16271                 this.fireEvent("selectionchange", this, this.selections);
16272             }
16273         }
16274     },
16275
16276     /**
16277      * Returns true if the passed node is selected
16278      * @param {HTMLElement/Number} node The node or node index
16279      * @return {Boolean}
16280      */
16281     isSelected : function(node){
16282         var s = this.selections;
16283         if(s.length < 1){
16284             return false;
16285         }
16286         node = this.getNode(node);
16287         return s.indexOf(node) !== -1;
16288     },
16289
16290     /**
16291      * Selects nodes.
16292      * @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
16293      * @param {Boolean} keepExisting (optional) true to keep existing selections
16294      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16295      */
16296     select : function(nodeInfo, keepExisting, suppressEvent){
16297         if(nodeInfo instanceof Array){
16298             if(!keepExisting){
16299                 this.clearSelections(true);
16300             }
16301             for(var i = 0, len = nodeInfo.length; i < len; i++){
16302                 this.select(nodeInfo[i], true, true);
16303             }
16304             return;
16305         } 
16306         var node = this.getNode(nodeInfo);
16307         if(!node || this.isSelected(node)){
16308             return; // already selected.
16309         }
16310         if(!keepExisting){
16311             this.clearSelections(true);
16312         }
16313         
16314         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16315             Roo.fly(node).addClass(this.selectedClass);
16316             this.selections.push(node);
16317             if(!suppressEvent){
16318                 this.fireEvent("selectionchange", this, this.selections);
16319             }
16320         }
16321         
16322         
16323     },
16324       /**
16325      * Unselects nodes.
16326      * @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
16327      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16328      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16329      */
16330     unselect : function(nodeInfo, keepExisting, suppressEvent)
16331     {
16332         if(nodeInfo instanceof Array){
16333             Roo.each(this.selections, function(s) {
16334                 this.unselect(s, nodeInfo);
16335             }, this);
16336             return;
16337         }
16338         var node = this.getNode(nodeInfo);
16339         if(!node || !this.isSelected(node)){
16340             //Roo.log("not selected");
16341             return; // not selected.
16342         }
16343         // fireevent???
16344         var ns = [];
16345         Roo.each(this.selections, function(s) {
16346             if (s == node ) {
16347                 Roo.fly(node).removeClass(this.selectedClass);
16348
16349                 return;
16350             }
16351             ns.push(s);
16352         },this);
16353         
16354         this.selections= ns;
16355         this.fireEvent("selectionchange", this, this.selections);
16356     },
16357
16358     /**
16359      * Gets a template node.
16360      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16361      * @return {HTMLElement} The node or null if it wasn't found
16362      */
16363     getNode : function(nodeInfo){
16364         if(typeof nodeInfo == "string"){
16365             return document.getElementById(nodeInfo);
16366         }else if(typeof nodeInfo == "number"){
16367             return this.nodes[nodeInfo];
16368         }
16369         return nodeInfo;
16370     },
16371
16372     /**
16373      * Gets a range template nodes.
16374      * @param {Number} startIndex
16375      * @param {Number} endIndex
16376      * @return {Array} An array of nodes
16377      */
16378     getNodes : function(start, end){
16379         var ns = this.nodes;
16380         start = start || 0;
16381         end = typeof end == "undefined" ? ns.length - 1 : end;
16382         var nodes = [];
16383         if(start <= end){
16384             for(var i = start; i <= end; i++){
16385                 nodes.push(ns[i]);
16386             }
16387         } else{
16388             for(var i = start; i >= end; i--){
16389                 nodes.push(ns[i]);
16390             }
16391         }
16392         return nodes;
16393     },
16394
16395     /**
16396      * Finds the index of the passed node
16397      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16398      * @return {Number} The index of the node or -1
16399      */
16400     indexOf : function(node){
16401         node = this.getNode(node);
16402         if(typeof node.nodeIndex == "number"){
16403             return node.nodeIndex;
16404         }
16405         var ns = this.nodes;
16406         for(var i = 0, len = ns.length; i < len; i++){
16407             if(ns[i] == node){
16408                 return i;
16409             }
16410         }
16411         return -1;
16412     }
16413 });
16414 /*
16415  * - LGPL
16416  *
16417  * based on jquery fullcalendar
16418  * 
16419  */
16420
16421 Roo.bootstrap = Roo.bootstrap || {};
16422 /**
16423  * @class Roo.bootstrap.Calendar
16424  * @extends Roo.bootstrap.Component
16425  * Bootstrap Calendar class
16426  * @cfg {Boolean} loadMask (true|false) default false
16427  * @cfg {Object} header generate the user specific header of the calendar, default false
16428
16429  * @constructor
16430  * Create a new Container
16431  * @param {Object} config The config object
16432  */
16433
16434
16435
16436 Roo.bootstrap.Calendar = function(config){
16437     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16438      this.addEvents({
16439         /**
16440              * @event select
16441              * Fires when a date is selected
16442              * @param {DatePicker} this
16443              * @param {Date} date The selected date
16444              */
16445         'select': true,
16446         /**
16447              * @event monthchange
16448              * Fires when the displayed month changes 
16449              * @param {DatePicker} this
16450              * @param {Date} date The selected month
16451              */
16452         'monthchange': true,
16453         /**
16454              * @event evententer
16455              * Fires when mouse over an event
16456              * @param {Calendar} this
16457              * @param {event} Event
16458              */
16459         'evententer': true,
16460         /**
16461              * @event eventleave
16462              * Fires when the mouse leaves an
16463              * @param {Calendar} this
16464              * @param {event}
16465              */
16466         'eventleave': true,
16467         /**
16468              * @event eventclick
16469              * Fires when the mouse click an
16470              * @param {Calendar} this
16471              * @param {event}
16472              */
16473         'eventclick': true
16474         
16475     });
16476
16477 };
16478
16479 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16480     
16481      /**
16482      * @cfg {Number} startDay
16483      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16484      */
16485     startDay : 0,
16486     
16487     loadMask : false,
16488     
16489     header : false,
16490       
16491     getAutoCreate : function(){
16492         
16493         
16494         var fc_button = function(name, corner, style, content ) {
16495             return Roo.apply({},{
16496                 tag : 'span',
16497                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16498                          (corner.length ?
16499                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16500                             ''
16501                         ),
16502                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16503                 unselectable: 'on'
16504             });
16505         };
16506         
16507         var header = {};
16508         
16509         if(!this.header){
16510             header = {
16511                 tag : 'table',
16512                 cls : 'fc-header',
16513                 style : 'width:100%',
16514                 cn : [
16515                     {
16516                         tag: 'tr',
16517                         cn : [
16518                             {
16519                                 tag : 'td',
16520                                 cls : 'fc-header-left',
16521                                 cn : [
16522                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16523                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16524                                     { tag: 'span', cls: 'fc-header-space' },
16525                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16526
16527
16528                                 ]
16529                             },
16530
16531                             {
16532                                 tag : 'td',
16533                                 cls : 'fc-header-center',
16534                                 cn : [
16535                                     {
16536                                         tag: 'span',
16537                                         cls: 'fc-header-title',
16538                                         cn : {
16539                                             tag: 'H2',
16540                                             html : 'month / year'
16541                                         }
16542                                     }
16543
16544                                 ]
16545                             },
16546                             {
16547                                 tag : 'td',
16548                                 cls : 'fc-header-right',
16549                                 cn : [
16550                               /*      fc_button('month', 'left', '', 'month' ),
16551                                     fc_button('week', '', '', 'week' ),
16552                                     fc_button('day', 'right', '', 'day' )
16553                                 */    
16554
16555                                 ]
16556                             }
16557
16558                         ]
16559                     }
16560                 ]
16561             };
16562         }
16563         
16564         header = this.header;
16565         
16566        
16567         var cal_heads = function() {
16568             var ret = [];
16569             // fixme - handle this.
16570             
16571             for (var i =0; i < Date.dayNames.length; i++) {
16572                 var d = Date.dayNames[i];
16573                 ret.push({
16574                     tag: 'th',
16575                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16576                     html : d.substring(0,3)
16577                 });
16578                 
16579             }
16580             ret[0].cls += ' fc-first';
16581             ret[6].cls += ' fc-last';
16582             return ret;
16583         };
16584         var cal_cell = function(n) {
16585             return  {
16586                 tag: 'td',
16587                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16588                 cn : [
16589                     {
16590                         cn : [
16591                             {
16592                                 cls: 'fc-day-number',
16593                                 html: 'D'
16594                             },
16595                             {
16596                                 cls: 'fc-day-content',
16597                              
16598                                 cn : [
16599                                      {
16600                                         style: 'position: relative;' // height: 17px;
16601                                     }
16602                                 ]
16603                             }
16604                             
16605                             
16606                         ]
16607                     }
16608                 ]
16609                 
16610             }
16611         };
16612         var cal_rows = function() {
16613             
16614             var ret = [];
16615             for (var r = 0; r < 6; r++) {
16616                 var row= {
16617                     tag : 'tr',
16618                     cls : 'fc-week',
16619                     cn : []
16620                 };
16621                 
16622                 for (var i =0; i < Date.dayNames.length; i++) {
16623                     var d = Date.dayNames[i];
16624                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16625
16626                 }
16627                 row.cn[0].cls+=' fc-first';
16628                 row.cn[0].cn[0].style = 'min-height:90px';
16629                 row.cn[6].cls+=' fc-last';
16630                 ret.push(row);
16631                 
16632             }
16633             ret[0].cls += ' fc-first';
16634             ret[4].cls += ' fc-prev-last';
16635             ret[5].cls += ' fc-last';
16636             return ret;
16637             
16638         };
16639         
16640         var cal_table = {
16641             tag: 'table',
16642             cls: 'fc-border-separate',
16643             style : 'width:100%',
16644             cellspacing  : 0,
16645             cn : [
16646                 { 
16647                     tag: 'thead',
16648                     cn : [
16649                         { 
16650                             tag: 'tr',
16651                             cls : 'fc-first fc-last',
16652                             cn : cal_heads()
16653                         }
16654                     ]
16655                 },
16656                 { 
16657                     tag: 'tbody',
16658                     cn : cal_rows()
16659                 }
16660                   
16661             ]
16662         };
16663          
16664          var cfg = {
16665             cls : 'fc fc-ltr',
16666             cn : [
16667                 header,
16668                 {
16669                     cls : 'fc-content',
16670                     style : "position: relative;",
16671                     cn : [
16672                         {
16673                             cls : 'fc-view fc-view-month fc-grid',
16674                             style : 'position: relative',
16675                             unselectable : 'on',
16676                             cn : [
16677                                 {
16678                                     cls : 'fc-event-container',
16679                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16680                                 },
16681                                 cal_table
16682                             ]
16683                         }
16684                     ]
16685     
16686                 }
16687            ] 
16688             
16689         };
16690         
16691          
16692         
16693         return cfg;
16694     },
16695     
16696     
16697     initEvents : function()
16698     {
16699         if(!this.store){
16700             throw "can not find store for calendar";
16701         }
16702         
16703         var mark = {
16704             tag: "div",
16705             cls:"x-dlg-mask",
16706             style: "text-align:center",
16707             cn: [
16708                 {
16709                     tag: "div",
16710                     style: "background-color:white;width:50%;margin:250 auto",
16711                     cn: [
16712                         {
16713                             tag: "img",
16714                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16715                         },
16716                         {
16717                             tag: "span",
16718                             html: "Loading"
16719                         }
16720                         
16721                     ]
16722                 }
16723             ]
16724         };
16725         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16726         
16727         var size = this.el.select('.fc-content', true).first().getSize();
16728         this.maskEl.setSize(size.width, size.height);
16729         this.maskEl.enableDisplayMode("block");
16730         if(!this.loadMask){
16731             this.maskEl.hide();
16732         }
16733         
16734         this.store = Roo.factory(this.store, Roo.data);
16735         this.store.on('load', this.onLoad, this);
16736         this.store.on('beforeload', this.onBeforeLoad, this);
16737         
16738         this.resize();
16739         
16740         this.cells = this.el.select('.fc-day',true);
16741         //Roo.log(this.cells);
16742         this.textNodes = this.el.query('.fc-day-number');
16743         this.cells.addClassOnOver('fc-state-hover');
16744         
16745         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16746         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16747         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16748         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16749         
16750         this.on('monthchange', this.onMonthChange, this);
16751         
16752         this.update(new Date().clearTime());
16753     },
16754     
16755     resize : function() {
16756         var sz  = this.el.getSize();
16757         
16758         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16759         this.el.select('.fc-day-content div',true).setHeight(34);
16760     },
16761     
16762     
16763     // private
16764     showPrevMonth : function(e){
16765         this.update(this.activeDate.add("mo", -1));
16766     },
16767     showToday : function(e){
16768         this.update(new Date().clearTime());
16769     },
16770     // private
16771     showNextMonth : function(e){
16772         this.update(this.activeDate.add("mo", 1));
16773     },
16774
16775     // private
16776     showPrevYear : function(){
16777         this.update(this.activeDate.add("y", -1));
16778     },
16779
16780     // private
16781     showNextYear : function(){
16782         this.update(this.activeDate.add("y", 1));
16783     },
16784
16785     
16786    // private
16787     update : function(date)
16788     {
16789         var vd = this.activeDate;
16790         this.activeDate = date;
16791 //        if(vd && this.el){
16792 //            var t = date.getTime();
16793 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16794 //                Roo.log('using add remove');
16795 //                
16796 //                this.fireEvent('monthchange', this, date);
16797 //                
16798 //                this.cells.removeClass("fc-state-highlight");
16799 //                this.cells.each(function(c){
16800 //                   if(c.dateValue == t){
16801 //                       c.addClass("fc-state-highlight");
16802 //                       setTimeout(function(){
16803 //                            try{c.dom.firstChild.focus();}catch(e){}
16804 //                       }, 50);
16805 //                       return false;
16806 //                   }
16807 //                   return true;
16808 //                });
16809 //                return;
16810 //            }
16811 //        }
16812         
16813         var days = date.getDaysInMonth();
16814         
16815         var firstOfMonth = date.getFirstDateOfMonth();
16816         var startingPos = firstOfMonth.getDay()-this.startDay;
16817         
16818         if(startingPos < this.startDay){
16819             startingPos += 7;
16820         }
16821         
16822         var pm = date.add(Date.MONTH, -1);
16823         var prevStart = pm.getDaysInMonth()-startingPos;
16824 //        
16825         this.cells = this.el.select('.fc-day',true);
16826         this.textNodes = this.el.query('.fc-day-number');
16827         this.cells.addClassOnOver('fc-state-hover');
16828         
16829         var cells = this.cells.elements;
16830         var textEls = this.textNodes;
16831         
16832         Roo.each(cells, function(cell){
16833             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16834         });
16835         
16836         days += startingPos;
16837
16838         // convert everything to numbers so it's fast
16839         var day = 86400000;
16840         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16841         //Roo.log(d);
16842         //Roo.log(pm);
16843         //Roo.log(prevStart);
16844         
16845         var today = new Date().clearTime().getTime();
16846         var sel = date.clearTime().getTime();
16847         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16848         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16849         var ddMatch = this.disabledDatesRE;
16850         var ddText = this.disabledDatesText;
16851         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16852         var ddaysText = this.disabledDaysText;
16853         var format = this.format;
16854         
16855         var setCellClass = function(cal, cell){
16856             cell.row = 0;
16857             cell.events = [];
16858             cell.more = [];
16859             //Roo.log('set Cell Class');
16860             cell.title = "";
16861             var t = d.getTime();
16862             
16863             //Roo.log(d);
16864             
16865             cell.dateValue = t;
16866             if(t == today){
16867                 cell.className += " fc-today";
16868                 cell.className += " fc-state-highlight";
16869                 cell.title = cal.todayText;
16870             }
16871             if(t == sel){
16872                 // disable highlight in other month..
16873                 //cell.className += " fc-state-highlight";
16874                 
16875             }
16876             // disabling
16877             if(t < min) {
16878                 cell.className = " fc-state-disabled";
16879                 cell.title = cal.minText;
16880                 return;
16881             }
16882             if(t > max) {
16883                 cell.className = " fc-state-disabled";
16884                 cell.title = cal.maxText;
16885                 return;
16886             }
16887             if(ddays){
16888                 if(ddays.indexOf(d.getDay()) != -1){
16889                     cell.title = ddaysText;
16890                     cell.className = " fc-state-disabled";
16891                 }
16892             }
16893             if(ddMatch && format){
16894                 var fvalue = d.dateFormat(format);
16895                 if(ddMatch.test(fvalue)){
16896                     cell.title = ddText.replace("%0", fvalue);
16897                     cell.className = " fc-state-disabled";
16898                 }
16899             }
16900             
16901             if (!cell.initialClassName) {
16902                 cell.initialClassName = cell.dom.className;
16903             }
16904             
16905             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16906         };
16907
16908         var i = 0;
16909         
16910         for(; i < startingPos; i++) {
16911             textEls[i].innerHTML = (++prevStart);
16912             d.setDate(d.getDate()+1);
16913             
16914             cells[i].className = "fc-past fc-other-month";
16915             setCellClass(this, cells[i]);
16916         }
16917         
16918         var intDay = 0;
16919         
16920         for(; i < days; i++){
16921             intDay = i - startingPos + 1;
16922             textEls[i].innerHTML = (intDay);
16923             d.setDate(d.getDate()+1);
16924             
16925             cells[i].className = ''; // "x-date-active";
16926             setCellClass(this, cells[i]);
16927         }
16928         var extraDays = 0;
16929         
16930         for(; i < 42; i++) {
16931             textEls[i].innerHTML = (++extraDays);
16932             d.setDate(d.getDate()+1);
16933             
16934             cells[i].className = "fc-future fc-other-month";
16935             setCellClass(this, cells[i]);
16936         }
16937         
16938         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16939         
16940         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16941         
16942         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16943         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16944         
16945         if(totalRows != 6){
16946             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16947             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16948         }
16949         
16950         this.fireEvent('monthchange', this, date);
16951         
16952         
16953         /*
16954         if(!this.internalRender){
16955             var main = this.el.dom.firstChild;
16956             var w = main.offsetWidth;
16957             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16958             Roo.fly(main).setWidth(w);
16959             this.internalRender = true;
16960             // opera does not respect the auto grow header center column
16961             // then, after it gets a width opera refuses to recalculate
16962             // without a second pass
16963             if(Roo.isOpera && !this.secondPass){
16964                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16965                 this.secondPass = true;
16966                 this.update.defer(10, this, [date]);
16967             }
16968         }
16969         */
16970         
16971     },
16972     
16973     findCell : function(dt) {
16974         dt = dt.clearTime().getTime();
16975         var ret = false;
16976         this.cells.each(function(c){
16977             //Roo.log("check " +c.dateValue + '?=' + dt);
16978             if(c.dateValue == dt){
16979                 ret = c;
16980                 return false;
16981             }
16982             return true;
16983         });
16984         
16985         return ret;
16986     },
16987     
16988     findCells : function(ev) {
16989         var s = ev.start.clone().clearTime().getTime();
16990        // Roo.log(s);
16991         var e= ev.end.clone().clearTime().getTime();
16992        // Roo.log(e);
16993         var ret = [];
16994         this.cells.each(function(c){
16995              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16996             
16997             if(c.dateValue > e){
16998                 return ;
16999             }
17000             if(c.dateValue < s){
17001                 return ;
17002             }
17003             ret.push(c);
17004         });
17005         
17006         return ret;    
17007     },
17008     
17009 //    findBestRow: function(cells)
17010 //    {
17011 //        var ret = 0;
17012 //        
17013 //        for (var i =0 ; i < cells.length;i++) {
17014 //            ret  = Math.max(cells[i].rows || 0,ret);
17015 //        }
17016 //        return ret;
17017 //        
17018 //    },
17019     
17020     
17021     addItem : function(ev)
17022     {
17023         // look for vertical location slot in
17024         var cells = this.findCells(ev);
17025         
17026 //        ev.row = this.findBestRow(cells);
17027         
17028         // work out the location.
17029         
17030         var crow = false;
17031         var rows = [];
17032         for(var i =0; i < cells.length; i++) {
17033             
17034             cells[i].row = cells[0].row;
17035             
17036             if(i == 0){
17037                 cells[i].row = cells[i].row + 1;
17038             }
17039             
17040             if (!crow) {
17041                 crow = {
17042                     start : cells[i],
17043                     end :  cells[i]
17044                 };
17045                 continue;
17046             }
17047             if (crow.start.getY() == cells[i].getY()) {
17048                 // on same row.
17049                 crow.end = cells[i];
17050                 continue;
17051             }
17052             // different row.
17053             rows.push(crow);
17054             crow = {
17055                 start: cells[i],
17056                 end : cells[i]
17057             };
17058             
17059         }
17060         
17061         rows.push(crow);
17062         ev.els = [];
17063         ev.rows = rows;
17064         ev.cells = cells;
17065         
17066         cells[0].events.push(ev);
17067         
17068         this.calevents.push(ev);
17069     },
17070     
17071     clearEvents: function() {
17072         
17073         if(!this.calevents){
17074             return;
17075         }
17076         
17077         Roo.each(this.cells.elements, function(c){
17078             c.row = 0;
17079             c.events = [];
17080             c.more = [];
17081         });
17082         
17083         Roo.each(this.calevents, function(e) {
17084             Roo.each(e.els, function(el) {
17085                 el.un('mouseenter' ,this.onEventEnter, this);
17086                 el.un('mouseleave' ,this.onEventLeave, this);
17087                 el.remove();
17088             },this);
17089         },this);
17090         
17091         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17092             e.remove();
17093         });
17094         
17095     },
17096     
17097     renderEvents: function()
17098     {   
17099         var _this = this;
17100         
17101         this.cells.each(function(c) {
17102             
17103             if(c.row < 5){
17104                 return;
17105             }
17106             
17107             var ev = c.events;
17108             
17109             var r = 4;
17110             if(c.row != c.events.length){
17111                 r = 4 - (4 - (c.row - c.events.length));
17112             }
17113             
17114             c.events = ev.slice(0, r);
17115             c.more = ev.slice(r);
17116             
17117             if(c.more.length && c.more.length == 1){
17118                 c.events.push(c.more.pop());
17119             }
17120             
17121             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17122             
17123         });
17124             
17125         this.cells.each(function(c) {
17126             
17127             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17128             
17129             
17130             for (var e = 0; e < c.events.length; e++){
17131                 var ev = c.events[e];
17132                 var rows = ev.rows;
17133                 
17134                 for(var i = 0; i < rows.length; i++) {
17135                 
17136                     // how many rows should it span..
17137
17138                     var  cfg = {
17139                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17140                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17141
17142                         unselectable : "on",
17143                         cn : [
17144                             {
17145                                 cls: 'fc-event-inner',
17146                                 cn : [
17147     //                                {
17148     //                                  tag:'span',
17149     //                                  cls: 'fc-event-time',
17150     //                                  html : cells.length > 1 ? '' : ev.time
17151     //                                },
17152                                     {
17153                                       tag:'span',
17154                                       cls: 'fc-event-title',
17155                                       html : String.format('{0}', ev.title)
17156                                     }
17157
17158
17159                                 ]
17160                             },
17161                             {
17162                                 cls: 'ui-resizable-handle ui-resizable-e',
17163                                 html : '&nbsp;&nbsp;&nbsp'
17164                             }
17165
17166                         ]
17167                     };
17168
17169                     if (i == 0) {
17170                         cfg.cls += ' fc-event-start';
17171                     }
17172                     if ((i+1) == rows.length) {
17173                         cfg.cls += ' fc-event-end';
17174                     }
17175
17176                     var ctr = _this.el.select('.fc-event-container',true).first();
17177                     var cg = ctr.createChild(cfg);
17178
17179                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17180                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17181
17182                     var r = (c.more.length) ? 1 : 0;
17183                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17184                     cg.setWidth(ebox.right - sbox.x -2);
17185
17186                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17187                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17188                     cg.on('click', _this.onEventClick, _this, ev);
17189
17190                     ev.els.push(cg);
17191                     
17192                 }
17193                 
17194             }
17195             
17196             
17197             if(c.more.length){
17198                 var  cfg = {
17199                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17200                     style : 'position: absolute',
17201                     unselectable : "on",
17202                     cn : [
17203                         {
17204                             cls: 'fc-event-inner',
17205                             cn : [
17206                                 {
17207                                   tag:'span',
17208                                   cls: 'fc-event-title',
17209                                   html : 'More'
17210                                 }
17211
17212
17213                             ]
17214                         },
17215                         {
17216                             cls: 'ui-resizable-handle ui-resizable-e',
17217                             html : '&nbsp;&nbsp;&nbsp'
17218                         }
17219
17220                     ]
17221                 };
17222
17223                 var ctr = _this.el.select('.fc-event-container',true).first();
17224                 var cg = ctr.createChild(cfg);
17225
17226                 var sbox = c.select('.fc-day-content',true).first().getBox();
17227                 var ebox = c.select('.fc-day-content',true).first().getBox();
17228                 //Roo.log(cg);
17229                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17230                 cg.setWidth(ebox.right - sbox.x -2);
17231
17232                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17233                 
17234             }
17235             
17236         });
17237         
17238         
17239         
17240     },
17241     
17242     onEventEnter: function (e, el,event,d) {
17243         this.fireEvent('evententer', this, el, event);
17244     },
17245     
17246     onEventLeave: function (e, el,event,d) {
17247         this.fireEvent('eventleave', this, el, event);
17248     },
17249     
17250     onEventClick: function (e, el,event,d) {
17251         this.fireEvent('eventclick', this, el, event);
17252     },
17253     
17254     onMonthChange: function () {
17255         this.store.load();
17256     },
17257     
17258     onMoreEventClick: function(e, el, more)
17259     {
17260         var _this = this;
17261         
17262         this.calpopover.placement = 'right';
17263         this.calpopover.setTitle('More');
17264         
17265         this.calpopover.setContent('');
17266         
17267         var ctr = this.calpopover.el.select('.popover-content', true).first();
17268         
17269         Roo.each(more, function(m){
17270             var cfg = {
17271                 cls : 'fc-event-hori fc-event-draggable',
17272                 html : m.title
17273             };
17274             var cg = ctr.createChild(cfg);
17275             
17276             cg.on('click', _this.onEventClick, _this, m);
17277         });
17278         
17279         this.calpopover.show(el);
17280         
17281         
17282     },
17283     
17284     onLoad: function () 
17285     {   
17286         this.calevents = [];
17287         var cal = this;
17288         
17289         if(this.store.getCount() > 0){
17290             this.store.data.each(function(d){
17291                cal.addItem({
17292                     id : d.data.id,
17293                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17294                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17295                     time : d.data.start_time,
17296                     title : d.data.title,
17297                     description : d.data.description,
17298                     venue : d.data.venue
17299                 });
17300             });
17301         }
17302         
17303         this.renderEvents();
17304         
17305         if(this.calevents.length && this.loadMask){
17306             this.maskEl.hide();
17307         }
17308     },
17309     
17310     onBeforeLoad: function()
17311     {
17312         this.clearEvents();
17313         if(this.loadMask){
17314             this.maskEl.show();
17315         }
17316     }
17317 });
17318
17319  
17320  /*
17321  * - LGPL
17322  *
17323  * element
17324  * 
17325  */
17326
17327 /**
17328  * @class Roo.bootstrap.Popover
17329  * @extends Roo.bootstrap.Component
17330  * Bootstrap Popover class
17331  * @cfg {String} html contents of the popover   (or false to use children..)
17332  * @cfg {String} title of popover (or false to hide)
17333  * @cfg {String} placement how it is placed
17334  * @cfg {String} trigger click || hover (or false to trigger manually)
17335  * @cfg {String} over what (parent or false to trigger manually.)
17336  * @cfg {Number} delay - delay before showing
17337  
17338  * @constructor
17339  * Create a new Popover
17340  * @param {Object} config The config object
17341  */
17342
17343 Roo.bootstrap.Popover = function(config){
17344     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17345     
17346     this.addEvents({
17347         // raw events
17348          /**
17349          * @event show
17350          * After the popover show
17351          * 
17352          * @param {Roo.bootstrap.Popover} this
17353          */
17354         "show" : true,
17355         /**
17356          * @event hide
17357          * After the popover hide
17358          * 
17359          * @param {Roo.bootstrap.Popover} this
17360          */
17361         "hide" : true
17362     });
17363 };
17364
17365 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17366     
17367     title: 'Fill in a title',
17368     html: false,
17369     
17370     placement : 'right',
17371     trigger : 'hover', // hover
17372     
17373     delay : 0,
17374     
17375     over: 'parent',
17376     
17377     can_build_overlaid : false,
17378     
17379     getChildContainer : function()
17380     {
17381         return this.el.select('.popover-content',true).first();
17382     },
17383     
17384     getAutoCreate : function(){
17385          
17386         var cfg = {
17387            cls : 'popover roo-dynamic',
17388            style: 'display:block',
17389            cn : [
17390                 {
17391                     cls : 'arrow'
17392                 },
17393                 {
17394                     cls : 'popover-inner',
17395                     cn : [
17396                         {
17397                             tag: 'h3',
17398                             cls: 'popover-title',
17399                             html : this.title
17400                         },
17401                         {
17402                             cls : 'popover-content',
17403                             html : this.html
17404                         }
17405                     ]
17406                     
17407                 }
17408            ]
17409         };
17410         
17411         return cfg;
17412     },
17413     setTitle: function(str)
17414     {
17415         this.title = str;
17416         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17417     },
17418     setContent: function(str)
17419     {
17420         this.html = str;
17421         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17422     },
17423     // as it get's added to the bottom of the page.
17424     onRender : function(ct, position)
17425     {
17426         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17427         if(!this.el){
17428             var cfg = Roo.apply({},  this.getAutoCreate());
17429             cfg.id = Roo.id();
17430             
17431             if (this.cls) {
17432                 cfg.cls += ' ' + this.cls;
17433             }
17434             if (this.style) {
17435                 cfg.style = this.style;
17436             }
17437             //Roo.log("adding to ");
17438             this.el = Roo.get(document.body).createChild(cfg, position);
17439 //            Roo.log(this.el);
17440         }
17441         this.initEvents();
17442     },
17443     
17444     initEvents : function()
17445     {
17446         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17447         this.el.enableDisplayMode('block');
17448         this.el.hide();
17449         if (this.over === false) {
17450             return; 
17451         }
17452         if (this.triggers === false) {
17453             return;
17454         }
17455         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17456         var triggers = this.trigger ? this.trigger.split(' ') : [];
17457         Roo.each(triggers, function(trigger) {
17458         
17459             if (trigger == 'click') {
17460                 on_el.on('click', this.toggle, this);
17461             } else if (trigger != 'manual') {
17462                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17463                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17464       
17465                 on_el.on(eventIn  ,this.enter, this);
17466                 on_el.on(eventOut, this.leave, this);
17467             }
17468         }, this);
17469         
17470     },
17471     
17472     
17473     // private
17474     timeout : null,
17475     hoverState : null,
17476     
17477     toggle : function () {
17478         this.hoverState == 'in' ? this.leave() : this.enter();
17479     },
17480     
17481     enter : function () {
17482         
17483         clearTimeout(this.timeout);
17484     
17485         this.hoverState = 'in';
17486     
17487         if (!this.delay || !this.delay.show) {
17488             this.show();
17489             return;
17490         }
17491         var _t = this;
17492         this.timeout = setTimeout(function () {
17493             if (_t.hoverState == 'in') {
17494                 _t.show();
17495             }
17496         }, this.delay.show)
17497     },
17498     
17499     leave : function() {
17500         clearTimeout(this.timeout);
17501     
17502         this.hoverState = 'out';
17503     
17504         if (!this.delay || !this.delay.hide) {
17505             this.hide();
17506             return;
17507         }
17508         var _t = this;
17509         this.timeout = setTimeout(function () {
17510             if (_t.hoverState == 'out') {
17511                 _t.hide();
17512             }
17513         }, this.delay.hide)
17514     },
17515     
17516     show : function (on_el)
17517     {
17518         if (!on_el) {
17519             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17520         }
17521         
17522         // set content.
17523         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17524         if (this.html !== false) {
17525             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17526         }
17527         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17528         if (!this.title.length) {
17529             this.el.select('.popover-title',true).hide();
17530         }
17531         
17532         var placement = typeof this.placement == 'function' ?
17533             this.placement.call(this, this.el, on_el) :
17534             this.placement;
17535             
17536         var autoToken = /\s?auto?\s?/i;
17537         var autoPlace = autoToken.test(placement);
17538         if (autoPlace) {
17539             placement = placement.replace(autoToken, '') || 'top';
17540         }
17541         
17542         //this.el.detach()
17543         //this.el.setXY([0,0]);
17544         this.el.show();
17545         this.el.dom.style.display='block';
17546         this.el.addClass(placement);
17547         
17548         //this.el.appendTo(on_el);
17549         
17550         var p = this.getPosition();
17551         var box = this.el.getBox();
17552         
17553         if (autoPlace) {
17554             // fixme..
17555         }
17556         var align = Roo.bootstrap.Popover.alignment[placement];
17557         
17558 //        Roo.log(align);
17559         this.el.alignTo(on_el, align[0],align[1]);
17560         //var arrow = this.el.select('.arrow',true).first();
17561         //arrow.set(align[2], 
17562         
17563         this.el.addClass('in');
17564         
17565         
17566         if (this.el.hasClass('fade')) {
17567             // fade it?
17568         }
17569         
17570         this.hoverState = 'in';
17571         
17572         this.fireEvent('show', this);
17573         
17574     },
17575     hide : function()
17576     {
17577         this.el.setXY([0,0]);
17578         this.el.removeClass('in');
17579         this.el.hide();
17580         this.hoverState = null;
17581         
17582         this.fireEvent('hide', this);
17583     }
17584     
17585 });
17586
17587 Roo.bootstrap.Popover.alignment = {
17588     'left' : ['r-l', [-10,0], 'right'],
17589     'right' : ['l-r', [10,0], 'left'],
17590     'bottom' : ['t-b', [0,10], 'top'],
17591     'top' : [ 'b-t', [0,-10], 'bottom']
17592 };
17593
17594  /*
17595  * - LGPL
17596  *
17597  * Progress
17598  * 
17599  */
17600
17601 /**
17602  * @class Roo.bootstrap.Progress
17603  * @extends Roo.bootstrap.Component
17604  * Bootstrap Progress class
17605  * @cfg {Boolean} striped striped of the progress bar
17606  * @cfg {Boolean} active animated of the progress bar
17607  * 
17608  * 
17609  * @constructor
17610  * Create a new Progress
17611  * @param {Object} config The config object
17612  */
17613
17614 Roo.bootstrap.Progress = function(config){
17615     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17616 };
17617
17618 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17619     
17620     striped : false,
17621     active: false,
17622     
17623     getAutoCreate : function(){
17624         var cfg = {
17625             tag: 'div',
17626             cls: 'progress'
17627         };
17628         
17629         
17630         if(this.striped){
17631             cfg.cls += ' progress-striped';
17632         }
17633       
17634         if(this.active){
17635             cfg.cls += ' active';
17636         }
17637         
17638         
17639         return cfg;
17640     }
17641    
17642 });
17643
17644  
17645
17646  /*
17647  * - LGPL
17648  *
17649  * ProgressBar
17650  * 
17651  */
17652
17653 /**
17654  * @class Roo.bootstrap.ProgressBar
17655  * @extends Roo.bootstrap.Component
17656  * Bootstrap ProgressBar class
17657  * @cfg {Number} aria_valuenow aria-value now
17658  * @cfg {Number} aria_valuemin aria-value min
17659  * @cfg {Number} aria_valuemax aria-value max
17660  * @cfg {String} label label for the progress bar
17661  * @cfg {String} panel (success | info | warning | danger )
17662  * @cfg {String} role role of the progress bar
17663  * @cfg {String} sr_only text
17664  * 
17665  * 
17666  * @constructor
17667  * Create a new ProgressBar
17668  * @param {Object} config The config object
17669  */
17670
17671 Roo.bootstrap.ProgressBar = function(config){
17672     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17673 };
17674
17675 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17676     
17677     aria_valuenow : 0,
17678     aria_valuemin : 0,
17679     aria_valuemax : 100,
17680     label : false,
17681     panel : false,
17682     role : false,
17683     sr_only: false,
17684     
17685     getAutoCreate : function()
17686     {
17687         
17688         var cfg = {
17689             tag: 'div',
17690             cls: 'progress-bar',
17691             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17692         };
17693         
17694         if(this.sr_only){
17695             cfg.cn = {
17696                 tag: 'span',
17697                 cls: 'sr-only',
17698                 html: this.sr_only
17699             }
17700         }
17701         
17702         if(this.role){
17703             cfg.role = this.role;
17704         }
17705         
17706         if(this.aria_valuenow){
17707             cfg['aria-valuenow'] = this.aria_valuenow;
17708         }
17709         
17710         if(this.aria_valuemin){
17711             cfg['aria-valuemin'] = this.aria_valuemin;
17712         }
17713         
17714         if(this.aria_valuemax){
17715             cfg['aria-valuemax'] = this.aria_valuemax;
17716         }
17717         
17718         if(this.label && !this.sr_only){
17719             cfg.html = this.label;
17720         }
17721         
17722         if(this.panel){
17723             cfg.cls += ' progress-bar-' + this.panel;
17724         }
17725         
17726         return cfg;
17727     },
17728     
17729     update : function(aria_valuenow)
17730     {
17731         this.aria_valuenow = aria_valuenow;
17732         
17733         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17734     }
17735    
17736 });
17737
17738  
17739
17740  /*
17741  * - LGPL
17742  *
17743  * column
17744  * 
17745  */
17746
17747 /**
17748  * @class Roo.bootstrap.TabGroup
17749  * @extends Roo.bootstrap.Column
17750  * Bootstrap Column class
17751  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17752  * @cfg {Boolean} carousel true to make the group behave like a carousel
17753  * @cfg {Boolean} bullets show bullets for the panels
17754  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17755  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17756  * @cfg {Boolean} showarrow (true|false) show arrow default true
17757  * 
17758  * @constructor
17759  * Create a new TabGroup
17760  * @param {Object} config The config object
17761  */
17762
17763 Roo.bootstrap.TabGroup = function(config){
17764     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17765     if (!this.navId) {
17766         this.navId = Roo.id();
17767     }
17768     this.tabs = [];
17769     Roo.bootstrap.TabGroup.register(this);
17770     
17771 };
17772
17773 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17774     
17775     carousel : false,
17776     transition : false,
17777     bullets : 0,
17778     timer : 0,
17779     autoslide : false,
17780     slideFn : false,
17781     slideOnTouch : false,
17782     showarrow : true,
17783     
17784     getAutoCreate : function()
17785     {
17786         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17787         
17788         cfg.cls += ' tab-content';
17789         
17790         if (this.carousel) {
17791             cfg.cls += ' carousel slide';
17792             
17793             cfg.cn = [{
17794                cls : 'carousel-inner',
17795                cn : []
17796             }];
17797         
17798             if(this.bullets  && !Roo.isTouch){
17799                 
17800                 var bullets = {
17801                     cls : 'carousel-bullets',
17802                     cn : []
17803                 };
17804                
17805                 if(this.bullets_cls){
17806                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17807                 }
17808                 
17809                 bullets.cn.push({
17810                     cls : 'clear'
17811                 });
17812                 
17813                 cfg.cn[0].cn.push(bullets);
17814             }
17815             
17816             if(this.showarrow){
17817                 cfg.cn[0].cn.push({
17818                     tag : 'div',
17819                     class : 'carousel-arrow',
17820                     cn : [
17821                         {
17822                             tag : 'div',
17823                             class : 'carousel-prev',
17824                             cn : [
17825                                 {
17826                                     tag : 'i',
17827                                     class : 'fa fa-chevron-left'
17828                                 }
17829                             ]
17830                         },
17831                         {
17832                             tag : 'div',
17833                             class : 'carousel-next',
17834                             cn : [
17835                                 {
17836                                     tag : 'i',
17837                                     class : 'fa fa-chevron-right'
17838                                 }
17839                             ]
17840                         }
17841                     ]
17842                 });
17843             }
17844             
17845         }
17846         
17847         return cfg;
17848     },
17849     
17850     initEvents:  function()
17851     {
17852 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17853 //            this.el.on("touchstart", this.onTouchStart, this);
17854 //        }
17855         
17856         if(this.autoslide){
17857             var _this = this;
17858             
17859             this.slideFn = window.setInterval(function() {
17860                 _this.showPanelNext();
17861             }, this.timer);
17862         }
17863         
17864         if(this.showarrow){
17865             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17866             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17867         }
17868         
17869         
17870     },
17871     
17872 //    onTouchStart : function(e, el, o)
17873 //    {
17874 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17875 //            return;
17876 //        }
17877 //        
17878 //        this.showPanelNext();
17879 //    },
17880     
17881     
17882     getChildContainer : function()
17883     {
17884         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17885     },
17886     
17887     /**
17888     * register a Navigation item
17889     * @param {Roo.bootstrap.NavItem} the navitem to add
17890     */
17891     register : function(item)
17892     {
17893         this.tabs.push( item);
17894         item.navId = this.navId; // not really needed..
17895         this.addBullet();
17896     
17897     },
17898     
17899     getActivePanel : function()
17900     {
17901         var r = false;
17902         Roo.each(this.tabs, function(t) {
17903             if (t.active) {
17904                 r = t;
17905                 return false;
17906             }
17907             return null;
17908         });
17909         return r;
17910         
17911     },
17912     getPanelByName : function(n)
17913     {
17914         var r = false;
17915         Roo.each(this.tabs, function(t) {
17916             if (t.tabId == n) {
17917                 r = t;
17918                 return false;
17919             }
17920             return null;
17921         });
17922         return r;
17923     },
17924     indexOfPanel : function(p)
17925     {
17926         var r = false;
17927         Roo.each(this.tabs, function(t,i) {
17928             if (t.tabId == p.tabId) {
17929                 r = i;
17930                 return false;
17931             }
17932             return null;
17933         });
17934         return r;
17935     },
17936     /**
17937      * show a specific panel
17938      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17939      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17940      */
17941     showPanel : function (pan)
17942     {
17943         if(this.transition || typeof(pan) == 'undefined'){
17944             Roo.log("waiting for the transitionend");
17945             return;
17946         }
17947         
17948         if (typeof(pan) == 'number') {
17949             pan = this.tabs[pan];
17950         }
17951         
17952         if (typeof(pan) == 'string') {
17953             pan = this.getPanelByName(pan);
17954         }
17955         
17956         var cur = this.getActivePanel();
17957         
17958         if(!pan || !cur){
17959             Roo.log('pan or acitve pan is undefined');
17960             return false;
17961         }
17962         
17963         if (pan.tabId == this.getActivePanel().tabId) {
17964             return true;
17965         }
17966         
17967         if (false === cur.fireEvent('beforedeactivate')) {
17968             return false;
17969         }
17970         
17971         if(this.bullets > 0 && !Roo.isTouch){
17972             this.setActiveBullet(this.indexOfPanel(pan));
17973         }
17974         
17975         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17976             
17977             this.transition = true;
17978             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17979             var lr = dir == 'next' ? 'left' : 'right';
17980             pan.el.addClass(dir); // or prev
17981             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17982             cur.el.addClass(lr); // or right
17983             pan.el.addClass(lr);
17984             
17985             var _this = this;
17986             cur.el.on('transitionend', function() {
17987                 Roo.log("trans end?");
17988                 
17989                 pan.el.removeClass([lr,dir]);
17990                 pan.setActive(true);
17991                 
17992                 cur.el.removeClass([lr]);
17993                 cur.setActive(false);
17994                 
17995                 _this.transition = false;
17996                 
17997             }, this, { single:  true } );
17998             
17999             return true;
18000         }
18001         
18002         cur.setActive(false);
18003         pan.setActive(true);
18004         
18005         return true;
18006         
18007     },
18008     showPanelNext : function()
18009     {
18010         var i = this.indexOfPanel(this.getActivePanel());
18011         
18012         if (i >= this.tabs.length - 1 && !this.autoslide) {
18013             return;
18014         }
18015         
18016         if (i >= this.tabs.length - 1 && this.autoslide) {
18017             i = -1;
18018         }
18019         
18020         this.showPanel(this.tabs[i+1]);
18021     },
18022     
18023     showPanelPrev : function()
18024     {
18025         var i = this.indexOfPanel(this.getActivePanel());
18026         
18027         if (i  < 1 && !this.autoslide) {
18028             return;
18029         }
18030         
18031         if (i < 1 && this.autoslide) {
18032             i = this.tabs.length;
18033         }
18034         
18035         this.showPanel(this.tabs[i-1]);
18036     },
18037     
18038     
18039     addBullet: function()
18040     {
18041         if(!this.bullets || Roo.isTouch){
18042             return;
18043         }
18044         var ctr = this.el.select('.carousel-bullets',true).first();
18045         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18046         var bullet = ctr.createChild({
18047             cls : 'bullet bullet-' + i
18048         },ctr.dom.lastChild);
18049         
18050         
18051         var _this = this;
18052         
18053         bullet.on('click', (function(e, el, o, ii, t){
18054
18055             e.preventDefault();
18056
18057             this.showPanel(ii);
18058
18059             if(this.autoslide && this.slideFn){
18060                 clearInterval(this.slideFn);
18061                 this.slideFn = window.setInterval(function() {
18062                     _this.showPanelNext();
18063                 }, this.timer);
18064             }
18065
18066         }).createDelegate(this, [i, bullet], true));
18067                 
18068         
18069     },
18070      
18071     setActiveBullet : function(i)
18072     {
18073         if(Roo.isTouch){
18074             return;
18075         }
18076         
18077         Roo.each(this.el.select('.bullet', true).elements, function(el){
18078             el.removeClass('selected');
18079         });
18080
18081         var bullet = this.el.select('.bullet-' + i, true).first();
18082         
18083         if(!bullet){
18084             return;
18085         }
18086         
18087         bullet.addClass('selected');
18088     }
18089     
18090     
18091   
18092 });
18093
18094  
18095
18096  
18097  
18098 Roo.apply(Roo.bootstrap.TabGroup, {
18099     
18100     groups: {},
18101      /**
18102     * register a Navigation Group
18103     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18104     */
18105     register : function(navgrp)
18106     {
18107         this.groups[navgrp.navId] = navgrp;
18108         
18109     },
18110     /**
18111     * fetch a Navigation Group based on the navigation ID
18112     * if one does not exist , it will get created.
18113     * @param {string} the navgroup to add
18114     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18115     */
18116     get: function(navId) {
18117         if (typeof(this.groups[navId]) == 'undefined') {
18118             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18119         }
18120         return this.groups[navId] ;
18121     }
18122     
18123     
18124     
18125 });
18126
18127  /*
18128  * - LGPL
18129  *
18130  * TabPanel
18131  * 
18132  */
18133
18134 /**
18135  * @class Roo.bootstrap.TabPanel
18136  * @extends Roo.bootstrap.Component
18137  * Bootstrap TabPanel class
18138  * @cfg {Boolean} active panel active
18139  * @cfg {String} html panel content
18140  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18141  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18142  * @cfg {String} href click to link..
18143  * 
18144  * 
18145  * @constructor
18146  * Create a new TabPanel
18147  * @param {Object} config The config object
18148  */
18149
18150 Roo.bootstrap.TabPanel = function(config){
18151     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18152     this.addEvents({
18153         /**
18154              * @event changed
18155              * Fires when the active status changes
18156              * @param {Roo.bootstrap.TabPanel} this
18157              * @param {Boolean} state the new state
18158             
18159          */
18160         'changed': true,
18161         /**
18162              * @event beforedeactivate
18163              * Fires before a tab is de-activated - can be used to do validation on a form.
18164              * @param {Roo.bootstrap.TabPanel} this
18165              * @return {Boolean} false if there is an error
18166             
18167          */
18168         'beforedeactivate': true
18169      });
18170     
18171     this.tabId = this.tabId || Roo.id();
18172   
18173 };
18174
18175 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18176     
18177     active: false,
18178     html: false,
18179     tabId: false,
18180     navId : false,
18181     href : '',
18182     
18183     getAutoCreate : function(){
18184         var cfg = {
18185             tag: 'div',
18186             // item is needed for carousel - not sure if it has any effect otherwise
18187             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18188             html: this.html || ''
18189         };
18190         
18191         if(this.active){
18192             cfg.cls += ' active';
18193         }
18194         
18195         if(this.tabId){
18196             cfg.tabId = this.tabId;
18197         }
18198         
18199         
18200         return cfg;
18201     },
18202     
18203     initEvents:  function()
18204     {
18205         var p = this.parent();
18206         
18207         this.navId = this.navId || p.navId;
18208         
18209         if (typeof(this.navId) != 'undefined') {
18210             // not really needed.. but just in case.. parent should be a NavGroup.
18211             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18212             
18213             tg.register(this);
18214             
18215             var i = tg.tabs.length - 1;
18216             
18217             if(this.active && tg.bullets > 0 && i < tg.bullets){
18218                 tg.setActiveBullet(i);
18219             }
18220         }
18221         
18222         this.el.on('click', this.onClick, this);
18223         
18224         if(Roo.isTouch){
18225             this.el.on("touchstart", this.onTouchStart, this);
18226             this.el.on("touchmove", this.onTouchMove, this);
18227             this.el.on("touchend", this.onTouchEnd, this);
18228         }
18229         
18230     },
18231     
18232     onRender : function(ct, position)
18233     {
18234         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18235     },
18236     
18237     setActive : function(state)
18238     {
18239         Roo.log("panel - set active " + this.tabId + "=" + state);
18240         
18241         this.active = state;
18242         if (!state) {
18243             this.el.removeClass('active');
18244             
18245         } else  if (!this.el.hasClass('active')) {
18246             this.el.addClass('active');
18247         }
18248         
18249         this.fireEvent('changed', this, state);
18250     },
18251     
18252     onClick : function(e)
18253     {
18254         e.preventDefault();
18255         
18256         if(!this.href.length){
18257             return;
18258         }
18259         
18260         window.location.href = this.href;
18261     },
18262     
18263     startX : 0,
18264     startY : 0,
18265     endX : 0,
18266     endY : 0,
18267     swiping : false,
18268     
18269     onTouchStart : function(e)
18270     {
18271         this.swiping = false;
18272         
18273         this.startX = e.browserEvent.touches[0].clientX;
18274         this.startY = e.browserEvent.touches[0].clientY;
18275     },
18276     
18277     onTouchMove : function(e)
18278     {
18279         this.swiping = true;
18280         
18281         this.endX = e.browserEvent.touches[0].clientX;
18282         this.endY = e.browserEvent.touches[0].clientY;
18283     },
18284     
18285     onTouchEnd : function(e)
18286     {
18287         if(!this.swiping){
18288             this.onClick(e);
18289             return;
18290         }
18291         
18292         var tabGroup = this.parent();
18293         
18294         if(this.endX > this.startX){ // swiping right
18295             tabGroup.showPanelPrev();
18296             return;
18297         }
18298         
18299         if(this.startX > this.endX){ // swiping left
18300             tabGroup.showPanelNext();
18301             return;
18302         }
18303     }
18304     
18305     
18306 });
18307  
18308
18309  
18310
18311  /*
18312  * - LGPL
18313  *
18314  * DateField
18315  * 
18316  */
18317
18318 /**
18319  * @class Roo.bootstrap.DateField
18320  * @extends Roo.bootstrap.Input
18321  * Bootstrap DateField class
18322  * @cfg {Number} weekStart default 0
18323  * @cfg {String} viewMode default empty, (months|years)
18324  * @cfg {String} minViewMode default empty, (months|years)
18325  * @cfg {Number} startDate default -Infinity
18326  * @cfg {Number} endDate default Infinity
18327  * @cfg {Boolean} todayHighlight default false
18328  * @cfg {Boolean} todayBtn default false
18329  * @cfg {Boolean} calendarWeeks default false
18330  * @cfg {Object} daysOfWeekDisabled default empty
18331  * @cfg {Boolean} singleMode default false (true | false)
18332  * 
18333  * @cfg {Boolean} keyboardNavigation default true
18334  * @cfg {String} language default en
18335  * 
18336  * @constructor
18337  * Create a new DateField
18338  * @param {Object} config The config object
18339  */
18340
18341 Roo.bootstrap.DateField = function(config){
18342     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18343      this.addEvents({
18344             /**
18345              * @event show
18346              * Fires when this field show.
18347              * @param {Roo.bootstrap.DateField} this
18348              * @param {Mixed} date The date value
18349              */
18350             show : true,
18351             /**
18352              * @event show
18353              * Fires when this field hide.
18354              * @param {Roo.bootstrap.DateField} this
18355              * @param {Mixed} date The date value
18356              */
18357             hide : true,
18358             /**
18359              * @event select
18360              * Fires when select a date.
18361              * @param {Roo.bootstrap.DateField} this
18362              * @param {Mixed} date The date value
18363              */
18364             select : true,
18365             /**
18366              * @event beforeselect
18367              * Fires when before select a date.
18368              * @param {Roo.bootstrap.DateField} this
18369              * @param {Mixed} date The date value
18370              */
18371             beforeselect : true
18372         });
18373 };
18374
18375 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18376     
18377     /**
18378      * @cfg {String} format
18379      * The default date format string which can be overriden for localization support.  The format must be
18380      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18381      */
18382     format : "m/d/y",
18383     /**
18384      * @cfg {String} altFormats
18385      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18386      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18387      */
18388     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18389     
18390     weekStart : 0,
18391     
18392     viewMode : '',
18393     
18394     minViewMode : '',
18395     
18396     todayHighlight : false,
18397     
18398     todayBtn: false,
18399     
18400     language: 'en',
18401     
18402     keyboardNavigation: true,
18403     
18404     calendarWeeks: false,
18405     
18406     startDate: -Infinity,
18407     
18408     endDate: Infinity,
18409     
18410     daysOfWeekDisabled: [],
18411     
18412     _events: [],
18413     
18414     singleMode : false,
18415     
18416     UTCDate: function()
18417     {
18418         return new Date(Date.UTC.apply(Date, arguments));
18419     },
18420     
18421     UTCToday: function()
18422     {
18423         var today = new Date();
18424         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18425     },
18426     
18427     getDate: function() {
18428             var d = this.getUTCDate();
18429             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18430     },
18431     
18432     getUTCDate: function() {
18433             return this.date;
18434     },
18435     
18436     setDate: function(d) {
18437             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18438     },
18439     
18440     setUTCDate: function(d) {
18441             this.date = d;
18442             this.setValue(this.formatDate(this.date));
18443     },
18444         
18445     onRender: function(ct, position)
18446     {
18447         
18448         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18449         
18450         this.language = this.language || 'en';
18451         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18452         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18453         
18454         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18455         this.format = this.format || 'm/d/y';
18456         this.isInline = false;
18457         this.isInput = true;
18458         this.component = this.el.select('.add-on', true).first() || false;
18459         this.component = (this.component && this.component.length === 0) ? false : this.component;
18460         this.hasInput = this.component && this.inputEl().length;
18461         
18462         if (typeof(this.minViewMode === 'string')) {
18463             switch (this.minViewMode) {
18464                 case 'months':
18465                     this.minViewMode = 1;
18466                     break;
18467                 case 'years':
18468                     this.minViewMode = 2;
18469                     break;
18470                 default:
18471                     this.minViewMode = 0;
18472                     break;
18473             }
18474         }
18475         
18476         if (typeof(this.viewMode === 'string')) {
18477             switch (this.viewMode) {
18478                 case 'months':
18479                     this.viewMode = 1;
18480                     break;
18481                 case 'years':
18482                     this.viewMode = 2;
18483                     break;
18484                 default:
18485                     this.viewMode = 0;
18486                     break;
18487             }
18488         }
18489                 
18490         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18491         
18492 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18493         
18494         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18495         
18496         this.picker().on('mousedown', this.onMousedown, this);
18497         this.picker().on('click', this.onClick, this);
18498         
18499         this.picker().addClass('datepicker-dropdown');
18500         
18501         this.startViewMode = this.viewMode;
18502         
18503         if(this.singleMode){
18504             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18505                 v.setVisibilityMode(Roo.Element.DISPLAY);
18506                 v.hide();
18507             });
18508             
18509             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18510                 v.setStyle('width', '189px');
18511             });
18512         }
18513         
18514         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18515             if(!this.calendarWeeks){
18516                 v.remove();
18517                 return;
18518             }
18519             
18520             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18521             v.attr('colspan', function(i, val){
18522                 return parseInt(val) + 1;
18523             });
18524         });
18525                         
18526         
18527         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18528         
18529         this.setStartDate(this.startDate);
18530         this.setEndDate(this.endDate);
18531         
18532         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18533         
18534         this.fillDow();
18535         this.fillMonths();
18536         this.update();
18537         this.showMode();
18538         
18539         if(this.isInline) {
18540             this.show();
18541         }
18542     },
18543     
18544     picker : function()
18545     {
18546         return this.pickerEl;
18547 //        return this.el.select('.datepicker', true).first();
18548     },
18549     
18550     fillDow: function()
18551     {
18552         var dowCnt = this.weekStart;
18553         
18554         var dow = {
18555             tag: 'tr',
18556             cn: [
18557                 
18558             ]
18559         };
18560         
18561         if(this.calendarWeeks){
18562             dow.cn.push({
18563                 tag: 'th',
18564                 cls: 'cw',
18565                 html: '&nbsp;'
18566             })
18567         }
18568         
18569         while (dowCnt < this.weekStart + 7) {
18570             dow.cn.push({
18571                 tag: 'th',
18572                 cls: 'dow',
18573                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18574             });
18575         }
18576         
18577         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18578     },
18579     
18580     fillMonths: function()
18581     {    
18582         var i = 0;
18583         var months = this.picker().select('>.datepicker-months td', true).first();
18584         
18585         months.dom.innerHTML = '';
18586         
18587         while (i < 12) {
18588             var month = {
18589                 tag: 'span',
18590                 cls: 'month',
18591                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18592             };
18593             
18594             months.createChild(month);
18595         }
18596         
18597     },
18598     
18599     update: function()
18600     {
18601         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;
18602         
18603         if (this.date < this.startDate) {
18604             this.viewDate = new Date(this.startDate);
18605         } else if (this.date > this.endDate) {
18606             this.viewDate = new Date(this.endDate);
18607         } else {
18608             this.viewDate = new Date(this.date);
18609         }
18610         
18611         this.fill();
18612     },
18613     
18614     fill: function() 
18615     {
18616         var d = new Date(this.viewDate),
18617                 year = d.getUTCFullYear(),
18618                 month = d.getUTCMonth(),
18619                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18620                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18621                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18622                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18623                 currentDate = this.date && this.date.valueOf(),
18624                 today = this.UTCToday();
18625         
18626         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18627         
18628 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18629         
18630 //        this.picker.select('>tfoot th.today').
18631 //                                              .text(dates[this.language].today)
18632 //                                              .toggle(this.todayBtn !== false);
18633     
18634         this.updateNavArrows();
18635         this.fillMonths();
18636                                                 
18637         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18638         
18639         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18640          
18641         prevMonth.setUTCDate(day);
18642         
18643         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18644         
18645         var nextMonth = new Date(prevMonth);
18646         
18647         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18648         
18649         nextMonth = nextMonth.valueOf();
18650         
18651         var fillMonths = false;
18652         
18653         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18654         
18655         while(prevMonth.valueOf() < nextMonth) {
18656             var clsName = '';
18657             
18658             if (prevMonth.getUTCDay() === this.weekStart) {
18659                 if(fillMonths){
18660                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18661                 }
18662                     
18663                 fillMonths = {
18664                     tag: 'tr',
18665                     cn: []
18666                 };
18667                 
18668                 if(this.calendarWeeks){
18669                     // ISO 8601: First week contains first thursday.
18670                     // ISO also states week starts on Monday, but we can be more abstract here.
18671                     var
18672                     // Start of current week: based on weekstart/current date
18673                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18674                     // Thursday of this week
18675                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18676                     // First Thursday of year, year from thursday
18677                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18678                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18679                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18680                     
18681                     fillMonths.cn.push({
18682                         tag: 'td',
18683                         cls: 'cw',
18684                         html: calWeek
18685                     });
18686                 }
18687             }
18688             
18689             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18690                 clsName += ' old';
18691             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18692                 clsName += ' new';
18693             }
18694             if (this.todayHighlight &&
18695                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18696                 prevMonth.getUTCMonth() == today.getMonth() &&
18697                 prevMonth.getUTCDate() == today.getDate()) {
18698                 clsName += ' today';
18699             }
18700             
18701             if (currentDate && prevMonth.valueOf() === currentDate) {
18702                 clsName += ' active';
18703             }
18704             
18705             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18706                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18707                     clsName += ' disabled';
18708             }
18709             
18710             fillMonths.cn.push({
18711                 tag: 'td',
18712                 cls: 'day ' + clsName,
18713                 html: prevMonth.getDate()
18714             });
18715             
18716             prevMonth.setDate(prevMonth.getDate()+1);
18717         }
18718           
18719         var currentYear = this.date && this.date.getUTCFullYear();
18720         var currentMonth = this.date && this.date.getUTCMonth();
18721         
18722         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18723         
18724         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18725             v.removeClass('active');
18726             
18727             if(currentYear === year && k === currentMonth){
18728                 v.addClass('active');
18729             }
18730             
18731             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18732                 v.addClass('disabled');
18733             }
18734             
18735         });
18736         
18737         
18738         year = parseInt(year/10, 10) * 10;
18739         
18740         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18741         
18742         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18743         
18744         year -= 1;
18745         for (var i = -1; i < 11; i++) {
18746             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18747                 tag: 'span',
18748                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18749                 html: year
18750             });
18751             
18752             year += 1;
18753         }
18754     },
18755     
18756     showMode: function(dir) 
18757     {
18758         if (dir) {
18759             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18760         }
18761         
18762         Roo.each(this.picker().select('>div',true).elements, function(v){
18763             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18764             v.hide();
18765         });
18766         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18767     },
18768     
18769     place: function()
18770     {
18771         if(this.isInline) {
18772             return;
18773         }
18774         
18775         this.picker().removeClass(['bottom', 'top']);
18776         
18777         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18778             /*
18779              * place to the top of element!
18780              *
18781              */
18782             
18783             this.picker().addClass('top');
18784             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18785             
18786             return;
18787         }
18788         
18789         this.picker().addClass('bottom');
18790         
18791         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18792     },
18793     
18794     parseDate : function(value)
18795     {
18796         if(!value || value instanceof Date){
18797             return value;
18798         }
18799         var v = Date.parseDate(value, this.format);
18800         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18801             v = Date.parseDate(value, 'Y-m-d');
18802         }
18803         if(!v && this.altFormats){
18804             if(!this.altFormatsArray){
18805                 this.altFormatsArray = this.altFormats.split("|");
18806             }
18807             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18808                 v = Date.parseDate(value, this.altFormatsArray[i]);
18809             }
18810         }
18811         return v;
18812     },
18813     
18814     formatDate : function(date, fmt)
18815     {   
18816         return (!date || !(date instanceof Date)) ?
18817         date : date.dateFormat(fmt || this.format);
18818     },
18819     
18820     onFocus : function()
18821     {
18822         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18823         this.show();
18824     },
18825     
18826     onBlur : function()
18827     {
18828         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18829         
18830         var d = this.inputEl().getValue();
18831         
18832         this.setValue(d);
18833                 
18834         this.hide();
18835     },
18836     
18837     show : function()
18838     {
18839         this.picker().show();
18840         this.update();
18841         this.place();
18842         
18843         this.fireEvent('show', this, this.date);
18844     },
18845     
18846     hide : function()
18847     {
18848         if(this.isInline) {
18849             return;
18850         }
18851         this.picker().hide();
18852         this.viewMode = this.startViewMode;
18853         this.showMode();
18854         
18855         this.fireEvent('hide', this, this.date);
18856         
18857     },
18858     
18859     onMousedown: function(e)
18860     {
18861         e.stopPropagation();
18862         e.preventDefault();
18863     },
18864     
18865     keyup: function(e)
18866     {
18867         Roo.bootstrap.DateField.superclass.keyup.call(this);
18868         this.update();
18869     },
18870
18871     setValue: function(v)
18872     {
18873         if(this.fireEvent('beforeselect', this, v) !== false){
18874             var d = new Date(this.parseDate(v) ).clearTime();
18875         
18876             if(isNaN(d.getTime())){
18877                 this.date = this.viewDate = '';
18878                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18879                 return;
18880             }
18881
18882             v = this.formatDate(d);
18883
18884             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18885
18886             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18887
18888             this.update();
18889
18890             this.fireEvent('select', this, this.date);
18891         }
18892     },
18893     
18894     getValue: function()
18895     {
18896         return this.formatDate(this.date);
18897     },
18898     
18899     fireKey: function(e)
18900     {
18901         if (!this.picker().isVisible()){
18902             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18903                 this.show();
18904             }
18905             return;
18906         }
18907         
18908         var dateChanged = false,
18909         dir, day, month,
18910         newDate, newViewDate;
18911         
18912         switch(e.keyCode){
18913             case 27: // escape
18914                 this.hide();
18915                 e.preventDefault();
18916                 break;
18917             case 37: // left
18918             case 39: // right
18919                 if (!this.keyboardNavigation) {
18920                     break;
18921                 }
18922                 dir = e.keyCode == 37 ? -1 : 1;
18923                 
18924                 if (e.ctrlKey){
18925                     newDate = this.moveYear(this.date, dir);
18926                     newViewDate = this.moveYear(this.viewDate, dir);
18927                 } else if (e.shiftKey){
18928                     newDate = this.moveMonth(this.date, dir);
18929                     newViewDate = this.moveMonth(this.viewDate, dir);
18930                 } else {
18931                     newDate = new Date(this.date);
18932                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18933                     newViewDate = new Date(this.viewDate);
18934                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18935                 }
18936                 if (this.dateWithinRange(newDate)){
18937                     this.date = newDate;
18938                     this.viewDate = newViewDate;
18939                     this.setValue(this.formatDate(this.date));
18940 //                    this.update();
18941                     e.preventDefault();
18942                     dateChanged = true;
18943                 }
18944                 break;
18945             case 38: // up
18946             case 40: // down
18947                 if (!this.keyboardNavigation) {
18948                     break;
18949                 }
18950                 dir = e.keyCode == 38 ? -1 : 1;
18951                 if (e.ctrlKey){
18952                     newDate = this.moveYear(this.date, dir);
18953                     newViewDate = this.moveYear(this.viewDate, dir);
18954                 } else if (e.shiftKey){
18955                     newDate = this.moveMonth(this.date, dir);
18956                     newViewDate = this.moveMonth(this.viewDate, dir);
18957                 } else {
18958                     newDate = new Date(this.date);
18959                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18960                     newViewDate = new Date(this.viewDate);
18961                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18962                 }
18963                 if (this.dateWithinRange(newDate)){
18964                     this.date = newDate;
18965                     this.viewDate = newViewDate;
18966                     this.setValue(this.formatDate(this.date));
18967 //                    this.update();
18968                     e.preventDefault();
18969                     dateChanged = true;
18970                 }
18971                 break;
18972             case 13: // enter
18973                 this.setValue(this.formatDate(this.date));
18974                 this.hide();
18975                 e.preventDefault();
18976                 break;
18977             case 9: // tab
18978                 this.setValue(this.formatDate(this.date));
18979                 this.hide();
18980                 break;
18981             case 16: // shift
18982             case 17: // ctrl
18983             case 18: // alt
18984                 break;
18985             default :
18986                 this.hide();
18987                 
18988         }
18989     },
18990     
18991     
18992     onClick: function(e) 
18993     {
18994         e.stopPropagation();
18995         e.preventDefault();
18996         
18997         var target = e.getTarget();
18998         
18999         if(target.nodeName.toLowerCase() === 'i'){
19000             target = Roo.get(target).dom.parentNode;
19001         }
19002         
19003         var nodeName = target.nodeName;
19004         var className = target.className;
19005         var html = target.innerHTML;
19006         //Roo.log(nodeName);
19007         
19008         switch(nodeName.toLowerCase()) {
19009             case 'th':
19010                 switch(className) {
19011                     case 'switch':
19012                         this.showMode(1);
19013                         break;
19014                     case 'prev':
19015                     case 'next':
19016                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19017                         switch(this.viewMode){
19018                                 case 0:
19019                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19020                                         break;
19021                                 case 1:
19022                                 case 2:
19023                                         this.viewDate = this.moveYear(this.viewDate, dir);
19024                                         break;
19025                         }
19026                         this.fill();
19027                         break;
19028                     case 'today':
19029                         var date = new Date();
19030                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19031 //                        this.fill()
19032                         this.setValue(this.formatDate(this.date));
19033                         
19034                         this.hide();
19035                         break;
19036                 }
19037                 break;
19038             case 'span':
19039                 if (className.indexOf('disabled') < 0) {
19040                     this.viewDate.setUTCDate(1);
19041                     if (className.indexOf('month') > -1) {
19042                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19043                     } else {
19044                         var year = parseInt(html, 10) || 0;
19045                         this.viewDate.setUTCFullYear(year);
19046                         
19047                     }
19048                     
19049                     if(this.singleMode){
19050                         this.setValue(this.formatDate(this.viewDate));
19051                         this.hide();
19052                         return;
19053                     }
19054                     
19055                     this.showMode(-1);
19056                     this.fill();
19057                 }
19058                 break;
19059                 
19060             case 'td':
19061                 //Roo.log(className);
19062                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19063                     var day = parseInt(html, 10) || 1;
19064                     var year = this.viewDate.getUTCFullYear(),
19065                         month = this.viewDate.getUTCMonth();
19066
19067                     if (className.indexOf('old') > -1) {
19068                         if(month === 0 ){
19069                             month = 11;
19070                             year -= 1;
19071                         }else{
19072                             month -= 1;
19073                         }
19074                     } else if (className.indexOf('new') > -1) {
19075                         if (month == 11) {
19076                             month = 0;
19077                             year += 1;
19078                         } else {
19079                             month += 1;
19080                         }
19081                     }
19082                     //Roo.log([year,month,day]);
19083                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19084                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19085 //                    this.fill();
19086                     //Roo.log(this.formatDate(this.date));
19087                     this.setValue(this.formatDate(this.date));
19088                     this.hide();
19089                 }
19090                 break;
19091         }
19092     },
19093     
19094     setStartDate: function(startDate)
19095     {
19096         this.startDate = startDate || -Infinity;
19097         if (this.startDate !== -Infinity) {
19098             this.startDate = this.parseDate(this.startDate);
19099         }
19100         this.update();
19101         this.updateNavArrows();
19102     },
19103
19104     setEndDate: function(endDate)
19105     {
19106         this.endDate = endDate || Infinity;
19107         if (this.endDate !== Infinity) {
19108             this.endDate = this.parseDate(this.endDate);
19109         }
19110         this.update();
19111         this.updateNavArrows();
19112     },
19113     
19114     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19115     {
19116         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19117         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19118             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19119         }
19120         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19121             return parseInt(d, 10);
19122         });
19123         this.update();
19124         this.updateNavArrows();
19125     },
19126     
19127     updateNavArrows: function() 
19128     {
19129         if(this.singleMode){
19130             return;
19131         }
19132         
19133         var d = new Date(this.viewDate),
19134         year = d.getUTCFullYear(),
19135         month = d.getUTCMonth();
19136         
19137         Roo.each(this.picker().select('.prev', true).elements, function(v){
19138             v.show();
19139             switch (this.viewMode) {
19140                 case 0:
19141
19142                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19143                         v.hide();
19144                     }
19145                     break;
19146                 case 1:
19147                 case 2:
19148                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19149                         v.hide();
19150                     }
19151                     break;
19152             }
19153         });
19154         
19155         Roo.each(this.picker().select('.next', true).elements, function(v){
19156             v.show();
19157             switch (this.viewMode) {
19158                 case 0:
19159
19160                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19161                         v.hide();
19162                     }
19163                     break;
19164                 case 1:
19165                 case 2:
19166                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19167                         v.hide();
19168                     }
19169                     break;
19170             }
19171         })
19172     },
19173     
19174     moveMonth: function(date, dir)
19175     {
19176         if (!dir) {
19177             return date;
19178         }
19179         var new_date = new Date(date.valueOf()),
19180         day = new_date.getUTCDate(),
19181         month = new_date.getUTCMonth(),
19182         mag = Math.abs(dir),
19183         new_month, test;
19184         dir = dir > 0 ? 1 : -1;
19185         if (mag == 1){
19186             test = dir == -1
19187             // If going back one month, make sure month is not current month
19188             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19189             ? function(){
19190                 return new_date.getUTCMonth() == month;
19191             }
19192             // If going forward one month, make sure month is as expected
19193             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19194             : function(){
19195                 return new_date.getUTCMonth() != new_month;
19196             };
19197             new_month = month + dir;
19198             new_date.setUTCMonth(new_month);
19199             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19200             if (new_month < 0 || new_month > 11) {
19201                 new_month = (new_month + 12) % 12;
19202             }
19203         } else {
19204             // For magnitudes >1, move one month at a time...
19205             for (var i=0; i<mag; i++) {
19206                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19207                 new_date = this.moveMonth(new_date, dir);
19208             }
19209             // ...then reset the day, keeping it in the new month
19210             new_month = new_date.getUTCMonth();
19211             new_date.setUTCDate(day);
19212             test = function(){
19213                 return new_month != new_date.getUTCMonth();
19214             };
19215         }
19216         // Common date-resetting loop -- if date is beyond end of month, make it
19217         // end of month
19218         while (test()){
19219             new_date.setUTCDate(--day);
19220             new_date.setUTCMonth(new_month);
19221         }
19222         return new_date;
19223     },
19224
19225     moveYear: function(date, dir)
19226     {
19227         return this.moveMonth(date, dir*12);
19228     },
19229
19230     dateWithinRange: function(date)
19231     {
19232         return date >= this.startDate && date <= this.endDate;
19233     },
19234
19235     
19236     remove: function() 
19237     {
19238         this.picker().remove();
19239     },
19240     
19241     validateValue : function(value)
19242     {
19243         if(this.getVisibilityEl().hasClass('hidden')){
19244             return true;
19245         }
19246         
19247         if(value.length < 1)  {
19248             if(this.allowBlank){
19249                 return true;
19250             }
19251             return false;
19252         }
19253         
19254         if(value.length < this.minLength){
19255             return false;
19256         }
19257         if(value.length > this.maxLength){
19258             return false;
19259         }
19260         if(this.vtype){
19261             var vt = Roo.form.VTypes;
19262             if(!vt[this.vtype](value, this)){
19263                 return false;
19264             }
19265         }
19266         if(typeof this.validator == "function"){
19267             var msg = this.validator(value);
19268             if(msg !== true){
19269                 return false;
19270             }
19271         }
19272         
19273         if(this.regex && !this.regex.test(value)){
19274             return false;
19275         }
19276         
19277         if(typeof(this.parseDate(value)) == 'undefined'){
19278             return false;
19279         }
19280         
19281         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19282             return false;
19283         }      
19284         
19285         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19286             return false;
19287         } 
19288         
19289         
19290         return true;
19291     },
19292     
19293     setVisible : function(visible)
19294     {
19295         if(!this.getEl()){
19296             return;
19297         }
19298         
19299         this.getEl().removeClass('hidden');
19300         
19301         if(visible){
19302             return;
19303         }
19304         
19305         this.getEl().addClass('hidden');
19306     }
19307    
19308 });
19309
19310 Roo.apply(Roo.bootstrap.DateField,  {
19311     
19312     head : {
19313         tag: 'thead',
19314         cn: [
19315         {
19316             tag: 'tr',
19317             cn: [
19318             {
19319                 tag: 'th',
19320                 cls: 'prev',
19321                 html: '<i class="fa fa-arrow-left"/>'
19322             },
19323             {
19324                 tag: 'th',
19325                 cls: 'switch',
19326                 colspan: '5'
19327             },
19328             {
19329                 tag: 'th',
19330                 cls: 'next',
19331                 html: '<i class="fa fa-arrow-right"/>'
19332             }
19333
19334             ]
19335         }
19336         ]
19337     },
19338     
19339     content : {
19340         tag: 'tbody',
19341         cn: [
19342         {
19343             tag: 'tr',
19344             cn: [
19345             {
19346                 tag: 'td',
19347                 colspan: '7'
19348             }
19349             ]
19350         }
19351         ]
19352     },
19353     
19354     footer : {
19355         tag: 'tfoot',
19356         cn: [
19357         {
19358             tag: 'tr',
19359             cn: [
19360             {
19361                 tag: 'th',
19362                 colspan: '7',
19363                 cls: 'today'
19364             }
19365                     
19366             ]
19367         }
19368         ]
19369     },
19370     
19371     dates:{
19372         en: {
19373             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19374             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19375             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19376             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19377             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19378             today: "Today"
19379         }
19380     },
19381     
19382     modes: [
19383     {
19384         clsName: 'days',
19385         navFnc: 'Month',
19386         navStep: 1
19387     },
19388     {
19389         clsName: 'months',
19390         navFnc: 'FullYear',
19391         navStep: 1
19392     },
19393     {
19394         clsName: 'years',
19395         navFnc: 'FullYear',
19396         navStep: 10
19397     }]
19398 });
19399
19400 Roo.apply(Roo.bootstrap.DateField,  {
19401   
19402     template : {
19403         tag: 'div',
19404         cls: 'datepicker dropdown-menu roo-dynamic',
19405         cn: [
19406         {
19407             tag: 'div',
19408             cls: 'datepicker-days',
19409             cn: [
19410             {
19411                 tag: 'table',
19412                 cls: 'table-condensed',
19413                 cn:[
19414                 Roo.bootstrap.DateField.head,
19415                 {
19416                     tag: 'tbody'
19417                 },
19418                 Roo.bootstrap.DateField.footer
19419                 ]
19420             }
19421             ]
19422         },
19423         {
19424             tag: 'div',
19425             cls: 'datepicker-months',
19426             cn: [
19427             {
19428                 tag: 'table',
19429                 cls: 'table-condensed',
19430                 cn:[
19431                 Roo.bootstrap.DateField.head,
19432                 Roo.bootstrap.DateField.content,
19433                 Roo.bootstrap.DateField.footer
19434                 ]
19435             }
19436             ]
19437         },
19438         {
19439             tag: 'div',
19440             cls: 'datepicker-years',
19441             cn: [
19442             {
19443                 tag: 'table',
19444                 cls: 'table-condensed',
19445                 cn:[
19446                 Roo.bootstrap.DateField.head,
19447                 Roo.bootstrap.DateField.content,
19448                 Roo.bootstrap.DateField.footer
19449                 ]
19450             }
19451             ]
19452         }
19453         ]
19454     }
19455 });
19456
19457  
19458
19459  /*
19460  * - LGPL
19461  *
19462  * TimeField
19463  * 
19464  */
19465
19466 /**
19467  * @class Roo.bootstrap.TimeField
19468  * @extends Roo.bootstrap.Input
19469  * Bootstrap DateField class
19470  * 
19471  * 
19472  * @constructor
19473  * Create a new TimeField
19474  * @param {Object} config The config object
19475  */
19476
19477 Roo.bootstrap.TimeField = function(config){
19478     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19479     this.addEvents({
19480             /**
19481              * @event show
19482              * Fires when this field show.
19483              * @param {Roo.bootstrap.DateField} thisthis
19484              * @param {Mixed} date The date value
19485              */
19486             show : true,
19487             /**
19488              * @event show
19489              * Fires when this field hide.
19490              * @param {Roo.bootstrap.DateField} this
19491              * @param {Mixed} date The date value
19492              */
19493             hide : true,
19494             /**
19495              * @event select
19496              * Fires when select a date.
19497              * @param {Roo.bootstrap.DateField} this
19498              * @param {Mixed} date The date value
19499              */
19500             select : true
19501         });
19502 };
19503
19504 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19505     
19506     /**
19507      * @cfg {String} format
19508      * The default time format string which can be overriden for localization support.  The format must be
19509      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19510      */
19511     format : "H:i",
19512        
19513     onRender: function(ct, position)
19514     {
19515         
19516         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19517                 
19518         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19519         
19520         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19521         
19522         this.pop = this.picker().select('>.datepicker-time',true).first();
19523         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19524         
19525         this.picker().on('mousedown', this.onMousedown, this);
19526         this.picker().on('click', this.onClick, this);
19527         
19528         this.picker().addClass('datepicker-dropdown');
19529     
19530         this.fillTime();
19531         this.update();
19532             
19533         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19534         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19535         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19536         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19537         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19538         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19539
19540     },
19541     
19542     fireKey: function(e){
19543         if (!this.picker().isVisible()){
19544             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19545                 this.show();
19546             }
19547             return;
19548         }
19549
19550         e.preventDefault();
19551         
19552         switch(e.keyCode){
19553             case 27: // escape
19554                 this.hide();
19555                 break;
19556             case 37: // left
19557             case 39: // right
19558                 this.onTogglePeriod();
19559                 break;
19560             case 38: // up
19561                 this.onIncrementMinutes();
19562                 break;
19563             case 40: // down
19564                 this.onDecrementMinutes();
19565                 break;
19566             case 13: // enter
19567             case 9: // tab
19568                 this.setTime();
19569                 break;
19570         }
19571     },
19572     
19573     onClick: function(e) {
19574         e.stopPropagation();
19575         e.preventDefault();
19576     },
19577     
19578     picker : function()
19579     {
19580         return this.el.select('.datepicker', true).first();
19581     },
19582     
19583     fillTime: function()
19584     {    
19585         var time = this.pop.select('tbody', true).first();
19586         
19587         time.dom.innerHTML = '';
19588         
19589         time.createChild({
19590             tag: 'tr',
19591             cn: [
19592                 {
19593                     tag: 'td',
19594                     cn: [
19595                         {
19596                             tag: 'a',
19597                             href: '#',
19598                             cls: 'btn',
19599                             cn: [
19600                                 {
19601                                     tag: 'span',
19602                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19603                                 }
19604                             ]
19605                         } 
19606                     ]
19607                 },
19608                 {
19609                     tag: 'td',
19610                     cls: 'separator'
19611                 },
19612                 {
19613                     tag: 'td',
19614                     cn: [
19615                         {
19616                             tag: 'a',
19617                             href: '#',
19618                             cls: 'btn',
19619                             cn: [
19620                                 {
19621                                     tag: 'span',
19622                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19623                                 }
19624                             ]
19625                         }
19626                     ]
19627                 },
19628                 {
19629                     tag: 'td',
19630                     cls: 'separator'
19631                 }
19632             ]
19633         });
19634         
19635         time.createChild({
19636             tag: 'tr',
19637             cn: [
19638                 {
19639                     tag: 'td',
19640                     cn: [
19641                         {
19642                             tag: 'span',
19643                             cls: 'timepicker-hour',
19644                             html: '00'
19645                         }  
19646                     ]
19647                 },
19648                 {
19649                     tag: 'td',
19650                     cls: 'separator',
19651                     html: ':'
19652                 },
19653                 {
19654                     tag: 'td',
19655                     cn: [
19656                         {
19657                             tag: 'span',
19658                             cls: 'timepicker-minute',
19659                             html: '00'
19660                         }  
19661                     ]
19662                 },
19663                 {
19664                     tag: 'td',
19665                     cls: 'separator'
19666                 },
19667                 {
19668                     tag: 'td',
19669                     cn: [
19670                         {
19671                             tag: 'button',
19672                             type: 'button',
19673                             cls: 'btn btn-primary period',
19674                             html: 'AM'
19675                             
19676                         }
19677                     ]
19678                 }
19679             ]
19680         });
19681         
19682         time.createChild({
19683             tag: 'tr',
19684             cn: [
19685                 {
19686                     tag: 'td',
19687                     cn: [
19688                         {
19689                             tag: 'a',
19690                             href: '#',
19691                             cls: 'btn',
19692                             cn: [
19693                                 {
19694                                     tag: 'span',
19695                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19696                                 }
19697                             ]
19698                         }
19699                     ]
19700                 },
19701                 {
19702                     tag: 'td',
19703                     cls: 'separator'
19704                 },
19705                 {
19706                     tag: 'td',
19707                     cn: [
19708                         {
19709                             tag: 'a',
19710                             href: '#',
19711                             cls: 'btn',
19712                             cn: [
19713                                 {
19714                                     tag: 'span',
19715                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19716                                 }
19717                             ]
19718                         }
19719                     ]
19720                 },
19721                 {
19722                     tag: 'td',
19723                     cls: 'separator'
19724                 }
19725             ]
19726         });
19727         
19728     },
19729     
19730     update: function()
19731     {
19732         
19733         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19734         
19735         this.fill();
19736     },
19737     
19738     fill: function() 
19739     {
19740         var hours = this.time.getHours();
19741         var minutes = this.time.getMinutes();
19742         var period = 'AM';
19743         
19744         if(hours > 11){
19745             period = 'PM';
19746         }
19747         
19748         if(hours == 0){
19749             hours = 12;
19750         }
19751         
19752         
19753         if(hours > 12){
19754             hours = hours - 12;
19755         }
19756         
19757         if(hours < 10){
19758             hours = '0' + hours;
19759         }
19760         
19761         if(minutes < 10){
19762             minutes = '0' + minutes;
19763         }
19764         
19765         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19766         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19767         this.pop.select('button', true).first().dom.innerHTML = period;
19768         
19769     },
19770     
19771     place: function()
19772     {   
19773         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19774         
19775         var cls = ['bottom'];
19776         
19777         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19778             cls.pop();
19779             cls.push('top');
19780         }
19781         
19782         cls.push('right');
19783         
19784         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19785             cls.pop();
19786             cls.push('left');
19787         }
19788         
19789         this.picker().addClass(cls.join('-'));
19790         
19791         var _this = this;
19792         
19793         Roo.each(cls, function(c){
19794             if(c == 'bottom'){
19795                 _this.picker().setTop(_this.inputEl().getHeight());
19796                 return;
19797             }
19798             if(c == 'top'){
19799                 _this.picker().setTop(0 - _this.picker().getHeight());
19800                 return;
19801             }
19802             
19803             if(c == 'left'){
19804                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19805                 return;
19806             }
19807             if(c == 'right'){
19808                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19809                 return;
19810             }
19811         });
19812         
19813     },
19814   
19815     onFocus : function()
19816     {
19817         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19818         this.show();
19819     },
19820     
19821     onBlur : function()
19822     {
19823         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19824         this.hide();
19825     },
19826     
19827     show : function()
19828     {
19829         this.picker().show();
19830         this.pop.show();
19831         this.update();
19832         this.place();
19833         
19834         this.fireEvent('show', this, this.date);
19835     },
19836     
19837     hide : function()
19838     {
19839         this.picker().hide();
19840         this.pop.hide();
19841         
19842         this.fireEvent('hide', this, this.date);
19843     },
19844     
19845     setTime : function()
19846     {
19847         this.hide();
19848         this.setValue(this.time.format(this.format));
19849         
19850         this.fireEvent('select', this, this.date);
19851         
19852         
19853     },
19854     
19855     onMousedown: function(e){
19856         e.stopPropagation();
19857         e.preventDefault();
19858     },
19859     
19860     onIncrementHours: function()
19861     {
19862         Roo.log('onIncrementHours');
19863         this.time = this.time.add(Date.HOUR, 1);
19864         this.update();
19865         
19866     },
19867     
19868     onDecrementHours: function()
19869     {
19870         Roo.log('onDecrementHours');
19871         this.time = this.time.add(Date.HOUR, -1);
19872         this.update();
19873     },
19874     
19875     onIncrementMinutes: function()
19876     {
19877         Roo.log('onIncrementMinutes');
19878         this.time = this.time.add(Date.MINUTE, 1);
19879         this.update();
19880     },
19881     
19882     onDecrementMinutes: function()
19883     {
19884         Roo.log('onDecrementMinutes');
19885         this.time = this.time.add(Date.MINUTE, -1);
19886         this.update();
19887     },
19888     
19889     onTogglePeriod: function()
19890     {
19891         Roo.log('onTogglePeriod');
19892         this.time = this.time.add(Date.HOUR, 12);
19893         this.update();
19894     }
19895     
19896    
19897 });
19898
19899 Roo.apply(Roo.bootstrap.TimeField,  {
19900     
19901     content : {
19902         tag: 'tbody',
19903         cn: [
19904             {
19905                 tag: 'tr',
19906                 cn: [
19907                 {
19908                     tag: 'td',
19909                     colspan: '7'
19910                 }
19911                 ]
19912             }
19913         ]
19914     },
19915     
19916     footer : {
19917         tag: 'tfoot',
19918         cn: [
19919             {
19920                 tag: 'tr',
19921                 cn: [
19922                 {
19923                     tag: 'th',
19924                     colspan: '7',
19925                     cls: '',
19926                     cn: [
19927                         {
19928                             tag: 'button',
19929                             cls: 'btn btn-info ok',
19930                             html: 'OK'
19931                         }
19932                     ]
19933                 }
19934
19935                 ]
19936             }
19937         ]
19938     }
19939 });
19940
19941 Roo.apply(Roo.bootstrap.TimeField,  {
19942   
19943     template : {
19944         tag: 'div',
19945         cls: 'datepicker dropdown-menu',
19946         cn: [
19947             {
19948                 tag: 'div',
19949                 cls: 'datepicker-time',
19950                 cn: [
19951                 {
19952                     tag: 'table',
19953                     cls: 'table-condensed',
19954                     cn:[
19955                     Roo.bootstrap.TimeField.content,
19956                     Roo.bootstrap.TimeField.footer
19957                     ]
19958                 }
19959                 ]
19960             }
19961         ]
19962     }
19963 });
19964
19965  
19966
19967  /*
19968  * - LGPL
19969  *
19970  * MonthField
19971  * 
19972  */
19973
19974 /**
19975  * @class Roo.bootstrap.MonthField
19976  * @extends Roo.bootstrap.Input
19977  * Bootstrap MonthField class
19978  * 
19979  * @cfg {String} language default en
19980  * 
19981  * @constructor
19982  * Create a new MonthField
19983  * @param {Object} config The config object
19984  */
19985
19986 Roo.bootstrap.MonthField = function(config){
19987     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19988     
19989     this.addEvents({
19990         /**
19991          * @event show
19992          * Fires when this field show.
19993          * @param {Roo.bootstrap.MonthField} this
19994          * @param {Mixed} date The date value
19995          */
19996         show : true,
19997         /**
19998          * @event show
19999          * Fires when this field hide.
20000          * @param {Roo.bootstrap.MonthField} this
20001          * @param {Mixed} date The date value
20002          */
20003         hide : true,
20004         /**
20005          * @event select
20006          * Fires when select a date.
20007          * @param {Roo.bootstrap.MonthField} this
20008          * @param {String} oldvalue The old value
20009          * @param {String} newvalue The new value
20010          */
20011         select : true
20012     });
20013 };
20014
20015 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20016     
20017     onRender: function(ct, position)
20018     {
20019         
20020         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20021         
20022         this.language = this.language || 'en';
20023         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20024         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20025         
20026         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20027         this.isInline = false;
20028         this.isInput = true;
20029         this.component = this.el.select('.add-on', true).first() || false;
20030         this.component = (this.component && this.component.length === 0) ? false : this.component;
20031         this.hasInput = this.component && this.inputEL().length;
20032         
20033         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20034         
20035         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20036         
20037         this.picker().on('mousedown', this.onMousedown, this);
20038         this.picker().on('click', this.onClick, this);
20039         
20040         this.picker().addClass('datepicker-dropdown');
20041         
20042         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20043             v.setStyle('width', '189px');
20044         });
20045         
20046         this.fillMonths();
20047         
20048         this.update();
20049         
20050         if(this.isInline) {
20051             this.show();
20052         }
20053         
20054     },
20055     
20056     setValue: function(v, suppressEvent)
20057     {   
20058         var o = this.getValue();
20059         
20060         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20061         
20062         this.update();
20063
20064         if(suppressEvent !== true){
20065             this.fireEvent('select', this, o, v);
20066         }
20067         
20068     },
20069     
20070     getValue: function()
20071     {
20072         return this.value;
20073     },
20074     
20075     onClick: function(e) 
20076     {
20077         e.stopPropagation();
20078         e.preventDefault();
20079         
20080         var target = e.getTarget();
20081         
20082         if(target.nodeName.toLowerCase() === 'i'){
20083             target = Roo.get(target).dom.parentNode;
20084         }
20085         
20086         var nodeName = target.nodeName;
20087         var className = target.className;
20088         var html = target.innerHTML;
20089         
20090         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20091             return;
20092         }
20093         
20094         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20095         
20096         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20097         
20098         this.hide();
20099                         
20100     },
20101     
20102     picker : function()
20103     {
20104         return this.pickerEl;
20105     },
20106     
20107     fillMonths: function()
20108     {    
20109         var i = 0;
20110         var months = this.picker().select('>.datepicker-months td', true).first();
20111         
20112         months.dom.innerHTML = '';
20113         
20114         while (i < 12) {
20115             var month = {
20116                 tag: 'span',
20117                 cls: 'month',
20118                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20119             };
20120             
20121             months.createChild(month);
20122         }
20123         
20124     },
20125     
20126     update: function()
20127     {
20128         var _this = this;
20129         
20130         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20131             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20132         }
20133         
20134         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20135             e.removeClass('active');
20136             
20137             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20138                 e.addClass('active');
20139             }
20140         })
20141     },
20142     
20143     place: function()
20144     {
20145         if(this.isInline) {
20146             return;
20147         }
20148         
20149         this.picker().removeClass(['bottom', 'top']);
20150         
20151         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20152             /*
20153              * place to the top of element!
20154              *
20155              */
20156             
20157             this.picker().addClass('top');
20158             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20159             
20160             return;
20161         }
20162         
20163         this.picker().addClass('bottom');
20164         
20165         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20166     },
20167     
20168     onFocus : function()
20169     {
20170         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20171         this.show();
20172     },
20173     
20174     onBlur : function()
20175     {
20176         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20177         
20178         var d = this.inputEl().getValue();
20179         
20180         this.setValue(d);
20181                 
20182         this.hide();
20183     },
20184     
20185     show : function()
20186     {
20187         this.picker().show();
20188         this.picker().select('>.datepicker-months', true).first().show();
20189         this.update();
20190         this.place();
20191         
20192         this.fireEvent('show', this, this.date);
20193     },
20194     
20195     hide : function()
20196     {
20197         if(this.isInline) {
20198             return;
20199         }
20200         this.picker().hide();
20201         this.fireEvent('hide', this, this.date);
20202         
20203     },
20204     
20205     onMousedown: function(e)
20206     {
20207         e.stopPropagation();
20208         e.preventDefault();
20209     },
20210     
20211     keyup: function(e)
20212     {
20213         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20214         this.update();
20215     },
20216
20217     fireKey: function(e)
20218     {
20219         if (!this.picker().isVisible()){
20220             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20221                 this.show();
20222             }
20223             return;
20224         }
20225         
20226         var dir;
20227         
20228         switch(e.keyCode){
20229             case 27: // escape
20230                 this.hide();
20231                 e.preventDefault();
20232                 break;
20233             case 37: // left
20234             case 39: // right
20235                 dir = e.keyCode == 37 ? -1 : 1;
20236                 
20237                 this.vIndex = this.vIndex + dir;
20238                 
20239                 if(this.vIndex < 0){
20240                     this.vIndex = 0;
20241                 }
20242                 
20243                 if(this.vIndex > 11){
20244                     this.vIndex = 11;
20245                 }
20246                 
20247                 if(isNaN(this.vIndex)){
20248                     this.vIndex = 0;
20249                 }
20250                 
20251                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20252                 
20253                 break;
20254             case 38: // up
20255             case 40: // down
20256                 
20257                 dir = e.keyCode == 38 ? -1 : 1;
20258                 
20259                 this.vIndex = this.vIndex + dir * 4;
20260                 
20261                 if(this.vIndex < 0){
20262                     this.vIndex = 0;
20263                 }
20264                 
20265                 if(this.vIndex > 11){
20266                     this.vIndex = 11;
20267                 }
20268                 
20269                 if(isNaN(this.vIndex)){
20270                     this.vIndex = 0;
20271                 }
20272                 
20273                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20274                 break;
20275                 
20276             case 13: // enter
20277                 
20278                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20279                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20280                 }
20281                 
20282                 this.hide();
20283                 e.preventDefault();
20284                 break;
20285             case 9: // tab
20286                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20287                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20288                 }
20289                 this.hide();
20290                 break;
20291             case 16: // shift
20292             case 17: // ctrl
20293             case 18: // alt
20294                 break;
20295             default :
20296                 this.hide();
20297                 
20298         }
20299     },
20300     
20301     remove: function() 
20302     {
20303         this.picker().remove();
20304     }
20305    
20306 });
20307
20308 Roo.apply(Roo.bootstrap.MonthField,  {
20309     
20310     content : {
20311         tag: 'tbody',
20312         cn: [
20313         {
20314             tag: 'tr',
20315             cn: [
20316             {
20317                 tag: 'td',
20318                 colspan: '7'
20319             }
20320             ]
20321         }
20322         ]
20323     },
20324     
20325     dates:{
20326         en: {
20327             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20328             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20329         }
20330     }
20331 });
20332
20333 Roo.apply(Roo.bootstrap.MonthField,  {
20334   
20335     template : {
20336         tag: 'div',
20337         cls: 'datepicker dropdown-menu roo-dynamic',
20338         cn: [
20339             {
20340                 tag: 'div',
20341                 cls: 'datepicker-months',
20342                 cn: [
20343                 {
20344                     tag: 'table',
20345                     cls: 'table-condensed',
20346                     cn:[
20347                         Roo.bootstrap.DateField.content
20348                     ]
20349                 }
20350                 ]
20351             }
20352         ]
20353     }
20354 });
20355
20356  
20357
20358  
20359  /*
20360  * - LGPL
20361  *
20362  * CheckBox
20363  * 
20364  */
20365
20366 /**
20367  * @class Roo.bootstrap.CheckBox
20368  * @extends Roo.bootstrap.Input
20369  * Bootstrap CheckBox class
20370  * 
20371  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20372  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20373  * @cfg {String} boxLabel The text that appears beside the checkbox
20374  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20375  * @cfg {Boolean} checked initnal the element
20376  * @cfg {Boolean} inline inline the element (default false)
20377  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20378  * @cfg {String} tooltip label tooltip
20379  * 
20380  * @constructor
20381  * Create a new CheckBox
20382  * @param {Object} config The config object
20383  */
20384
20385 Roo.bootstrap.CheckBox = function(config){
20386     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20387    
20388     this.addEvents({
20389         /**
20390         * @event check
20391         * Fires when the element is checked or unchecked.
20392         * @param {Roo.bootstrap.CheckBox} this This input
20393         * @param {Boolean} checked The new checked value
20394         */
20395        check : true,
20396        /**
20397         * @event click
20398         * Fires when the element is click.
20399         * @param {Roo.bootstrap.CheckBox} this This input
20400         */
20401        click : true
20402     });
20403     
20404 };
20405
20406 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20407   
20408     inputType: 'checkbox',
20409     inputValue: 1,
20410     valueOff: 0,
20411     boxLabel: false,
20412     checked: false,
20413     weight : false,
20414     inline: false,
20415     tooltip : '',
20416     
20417     getAutoCreate : function()
20418     {
20419         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20420         
20421         var id = Roo.id();
20422         
20423         var cfg = {};
20424         
20425         cfg.cls = 'form-group ' + this.inputType; //input-group
20426         
20427         if(this.inline){
20428             cfg.cls += ' ' + this.inputType + '-inline';
20429         }
20430         
20431         var input =  {
20432             tag: 'input',
20433             id : id,
20434             type : this.inputType,
20435             value : this.inputValue,
20436             cls : 'roo-' + this.inputType, //'form-box',
20437             placeholder : this.placeholder || ''
20438             
20439         };
20440         
20441         if(this.inputType != 'radio'){
20442             var hidden =  {
20443                 tag: 'input',
20444                 type : 'hidden',
20445                 cls : 'roo-hidden-value',
20446                 value : this.checked ? this.inputValue : this.valueOff
20447             };
20448         }
20449         
20450             
20451         if (this.weight) { // Validity check?
20452             cfg.cls += " " + this.inputType + "-" + this.weight;
20453         }
20454         
20455         if (this.disabled) {
20456             input.disabled=true;
20457         }
20458         
20459         if(this.checked){
20460             input.checked = this.checked;
20461         }
20462         
20463         if (this.name) {
20464             
20465             input.name = this.name;
20466             
20467             if(this.inputType != 'radio'){
20468                 hidden.name = this.name;
20469                 input.name = '_hidden_' + this.name;
20470             }
20471         }
20472         
20473         if (this.size) {
20474             input.cls += ' input-' + this.size;
20475         }
20476         
20477         var settings=this;
20478         
20479         ['xs','sm','md','lg'].map(function(size){
20480             if (settings[size]) {
20481                 cfg.cls += ' col-' + size + '-' + settings[size];
20482             }
20483         });
20484         
20485         var inputblock = input;
20486          
20487         if (this.before || this.after) {
20488             
20489             inputblock = {
20490                 cls : 'input-group',
20491                 cn :  [] 
20492             };
20493             
20494             if (this.before) {
20495                 inputblock.cn.push({
20496                     tag :'span',
20497                     cls : 'input-group-addon',
20498                     html : this.before
20499                 });
20500             }
20501             
20502             inputblock.cn.push(input);
20503             
20504             if(this.inputType != 'radio'){
20505                 inputblock.cn.push(hidden);
20506             }
20507             
20508             if (this.after) {
20509                 inputblock.cn.push({
20510                     tag :'span',
20511                     cls : 'input-group-addon',
20512                     html : this.after
20513                 });
20514             }
20515             
20516         }
20517         
20518         if (align ==='left' && this.fieldLabel.length) {
20519 //                Roo.log("left and has label");
20520             cfg.cn = [
20521                 {
20522                     tag: 'label',
20523                     'for' :  id,
20524                     cls : 'control-label',
20525                     html : this.fieldLabel
20526                 },
20527                 {
20528                     cls : "", 
20529                     cn: [
20530                         inputblock
20531                     ]
20532                 }
20533             ];
20534             
20535             if(this.labelWidth > 12){
20536                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20537             }
20538             
20539             if(this.labelWidth < 13 && this.labelmd == 0){
20540                 this.labelmd = this.labelWidth;
20541             }
20542             
20543             if(this.labellg > 0){
20544                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20545                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20546             }
20547             
20548             if(this.labelmd > 0){
20549                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20550                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20551             }
20552             
20553             if(this.labelsm > 0){
20554                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20555                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20556             }
20557             
20558             if(this.labelxs > 0){
20559                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20560                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20561             }
20562             
20563         } else if ( this.fieldLabel.length) {
20564 //                Roo.log(" label");
20565                 cfg.cn = [
20566                    
20567                     {
20568                         tag: this.boxLabel ? 'span' : 'label',
20569                         'for': id,
20570                         cls: 'control-label box-input-label',
20571                         //cls : 'input-group-addon',
20572                         html : this.fieldLabel
20573                     },
20574                     
20575                     inputblock
20576                     
20577                 ];
20578
20579         } else {
20580             
20581 //                Roo.log(" no label && no align");
20582                 cfg.cn = [  inputblock ] ;
20583                 
20584                 
20585         }
20586         
20587         if(this.boxLabel){
20588              var boxLabelCfg = {
20589                 tag: 'label',
20590                 //'for': id, // box label is handled by onclick - so no for...
20591                 cls: 'box-label',
20592                 html: this.boxLabel
20593             };
20594             
20595             if(this.tooltip){
20596                 boxLabelCfg.tooltip = this.tooltip;
20597             }
20598              
20599             cfg.cn.push(boxLabelCfg);
20600         }
20601         
20602         if(this.inputType != 'radio'){
20603             cfg.cn.push(hidden);
20604         }
20605         
20606         return cfg;
20607         
20608     },
20609     
20610     /**
20611      * return the real input element.
20612      */
20613     inputEl: function ()
20614     {
20615         return this.el.select('input.roo-' + this.inputType,true).first();
20616     },
20617     hiddenEl: function ()
20618     {
20619         return this.el.select('input.roo-hidden-value',true).first();
20620     },
20621     
20622     labelEl: function()
20623     {
20624         return this.el.select('label.control-label',true).first();
20625     },
20626     /* depricated... */
20627     
20628     label: function()
20629     {
20630         return this.labelEl();
20631     },
20632     
20633     boxLabelEl: function()
20634     {
20635         return this.el.select('label.box-label',true).first();
20636     },
20637     
20638     initEvents : function()
20639     {
20640 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20641         
20642         this.inputEl().on('click', this.onClick,  this);
20643         
20644         if (this.boxLabel) { 
20645             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20646         }
20647         
20648         this.startValue = this.getValue();
20649         
20650         if(this.groupId){
20651             Roo.bootstrap.CheckBox.register(this);
20652         }
20653     },
20654     
20655     onClick : function(e)
20656     {   
20657         if(this.fireEvent('click', this, e) !== false){
20658             this.setChecked(!this.checked);
20659         }
20660         
20661     },
20662     
20663     setChecked : function(state,suppressEvent)
20664     {
20665         this.startValue = this.getValue();
20666
20667         if(this.inputType == 'radio'){
20668             
20669             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20670                 e.dom.checked = false;
20671             });
20672             
20673             this.inputEl().dom.checked = true;
20674             
20675             this.inputEl().dom.value = this.inputValue;
20676             
20677             if(suppressEvent !== true){
20678                 this.fireEvent('check', this, true);
20679             }
20680             
20681             this.validate();
20682             
20683             return;
20684         }
20685         
20686         this.checked = state;
20687         
20688         this.inputEl().dom.checked = state;
20689         
20690         
20691         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20692         
20693         if(suppressEvent !== true){
20694             this.fireEvent('check', this, state);
20695         }
20696         
20697         this.validate();
20698     },
20699     
20700     getValue : function()
20701     {
20702         if(this.inputType == 'radio'){
20703             return this.getGroupValue();
20704         }
20705         
20706         return this.hiddenEl().dom.value;
20707         
20708     },
20709     
20710     getGroupValue : function()
20711     {
20712         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20713             return '';
20714         }
20715         
20716         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20717     },
20718     
20719     setValue : function(v,suppressEvent)
20720     {
20721         if(this.inputType == 'radio'){
20722             this.setGroupValue(v, suppressEvent);
20723             return;
20724         }
20725         
20726         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20727         
20728         this.validate();
20729     },
20730     
20731     setGroupValue : function(v, suppressEvent)
20732     {
20733         this.startValue = this.getValue();
20734         
20735         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20736             e.dom.checked = false;
20737             
20738             if(e.dom.value == v){
20739                 e.dom.checked = true;
20740             }
20741         });
20742         
20743         if(suppressEvent !== true){
20744             this.fireEvent('check', this, true);
20745         }
20746
20747         this.validate();
20748         
20749         return;
20750     },
20751     
20752     validate : function()
20753     {
20754         if(this.getVisibilityEl().hasClass('hidden')){
20755             return true;
20756         }
20757         
20758         if(
20759                 this.disabled || 
20760                 (this.inputType == 'radio' && this.validateRadio()) ||
20761                 (this.inputType == 'checkbox' && this.validateCheckbox())
20762         ){
20763             this.markValid();
20764             return true;
20765         }
20766         
20767         this.markInvalid();
20768         return false;
20769     },
20770     
20771     validateRadio : function()
20772     {
20773         if(this.getVisibilityEl().hasClass('hidden')){
20774             return true;
20775         }
20776         
20777         if(this.allowBlank){
20778             return true;
20779         }
20780         
20781         var valid = false;
20782         
20783         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20784             if(!e.dom.checked){
20785                 return;
20786             }
20787             
20788             valid = true;
20789             
20790             return false;
20791         });
20792         
20793         return valid;
20794     },
20795     
20796     validateCheckbox : function()
20797     {
20798         if(!this.groupId){
20799             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20800             //return (this.getValue() == this.inputValue) ? true : false;
20801         }
20802         
20803         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20804         
20805         if(!group){
20806             return false;
20807         }
20808         
20809         var r = false;
20810         
20811         for(var i in group){
20812             if(group[i].el.isVisible(true)){
20813                 r = false;
20814                 break;
20815             }
20816             
20817             r = true;
20818         }
20819         
20820         for(var i in group){
20821             if(r){
20822                 break;
20823             }
20824             
20825             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20826         }
20827         
20828         return r;
20829     },
20830     
20831     /**
20832      * Mark this field as valid
20833      */
20834     markValid : function()
20835     {
20836         var _this = this;
20837         
20838         this.fireEvent('valid', this);
20839         
20840         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20841         
20842         if(this.groupId){
20843             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20844         }
20845         
20846         if(label){
20847             label.markValid();
20848         }
20849
20850         if(this.inputType == 'radio'){
20851             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20852                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20853                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20854             });
20855             
20856             return;
20857         }
20858
20859         if(!this.groupId){
20860             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20861             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20862             return;
20863         }
20864         
20865         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20866         
20867         if(!group){
20868             return;
20869         }
20870         
20871         for(var i in group){
20872             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20873             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20874         }
20875     },
20876     
20877      /**
20878      * Mark this field as invalid
20879      * @param {String} msg The validation message
20880      */
20881     markInvalid : function(msg)
20882     {
20883         if(this.allowBlank){
20884             return;
20885         }
20886         
20887         var _this = this;
20888         
20889         this.fireEvent('invalid', this, msg);
20890         
20891         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20892         
20893         if(this.groupId){
20894             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20895         }
20896         
20897         if(label){
20898             label.markInvalid();
20899         }
20900             
20901         if(this.inputType == 'radio'){
20902             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20903                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20904                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20905             });
20906             
20907             return;
20908         }
20909         
20910         if(!this.groupId){
20911             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20912             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20913             return;
20914         }
20915         
20916         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20917         
20918         if(!group){
20919             return;
20920         }
20921         
20922         for(var i in group){
20923             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20924             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20925         }
20926         
20927     },
20928     
20929     clearInvalid : function()
20930     {
20931         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20932         
20933         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20934         
20935         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20936         
20937         if (label && label.iconEl) {
20938             label.iconEl.removeClass(label.validClass);
20939             label.iconEl.removeClass(label.invalidClass);
20940         }
20941     },
20942     
20943     disable : function()
20944     {
20945         if(this.inputType != 'radio'){
20946             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20947             return;
20948         }
20949         
20950         var _this = this;
20951         
20952         if(this.rendered){
20953             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20954                 _this.getActionEl().addClass(this.disabledClass);
20955                 e.dom.disabled = true;
20956             });
20957         }
20958         
20959         this.disabled = true;
20960         this.fireEvent("disable", this);
20961         return this;
20962     },
20963
20964     enable : function()
20965     {
20966         if(this.inputType != 'radio'){
20967             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20968             return;
20969         }
20970         
20971         var _this = this;
20972         
20973         if(this.rendered){
20974             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20975                 _this.getActionEl().removeClass(this.disabledClass);
20976                 e.dom.disabled = false;
20977             });
20978         }
20979         
20980         this.disabled = false;
20981         this.fireEvent("enable", this);
20982         return this;
20983     },
20984     
20985     setBoxLabel : function(v)
20986     {
20987         this.boxLabel = v;
20988         
20989         if(this.rendered){
20990             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20991         }
20992     }
20993
20994 });
20995
20996 Roo.apply(Roo.bootstrap.CheckBox, {
20997     
20998     groups: {},
20999     
21000      /**
21001     * register a CheckBox Group
21002     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21003     */
21004     register : function(checkbox)
21005     {
21006         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21007             this.groups[checkbox.groupId] = {};
21008         }
21009         
21010         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21011             return;
21012         }
21013         
21014         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21015         
21016     },
21017     /**
21018     * fetch a CheckBox Group based on the group ID
21019     * @param {string} the group ID
21020     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21021     */
21022     get: function(groupId) {
21023         if (typeof(this.groups[groupId]) == 'undefined') {
21024             return false;
21025         }
21026         
21027         return this.groups[groupId] ;
21028     }
21029     
21030     
21031 });
21032 /*
21033  * - LGPL
21034  *
21035  * RadioItem
21036  * 
21037  */
21038
21039 /**
21040  * @class Roo.bootstrap.Radio
21041  * @extends Roo.bootstrap.Component
21042  * Bootstrap Radio class
21043  * @cfg {String} boxLabel - the label associated
21044  * @cfg {String} value - the value of radio
21045  * 
21046  * @constructor
21047  * Create a new Radio
21048  * @param {Object} config The config object
21049  */
21050 Roo.bootstrap.Radio = function(config){
21051     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21052     
21053 };
21054
21055 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21056     
21057     boxLabel : '',
21058     
21059     value : '',
21060     
21061     getAutoCreate : function()
21062     {
21063         var cfg = {
21064             tag : 'div',
21065             cls : 'form-group radio',
21066             cn : [
21067                 {
21068                     tag : 'label',
21069                     cls : 'box-label',
21070                     html : this.boxLabel
21071                 }
21072             ]
21073         };
21074         
21075         return cfg;
21076     },
21077     
21078     initEvents : function() 
21079     {
21080         this.parent().register(this);
21081         
21082         this.el.on('click', this.onClick, this);
21083         
21084     },
21085     
21086     onClick : function(e)
21087     {
21088         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21089             this.setChecked(true);
21090         }
21091     },
21092     
21093     setChecked : function(state, suppressEvent)
21094     {
21095         this.parent().setValue(this.value, suppressEvent);
21096         
21097     },
21098     
21099     setBoxLabel : function(v)
21100     {
21101         this.boxLabel = v;
21102         
21103         if(this.rendered){
21104             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21105         }
21106     }
21107     
21108 });
21109  
21110
21111  /*
21112  * - LGPL
21113  *
21114  * Input
21115  * 
21116  */
21117
21118 /**
21119  * @class Roo.bootstrap.SecurePass
21120  * @extends Roo.bootstrap.Input
21121  * Bootstrap SecurePass class
21122  *
21123  * 
21124  * @constructor
21125  * Create a new SecurePass
21126  * @param {Object} config The config object
21127  */
21128  
21129 Roo.bootstrap.SecurePass = function (config) {
21130     // these go here, so the translation tool can replace them..
21131     this.errors = {
21132         PwdEmpty: "Please type a password, and then retype it to confirm.",
21133         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21134         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21135         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21136         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21137         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21138         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21139         TooWeak: "Your password is Too Weak."
21140     },
21141     this.meterLabel = "Password strength:";
21142     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21143     this.meterClass = [
21144         "roo-password-meter-tooweak", 
21145         "roo-password-meter-weak", 
21146         "roo-password-meter-medium", 
21147         "roo-password-meter-strong", 
21148         "roo-password-meter-grey"
21149     ];
21150     
21151     this.errors = {};
21152     
21153     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21154 }
21155
21156 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21157     /**
21158      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21159      * {
21160      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21161      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21162      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21163      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21164      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21165      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21166      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21167      * })
21168      */
21169     // private
21170     
21171     meterWidth: 300,
21172     errorMsg :'',    
21173     errors: false,
21174     imageRoot: '/',
21175     /**
21176      * @cfg {String/Object} Label for the strength meter (defaults to
21177      * 'Password strength:')
21178      */
21179     // private
21180     meterLabel: '',
21181     /**
21182      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21183      * ['Weak', 'Medium', 'Strong'])
21184      */
21185     // private    
21186     pwdStrengths: false,    
21187     // private
21188     strength: 0,
21189     // private
21190     _lastPwd: null,
21191     // private
21192     kCapitalLetter: 0,
21193     kSmallLetter: 1,
21194     kDigit: 2,
21195     kPunctuation: 3,
21196     
21197     insecure: false,
21198     // private
21199     initEvents: function ()
21200     {
21201         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21202
21203         if (this.el.is('input[type=password]') && Roo.isSafari) {
21204             this.el.on('keydown', this.SafariOnKeyDown, this);
21205         }
21206
21207         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21208     },
21209     // private
21210     onRender: function (ct, position)
21211     {
21212         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21213         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21214         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21215
21216         this.trigger.createChild({
21217                    cn: [
21218                     {
21219                     //id: 'PwdMeter',
21220                     tag: 'div',
21221                     cls: 'roo-password-meter-grey col-xs-12',
21222                     style: {
21223                         //width: 0,
21224                         //width: this.meterWidth + 'px'                                                
21225                         }
21226                     },
21227                     {                            
21228                          cls: 'roo-password-meter-text'                          
21229                     }
21230                 ]            
21231         });
21232
21233          
21234         if (this.hideTrigger) {
21235             this.trigger.setDisplayed(false);
21236         }
21237         this.setSize(this.width || '', this.height || '');
21238     },
21239     // private
21240     onDestroy: function ()
21241     {
21242         if (this.trigger) {
21243             this.trigger.removeAllListeners();
21244             this.trigger.remove();
21245         }
21246         if (this.wrap) {
21247             this.wrap.remove();
21248         }
21249         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21250     },
21251     // private
21252     checkStrength: function ()
21253     {
21254         var pwd = this.inputEl().getValue();
21255         if (pwd == this._lastPwd) {
21256             return;
21257         }
21258
21259         var strength;
21260         if (this.ClientSideStrongPassword(pwd)) {
21261             strength = 3;
21262         } else if (this.ClientSideMediumPassword(pwd)) {
21263             strength = 2;
21264         } else if (this.ClientSideWeakPassword(pwd)) {
21265             strength = 1;
21266         } else {
21267             strength = 0;
21268         }
21269         
21270         Roo.log('strength1: ' + strength);
21271         
21272         //var pm = this.trigger.child('div/div/div').dom;
21273         var pm = this.trigger.child('div/div');
21274         pm.removeClass(this.meterClass);
21275         pm.addClass(this.meterClass[strength]);
21276                 
21277         
21278         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21279                 
21280         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21281         
21282         this._lastPwd = pwd;
21283     },
21284     reset: function ()
21285     {
21286         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21287         
21288         this._lastPwd = '';
21289         
21290         var pm = this.trigger.child('div/div');
21291         pm.removeClass(this.meterClass);
21292         pm.addClass('roo-password-meter-grey');        
21293         
21294         
21295         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21296         
21297         pt.innerHTML = '';
21298         this.inputEl().dom.type='password';
21299     },
21300     // private
21301     validateValue: function (value)
21302     {
21303         
21304         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21305             return false;
21306         }
21307         if (value.length == 0) {
21308             if (this.allowBlank) {
21309                 this.clearInvalid();
21310                 return true;
21311             }
21312
21313             this.markInvalid(this.errors.PwdEmpty);
21314             this.errorMsg = this.errors.PwdEmpty;
21315             return false;
21316         }
21317         
21318         if(this.insecure){
21319             return true;
21320         }
21321         
21322         if ('[\x21-\x7e]*'.match(value)) {
21323             this.markInvalid(this.errors.PwdBadChar);
21324             this.errorMsg = this.errors.PwdBadChar;
21325             return false;
21326         }
21327         if (value.length < 6) {
21328             this.markInvalid(this.errors.PwdShort);
21329             this.errorMsg = this.errors.PwdShort;
21330             return false;
21331         }
21332         if (value.length > 16) {
21333             this.markInvalid(this.errors.PwdLong);
21334             this.errorMsg = this.errors.PwdLong;
21335             return false;
21336         }
21337         var strength;
21338         if (this.ClientSideStrongPassword(value)) {
21339             strength = 3;
21340         } else if (this.ClientSideMediumPassword(value)) {
21341             strength = 2;
21342         } else if (this.ClientSideWeakPassword(value)) {
21343             strength = 1;
21344         } else {
21345             strength = 0;
21346         }
21347
21348         
21349         if (strength < 2) {
21350             //this.markInvalid(this.errors.TooWeak);
21351             this.errorMsg = this.errors.TooWeak;
21352             //return false;
21353         }
21354         
21355         
21356         console.log('strength2: ' + strength);
21357         
21358         //var pm = this.trigger.child('div/div/div').dom;
21359         
21360         var pm = this.trigger.child('div/div');
21361         pm.removeClass(this.meterClass);
21362         pm.addClass(this.meterClass[strength]);
21363                 
21364         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21365                 
21366         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21367         
21368         this.errorMsg = ''; 
21369         return true;
21370     },
21371     // private
21372     CharacterSetChecks: function (type)
21373     {
21374         this.type = type;
21375         this.fResult = false;
21376     },
21377     // private
21378     isctype: function (character, type)
21379     {
21380         switch (type) {  
21381             case this.kCapitalLetter:
21382                 if (character >= 'A' && character <= 'Z') {
21383                     return true;
21384                 }
21385                 break;
21386             
21387             case this.kSmallLetter:
21388                 if (character >= 'a' && character <= 'z') {
21389                     return true;
21390                 }
21391                 break;
21392             
21393             case this.kDigit:
21394                 if (character >= '0' && character <= '9') {
21395                     return true;
21396                 }
21397                 break;
21398             
21399             case this.kPunctuation:
21400                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21401                     return true;
21402                 }
21403                 break;
21404             
21405             default:
21406                 return false;
21407         }
21408
21409     },
21410     // private
21411     IsLongEnough: function (pwd, size)
21412     {
21413         return !(pwd == null || isNaN(size) || pwd.length < size);
21414     },
21415     // private
21416     SpansEnoughCharacterSets: function (word, nb)
21417     {
21418         if (!this.IsLongEnough(word, nb))
21419         {
21420             return false;
21421         }
21422
21423         var characterSetChecks = new Array(
21424             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21425             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21426         );
21427         
21428         for (var index = 0; index < word.length; ++index) {
21429             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21430                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21431                     characterSetChecks[nCharSet].fResult = true;
21432                     break;
21433                 }
21434             }
21435         }
21436
21437         var nCharSets = 0;
21438         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21439             if (characterSetChecks[nCharSet].fResult) {
21440                 ++nCharSets;
21441             }
21442         }
21443
21444         if (nCharSets < nb) {
21445             return false;
21446         }
21447         return true;
21448     },
21449     // private
21450     ClientSideStrongPassword: function (pwd)
21451     {
21452         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21453     },
21454     // private
21455     ClientSideMediumPassword: function (pwd)
21456     {
21457         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21458     },
21459     // private
21460     ClientSideWeakPassword: function (pwd)
21461     {
21462         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21463     }
21464           
21465 })//<script type="text/javascript">
21466
21467 /*
21468  * Based  Ext JS Library 1.1.1
21469  * Copyright(c) 2006-2007, Ext JS, LLC.
21470  * LGPL
21471  *
21472  */
21473  
21474 /**
21475  * @class Roo.HtmlEditorCore
21476  * @extends Roo.Component
21477  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21478  *
21479  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21480  */
21481
21482 Roo.HtmlEditorCore = function(config){
21483     
21484     
21485     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21486     
21487     
21488     this.addEvents({
21489         /**
21490          * @event initialize
21491          * Fires when the editor is fully initialized (including the iframe)
21492          * @param {Roo.HtmlEditorCore} this
21493          */
21494         initialize: true,
21495         /**
21496          * @event activate
21497          * Fires when the editor is first receives the focus. Any insertion must wait
21498          * until after this event.
21499          * @param {Roo.HtmlEditorCore} this
21500          */
21501         activate: true,
21502          /**
21503          * @event beforesync
21504          * Fires before the textarea is updated with content from the editor iframe. Return false
21505          * to cancel the sync.
21506          * @param {Roo.HtmlEditorCore} this
21507          * @param {String} html
21508          */
21509         beforesync: true,
21510          /**
21511          * @event beforepush
21512          * Fires before the iframe editor is updated with content from the textarea. Return false
21513          * to cancel the push.
21514          * @param {Roo.HtmlEditorCore} this
21515          * @param {String} html
21516          */
21517         beforepush: true,
21518          /**
21519          * @event sync
21520          * Fires when the textarea is updated with content from the editor iframe.
21521          * @param {Roo.HtmlEditorCore} this
21522          * @param {String} html
21523          */
21524         sync: true,
21525          /**
21526          * @event push
21527          * Fires when the iframe editor is updated with content from the textarea.
21528          * @param {Roo.HtmlEditorCore} this
21529          * @param {String} html
21530          */
21531         push: true,
21532         
21533         /**
21534          * @event editorevent
21535          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21536          * @param {Roo.HtmlEditorCore} this
21537          */
21538         editorevent: true
21539         
21540     });
21541     
21542     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21543     
21544     // defaults : white / black...
21545     this.applyBlacklists();
21546     
21547     
21548     
21549 };
21550
21551
21552 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21553
21554
21555      /**
21556      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21557      */
21558     
21559     owner : false,
21560     
21561      /**
21562      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21563      *                        Roo.resizable.
21564      */
21565     resizable : false,
21566      /**
21567      * @cfg {Number} height (in pixels)
21568      */   
21569     height: 300,
21570    /**
21571      * @cfg {Number} width (in pixels)
21572      */   
21573     width: 500,
21574     
21575     /**
21576      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21577      * 
21578      */
21579     stylesheets: false,
21580     
21581     // id of frame..
21582     frameId: false,
21583     
21584     // private properties
21585     validationEvent : false,
21586     deferHeight: true,
21587     initialized : false,
21588     activated : false,
21589     sourceEditMode : false,
21590     onFocus : Roo.emptyFn,
21591     iframePad:3,
21592     hideMode:'offsets',
21593     
21594     clearUp: true,
21595     
21596     // blacklist + whitelisted elements..
21597     black: false,
21598     white: false,
21599      
21600     bodyCls : '',
21601
21602     /**
21603      * Protected method that will not generally be called directly. It
21604      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21605      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21606      */
21607     getDocMarkup : function(){
21608         // body styles..
21609         var st = '';
21610         
21611         // inherit styels from page...?? 
21612         if (this.stylesheets === false) {
21613             
21614             Roo.get(document.head).select('style').each(function(node) {
21615                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21616             });
21617             
21618             Roo.get(document.head).select('link').each(function(node) { 
21619                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21620             });
21621             
21622         } else if (!this.stylesheets.length) {
21623                 // simple..
21624                 st = '<style type="text/css">' +
21625                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21626                    '</style>';
21627         } else { 
21628             st = '<style type="text/css">' +
21629                     this.stylesheets +
21630                 '</style>';
21631         }
21632         
21633         st +=  '<style type="text/css">' +
21634             'IMG { cursor: pointer } ' +
21635         '</style>';
21636
21637         var cls = 'roo-htmleditor-body';
21638         
21639         if(this.bodyCls.length){
21640             cls += ' ' + this.bodyCls;
21641         }
21642         
21643         return '<html><head>' + st  +
21644             //<style type="text/css">' +
21645             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21646             //'</style>' +
21647             ' </head><body class="' +  cls + '"></body></html>';
21648     },
21649
21650     // private
21651     onRender : function(ct, position)
21652     {
21653         var _t = this;
21654         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21655         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21656         
21657         
21658         this.el.dom.style.border = '0 none';
21659         this.el.dom.setAttribute('tabIndex', -1);
21660         this.el.addClass('x-hidden hide');
21661         
21662         
21663         
21664         if(Roo.isIE){ // fix IE 1px bogus margin
21665             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21666         }
21667        
21668         
21669         this.frameId = Roo.id();
21670         
21671          
21672         
21673         var iframe = this.owner.wrap.createChild({
21674             tag: 'iframe',
21675             cls: 'form-control', // bootstrap..
21676             id: this.frameId,
21677             name: this.frameId,
21678             frameBorder : 'no',
21679             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21680         }, this.el
21681         );
21682         
21683         
21684         this.iframe = iframe.dom;
21685
21686          this.assignDocWin();
21687         
21688         this.doc.designMode = 'on';
21689        
21690         this.doc.open();
21691         this.doc.write(this.getDocMarkup());
21692         this.doc.close();
21693
21694         
21695         var task = { // must defer to wait for browser to be ready
21696             run : function(){
21697                 //console.log("run task?" + this.doc.readyState);
21698                 this.assignDocWin();
21699                 if(this.doc.body || this.doc.readyState == 'complete'){
21700                     try {
21701                         this.doc.designMode="on";
21702                     } catch (e) {
21703                         return;
21704                     }
21705                     Roo.TaskMgr.stop(task);
21706                     this.initEditor.defer(10, this);
21707                 }
21708             },
21709             interval : 10,
21710             duration: 10000,
21711             scope: this
21712         };
21713         Roo.TaskMgr.start(task);
21714
21715     },
21716
21717     // private
21718     onResize : function(w, h)
21719     {
21720          Roo.log('resize: ' +w + ',' + h );
21721         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21722         if(!this.iframe){
21723             return;
21724         }
21725         if(typeof w == 'number'){
21726             
21727             this.iframe.style.width = w + 'px';
21728         }
21729         if(typeof h == 'number'){
21730             
21731             this.iframe.style.height = h + 'px';
21732             if(this.doc){
21733                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21734             }
21735         }
21736         
21737     },
21738
21739     /**
21740      * Toggles the editor between standard and source edit mode.
21741      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21742      */
21743     toggleSourceEdit : function(sourceEditMode){
21744         
21745         this.sourceEditMode = sourceEditMode === true;
21746         
21747         if(this.sourceEditMode){
21748  
21749             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21750             
21751         }else{
21752             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21753             //this.iframe.className = '';
21754             this.deferFocus();
21755         }
21756         //this.setSize(this.owner.wrap.getSize());
21757         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21758     },
21759
21760     
21761   
21762
21763     /**
21764      * Protected method that will not generally be called directly. If you need/want
21765      * custom HTML cleanup, this is the method you should override.
21766      * @param {String} html The HTML to be cleaned
21767      * return {String} The cleaned HTML
21768      */
21769     cleanHtml : function(html){
21770         html = String(html);
21771         if(html.length > 5){
21772             if(Roo.isSafari){ // strip safari nonsense
21773                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21774             }
21775         }
21776         if(html == '&nbsp;'){
21777             html = '';
21778         }
21779         return html;
21780     },
21781
21782     /**
21783      * HTML Editor -> Textarea
21784      * Protected method that will not generally be called directly. Syncs the contents
21785      * of the editor iframe with the textarea.
21786      */
21787     syncValue : function(){
21788         if(this.initialized){
21789             var bd = (this.doc.body || this.doc.documentElement);
21790             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21791             var html = bd.innerHTML;
21792             if(Roo.isSafari){
21793                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21794                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21795                 if(m && m[1]){
21796                     html = '<div style="'+m[0]+'">' + html + '</div>';
21797                 }
21798             }
21799             html = this.cleanHtml(html);
21800             // fix up the special chars.. normaly like back quotes in word...
21801             // however we do not want to do this with chinese..
21802             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21803                 var cc = b.charCodeAt();
21804                 if (
21805                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21806                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21807                     (cc >= 0xf900 && cc < 0xfb00 )
21808                 ) {
21809                         return b;
21810                 }
21811                 return "&#"+cc+";" 
21812             });
21813             if(this.owner.fireEvent('beforesync', this, html) !== false){
21814                 this.el.dom.value = html;
21815                 this.owner.fireEvent('sync', this, html);
21816             }
21817         }
21818     },
21819
21820     /**
21821      * Protected method that will not generally be called directly. Pushes the value of the textarea
21822      * into the iframe editor.
21823      */
21824     pushValue : function(){
21825         if(this.initialized){
21826             var v = this.el.dom.value.trim();
21827             
21828 //            if(v.length < 1){
21829 //                v = '&#160;';
21830 //            }
21831             
21832             if(this.owner.fireEvent('beforepush', this, v) !== false){
21833                 var d = (this.doc.body || this.doc.documentElement);
21834                 d.innerHTML = v;
21835                 this.cleanUpPaste();
21836                 this.el.dom.value = d.innerHTML;
21837                 this.owner.fireEvent('push', this, v);
21838             }
21839         }
21840     },
21841
21842     // private
21843     deferFocus : function(){
21844         this.focus.defer(10, this);
21845     },
21846
21847     // doc'ed in Field
21848     focus : function(){
21849         if(this.win && !this.sourceEditMode){
21850             this.win.focus();
21851         }else{
21852             this.el.focus();
21853         }
21854     },
21855     
21856     assignDocWin: function()
21857     {
21858         var iframe = this.iframe;
21859         
21860          if(Roo.isIE){
21861             this.doc = iframe.contentWindow.document;
21862             this.win = iframe.contentWindow;
21863         } else {
21864 //            if (!Roo.get(this.frameId)) {
21865 //                return;
21866 //            }
21867 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21868 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21869             
21870             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21871                 return;
21872             }
21873             
21874             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21875             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21876         }
21877     },
21878     
21879     // private
21880     initEditor : function(){
21881         //console.log("INIT EDITOR");
21882         this.assignDocWin();
21883         
21884         
21885         
21886         this.doc.designMode="on";
21887         this.doc.open();
21888         this.doc.write(this.getDocMarkup());
21889         this.doc.close();
21890         
21891         var dbody = (this.doc.body || this.doc.documentElement);
21892         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21893         // this copies styles from the containing element into thsi one..
21894         // not sure why we need all of this..
21895         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21896         
21897         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21898         //ss['background-attachment'] = 'fixed'; // w3c
21899         dbody.bgProperties = 'fixed'; // ie
21900         //Roo.DomHelper.applyStyles(dbody, ss);
21901         Roo.EventManager.on(this.doc, {
21902             //'mousedown': this.onEditorEvent,
21903             'mouseup': this.onEditorEvent,
21904             'dblclick': this.onEditorEvent,
21905             'click': this.onEditorEvent,
21906             'keyup': this.onEditorEvent,
21907             buffer:100,
21908             scope: this
21909         });
21910         if(Roo.isGecko){
21911             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21912         }
21913         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21914             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21915         }
21916         this.initialized = true;
21917
21918         this.owner.fireEvent('initialize', this);
21919         this.pushValue();
21920     },
21921
21922     // private
21923     onDestroy : function(){
21924         
21925         
21926         
21927         if(this.rendered){
21928             
21929             //for (var i =0; i < this.toolbars.length;i++) {
21930             //    // fixme - ask toolbars for heights?
21931             //    this.toolbars[i].onDestroy();
21932            // }
21933             
21934             //this.wrap.dom.innerHTML = '';
21935             //this.wrap.remove();
21936         }
21937     },
21938
21939     // private
21940     onFirstFocus : function(){
21941         
21942         this.assignDocWin();
21943         
21944         
21945         this.activated = true;
21946          
21947     
21948         if(Roo.isGecko){ // prevent silly gecko errors
21949             this.win.focus();
21950             var s = this.win.getSelection();
21951             if(!s.focusNode || s.focusNode.nodeType != 3){
21952                 var r = s.getRangeAt(0);
21953                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21954                 r.collapse(true);
21955                 this.deferFocus();
21956             }
21957             try{
21958                 this.execCmd('useCSS', true);
21959                 this.execCmd('styleWithCSS', false);
21960             }catch(e){}
21961         }
21962         this.owner.fireEvent('activate', this);
21963     },
21964
21965     // private
21966     adjustFont: function(btn){
21967         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21968         //if(Roo.isSafari){ // safari
21969         //    adjust *= 2;
21970        // }
21971         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21972         if(Roo.isSafari){ // safari
21973             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21974             v =  (v < 10) ? 10 : v;
21975             v =  (v > 48) ? 48 : v;
21976             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21977             
21978         }
21979         
21980         
21981         v = Math.max(1, v+adjust);
21982         
21983         this.execCmd('FontSize', v  );
21984     },
21985
21986     onEditorEvent : function(e)
21987     {
21988         this.owner.fireEvent('editorevent', this, e);
21989       //  this.updateToolbar();
21990         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21991     },
21992
21993     insertTag : function(tg)
21994     {
21995         // could be a bit smarter... -> wrap the current selected tRoo..
21996         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21997             
21998             range = this.createRange(this.getSelection());
21999             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22000             wrappingNode.appendChild(range.extractContents());
22001             range.insertNode(wrappingNode);
22002
22003             return;
22004             
22005             
22006             
22007         }
22008         this.execCmd("formatblock",   tg);
22009         
22010     },
22011     
22012     insertText : function(txt)
22013     {
22014         
22015         
22016         var range = this.createRange();
22017         range.deleteContents();
22018                //alert(Sender.getAttribute('label'));
22019                
22020         range.insertNode(this.doc.createTextNode(txt));
22021     } ,
22022     
22023      
22024
22025     /**
22026      * Executes a Midas editor command on the editor document and performs necessary focus and
22027      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22028      * @param {String} cmd The Midas command
22029      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22030      */
22031     relayCmd : function(cmd, value){
22032         this.win.focus();
22033         this.execCmd(cmd, value);
22034         this.owner.fireEvent('editorevent', this);
22035         //this.updateToolbar();
22036         this.owner.deferFocus();
22037     },
22038
22039     /**
22040      * Executes a Midas editor command directly on the editor document.
22041      * For visual commands, you should use {@link #relayCmd} instead.
22042      * <b>This should only be called after the editor is initialized.</b>
22043      * @param {String} cmd The Midas command
22044      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22045      */
22046     execCmd : function(cmd, value){
22047         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22048         this.syncValue();
22049     },
22050  
22051  
22052    
22053     /**
22054      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22055      * to insert tRoo.
22056      * @param {String} text | dom node.. 
22057      */
22058     insertAtCursor : function(text)
22059     {
22060         
22061         if(!this.activated){
22062             return;
22063         }
22064         /*
22065         if(Roo.isIE){
22066             this.win.focus();
22067             var r = this.doc.selection.createRange();
22068             if(r){
22069                 r.collapse(true);
22070                 r.pasteHTML(text);
22071                 this.syncValue();
22072                 this.deferFocus();
22073             
22074             }
22075             return;
22076         }
22077         */
22078         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22079             this.win.focus();
22080             
22081             
22082             // from jquery ui (MIT licenced)
22083             var range, node;
22084             var win = this.win;
22085             
22086             if (win.getSelection && win.getSelection().getRangeAt) {
22087                 range = win.getSelection().getRangeAt(0);
22088                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22089                 range.insertNode(node);
22090             } else if (win.document.selection && win.document.selection.createRange) {
22091                 // no firefox support
22092                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22093                 win.document.selection.createRange().pasteHTML(txt);
22094             } else {
22095                 // no firefox support
22096                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22097                 this.execCmd('InsertHTML', txt);
22098             } 
22099             
22100             this.syncValue();
22101             
22102             this.deferFocus();
22103         }
22104     },
22105  // private
22106     mozKeyPress : function(e){
22107         if(e.ctrlKey){
22108             var c = e.getCharCode(), cmd;
22109           
22110             if(c > 0){
22111                 c = String.fromCharCode(c).toLowerCase();
22112                 switch(c){
22113                     case 'b':
22114                         cmd = 'bold';
22115                         break;
22116                     case 'i':
22117                         cmd = 'italic';
22118                         break;
22119                     
22120                     case 'u':
22121                         cmd = 'underline';
22122                         break;
22123                     
22124                     case 'v':
22125                         this.cleanUpPaste.defer(100, this);
22126                         return;
22127                         
22128                 }
22129                 if(cmd){
22130                     this.win.focus();
22131                     this.execCmd(cmd);
22132                     this.deferFocus();
22133                     e.preventDefault();
22134                 }
22135                 
22136             }
22137         }
22138     },
22139
22140     // private
22141     fixKeys : function(){ // load time branching for fastest keydown performance
22142         if(Roo.isIE){
22143             return function(e){
22144                 var k = e.getKey(), r;
22145                 if(k == e.TAB){
22146                     e.stopEvent();
22147                     r = this.doc.selection.createRange();
22148                     if(r){
22149                         r.collapse(true);
22150                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22151                         this.deferFocus();
22152                     }
22153                     return;
22154                 }
22155                 
22156                 if(k == e.ENTER){
22157                     r = this.doc.selection.createRange();
22158                     if(r){
22159                         var target = r.parentElement();
22160                         if(!target || target.tagName.toLowerCase() != 'li'){
22161                             e.stopEvent();
22162                             r.pasteHTML('<br />');
22163                             r.collapse(false);
22164                             r.select();
22165                         }
22166                     }
22167                 }
22168                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22169                     this.cleanUpPaste.defer(100, this);
22170                     return;
22171                 }
22172                 
22173                 
22174             };
22175         }else if(Roo.isOpera){
22176             return function(e){
22177                 var k = e.getKey();
22178                 if(k == e.TAB){
22179                     e.stopEvent();
22180                     this.win.focus();
22181                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22182                     this.deferFocus();
22183                 }
22184                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22185                     this.cleanUpPaste.defer(100, this);
22186                     return;
22187                 }
22188                 
22189             };
22190         }else if(Roo.isSafari){
22191             return function(e){
22192                 var k = e.getKey();
22193                 
22194                 if(k == e.TAB){
22195                     e.stopEvent();
22196                     this.execCmd('InsertText','\t');
22197                     this.deferFocus();
22198                     return;
22199                 }
22200                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22201                     this.cleanUpPaste.defer(100, this);
22202                     return;
22203                 }
22204                 
22205              };
22206         }
22207     }(),
22208     
22209     getAllAncestors: function()
22210     {
22211         var p = this.getSelectedNode();
22212         var a = [];
22213         if (!p) {
22214             a.push(p); // push blank onto stack..
22215             p = this.getParentElement();
22216         }
22217         
22218         
22219         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22220             a.push(p);
22221             p = p.parentNode;
22222         }
22223         a.push(this.doc.body);
22224         return a;
22225     },
22226     lastSel : false,
22227     lastSelNode : false,
22228     
22229     
22230     getSelection : function() 
22231     {
22232         this.assignDocWin();
22233         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22234     },
22235     
22236     getSelectedNode: function() 
22237     {
22238         // this may only work on Gecko!!!
22239         
22240         // should we cache this!!!!
22241         
22242         
22243         
22244          
22245         var range = this.createRange(this.getSelection()).cloneRange();
22246         
22247         if (Roo.isIE) {
22248             var parent = range.parentElement();
22249             while (true) {
22250                 var testRange = range.duplicate();
22251                 testRange.moveToElementText(parent);
22252                 if (testRange.inRange(range)) {
22253                     break;
22254                 }
22255                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22256                     break;
22257                 }
22258                 parent = parent.parentElement;
22259             }
22260             return parent;
22261         }
22262         
22263         // is ancestor a text element.
22264         var ac =  range.commonAncestorContainer;
22265         if (ac.nodeType == 3) {
22266             ac = ac.parentNode;
22267         }
22268         
22269         var ar = ac.childNodes;
22270          
22271         var nodes = [];
22272         var other_nodes = [];
22273         var has_other_nodes = false;
22274         for (var i=0;i<ar.length;i++) {
22275             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22276                 continue;
22277             }
22278             // fullly contained node.
22279             
22280             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22281                 nodes.push(ar[i]);
22282                 continue;
22283             }
22284             
22285             // probably selected..
22286             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22287                 other_nodes.push(ar[i]);
22288                 continue;
22289             }
22290             // outer..
22291             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22292                 continue;
22293             }
22294             
22295             
22296             has_other_nodes = true;
22297         }
22298         if (!nodes.length && other_nodes.length) {
22299             nodes= other_nodes;
22300         }
22301         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22302             return false;
22303         }
22304         
22305         return nodes[0];
22306     },
22307     createRange: function(sel)
22308     {
22309         // this has strange effects when using with 
22310         // top toolbar - not sure if it's a great idea.
22311         //this.editor.contentWindow.focus();
22312         if (typeof sel != "undefined") {
22313             try {
22314                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22315             } catch(e) {
22316                 return this.doc.createRange();
22317             }
22318         } else {
22319             return this.doc.createRange();
22320         }
22321     },
22322     getParentElement: function()
22323     {
22324         
22325         this.assignDocWin();
22326         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22327         
22328         var range = this.createRange(sel);
22329          
22330         try {
22331             var p = range.commonAncestorContainer;
22332             while (p.nodeType == 3) { // text node
22333                 p = p.parentNode;
22334             }
22335             return p;
22336         } catch (e) {
22337             return null;
22338         }
22339     
22340     },
22341     /***
22342      *
22343      * Range intersection.. the hard stuff...
22344      *  '-1' = before
22345      *  '0' = hits..
22346      *  '1' = after.
22347      *         [ -- selected range --- ]
22348      *   [fail]                        [fail]
22349      *
22350      *    basically..
22351      *      if end is before start or  hits it. fail.
22352      *      if start is after end or hits it fail.
22353      *
22354      *   if either hits (but other is outside. - then it's not 
22355      *   
22356      *    
22357      **/
22358     
22359     
22360     // @see http://www.thismuchiknow.co.uk/?p=64.
22361     rangeIntersectsNode : function(range, node)
22362     {
22363         var nodeRange = node.ownerDocument.createRange();
22364         try {
22365             nodeRange.selectNode(node);
22366         } catch (e) {
22367             nodeRange.selectNodeContents(node);
22368         }
22369     
22370         var rangeStartRange = range.cloneRange();
22371         rangeStartRange.collapse(true);
22372     
22373         var rangeEndRange = range.cloneRange();
22374         rangeEndRange.collapse(false);
22375     
22376         var nodeStartRange = nodeRange.cloneRange();
22377         nodeStartRange.collapse(true);
22378     
22379         var nodeEndRange = nodeRange.cloneRange();
22380         nodeEndRange.collapse(false);
22381     
22382         return rangeStartRange.compareBoundaryPoints(
22383                  Range.START_TO_START, nodeEndRange) == -1 &&
22384                rangeEndRange.compareBoundaryPoints(
22385                  Range.START_TO_START, nodeStartRange) == 1;
22386         
22387          
22388     },
22389     rangeCompareNode : function(range, node)
22390     {
22391         var nodeRange = node.ownerDocument.createRange();
22392         try {
22393             nodeRange.selectNode(node);
22394         } catch (e) {
22395             nodeRange.selectNodeContents(node);
22396         }
22397         
22398         
22399         range.collapse(true);
22400     
22401         nodeRange.collapse(true);
22402      
22403         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22404         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22405          
22406         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22407         
22408         var nodeIsBefore   =  ss == 1;
22409         var nodeIsAfter    = ee == -1;
22410         
22411         if (nodeIsBefore && nodeIsAfter) {
22412             return 0; // outer
22413         }
22414         if (!nodeIsBefore && nodeIsAfter) {
22415             return 1; //right trailed.
22416         }
22417         
22418         if (nodeIsBefore && !nodeIsAfter) {
22419             return 2;  // left trailed.
22420         }
22421         // fully contined.
22422         return 3;
22423     },
22424
22425     // private? - in a new class?
22426     cleanUpPaste :  function()
22427     {
22428         // cleans up the whole document..
22429         Roo.log('cleanuppaste');
22430         
22431         this.cleanUpChildren(this.doc.body);
22432         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22433         if (clean != this.doc.body.innerHTML) {
22434             this.doc.body.innerHTML = clean;
22435         }
22436         
22437     },
22438     
22439     cleanWordChars : function(input) {// change the chars to hex code
22440         var he = Roo.HtmlEditorCore;
22441         
22442         var output = input;
22443         Roo.each(he.swapCodes, function(sw) { 
22444             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22445             
22446             output = output.replace(swapper, sw[1]);
22447         });
22448         
22449         return output;
22450     },
22451     
22452     
22453     cleanUpChildren : function (n)
22454     {
22455         if (!n.childNodes.length) {
22456             return;
22457         }
22458         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22459            this.cleanUpChild(n.childNodes[i]);
22460         }
22461     },
22462     
22463     
22464         
22465     
22466     cleanUpChild : function (node)
22467     {
22468         var ed = this;
22469         //console.log(node);
22470         if (node.nodeName == "#text") {
22471             // clean up silly Windows -- stuff?
22472             return; 
22473         }
22474         if (node.nodeName == "#comment") {
22475             node.parentNode.removeChild(node);
22476             // clean up silly Windows -- stuff?
22477             return; 
22478         }
22479         var lcname = node.tagName.toLowerCase();
22480         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22481         // whitelist of tags..
22482         
22483         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22484             // remove node.
22485             node.parentNode.removeChild(node);
22486             return;
22487             
22488         }
22489         
22490         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22491         
22492         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22493         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22494         
22495         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22496         //    remove_keep_children = true;
22497         //}
22498         
22499         if (remove_keep_children) {
22500             this.cleanUpChildren(node);
22501             // inserts everything just before this node...
22502             while (node.childNodes.length) {
22503                 var cn = node.childNodes[0];
22504                 node.removeChild(cn);
22505                 node.parentNode.insertBefore(cn, node);
22506             }
22507             node.parentNode.removeChild(node);
22508             return;
22509         }
22510         
22511         if (!node.attributes || !node.attributes.length) {
22512             this.cleanUpChildren(node);
22513             return;
22514         }
22515         
22516         function cleanAttr(n,v)
22517         {
22518             
22519             if (v.match(/^\./) || v.match(/^\//)) {
22520                 return;
22521             }
22522             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22523                 return;
22524             }
22525             if (v.match(/^#/)) {
22526                 return;
22527             }
22528 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22529             node.removeAttribute(n);
22530             
22531         }
22532         
22533         var cwhite = this.cwhite;
22534         var cblack = this.cblack;
22535             
22536         function cleanStyle(n,v)
22537         {
22538             if (v.match(/expression/)) { //XSS?? should we even bother..
22539                 node.removeAttribute(n);
22540                 return;
22541             }
22542             
22543             var parts = v.split(/;/);
22544             var clean = [];
22545             
22546             Roo.each(parts, function(p) {
22547                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22548                 if (!p.length) {
22549                     return true;
22550                 }
22551                 var l = p.split(':').shift().replace(/\s+/g,'');
22552                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22553                 
22554                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22555 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22556                     //node.removeAttribute(n);
22557                     return true;
22558                 }
22559                 //Roo.log()
22560                 // only allow 'c whitelisted system attributes'
22561                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22562 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22563                     //node.removeAttribute(n);
22564                     return true;
22565                 }
22566                 
22567                 
22568                  
22569                 
22570                 clean.push(p);
22571                 return true;
22572             });
22573             if (clean.length) { 
22574                 node.setAttribute(n, clean.join(';'));
22575             } else {
22576                 node.removeAttribute(n);
22577             }
22578             
22579         }
22580         
22581         
22582         for (var i = node.attributes.length-1; i > -1 ; i--) {
22583             var a = node.attributes[i];
22584             //console.log(a);
22585             
22586             if (a.name.toLowerCase().substr(0,2)=='on')  {
22587                 node.removeAttribute(a.name);
22588                 continue;
22589             }
22590             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22591                 node.removeAttribute(a.name);
22592                 continue;
22593             }
22594             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22595                 cleanAttr(a.name,a.value); // fixme..
22596                 continue;
22597             }
22598             if (a.name == 'style') {
22599                 cleanStyle(a.name,a.value);
22600                 continue;
22601             }
22602             /// clean up MS crap..
22603             // tecnically this should be a list of valid class'es..
22604             
22605             
22606             if (a.name == 'class') {
22607                 if (a.value.match(/^Mso/)) {
22608                     node.className = '';
22609                 }
22610                 
22611                 if (a.value.match(/^body$/)) {
22612                     node.className = '';
22613                 }
22614                 continue;
22615             }
22616             
22617             // style cleanup!?
22618             // class cleanup?
22619             
22620         }
22621         
22622         
22623         this.cleanUpChildren(node);
22624         
22625         
22626     },
22627     
22628     /**
22629      * Clean up MS wordisms...
22630      */
22631     cleanWord : function(node)
22632     {
22633         
22634         
22635         if (!node) {
22636             this.cleanWord(this.doc.body);
22637             return;
22638         }
22639         if (node.nodeName == "#text") {
22640             // clean up silly Windows -- stuff?
22641             return; 
22642         }
22643         if (node.nodeName == "#comment") {
22644             node.parentNode.removeChild(node);
22645             // clean up silly Windows -- stuff?
22646             return; 
22647         }
22648         
22649         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22650             node.parentNode.removeChild(node);
22651             return;
22652         }
22653         
22654         // remove - but keep children..
22655         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22656             while (node.childNodes.length) {
22657                 var cn = node.childNodes[0];
22658                 node.removeChild(cn);
22659                 node.parentNode.insertBefore(cn, node);
22660             }
22661             node.parentNode.removeChild(node);
22662             this.iterateChildren(node, this.cleanWord);
22663             return;
22664         }
22665         // clean styles
22666         if (node.className.length) {
22667             
22668             var cn = node.className.split(/\W+/);
22669             var cna = [];
22670             Roo.each(cn, function(cls) {
22671                 if (cls.match(/Mso[a-zA-Z]+/)) {
22672                     return;
22673                 }
22674                 cna.push(cls);
22675             });
22676             node.className = cna.length ? cna.join(' ') : '';
22677             if (!cna.length) {
22678                 node.removeAttribute("class");
22679             }
22680         }
22681         
22682         if (node.hasAttribute("lang")) {
22683             node.removeAttribute("lang");
22684         }
22685         
22686         if (node.hasAttribute("style")) {
22687             
22688             var styles = node.getAttribute("style").split(";");
22689             var nstyle = [];
22690             Roo.each(styles, function(s) {
22691                 if (!s.match(/:/)) {
22692                     return;
22693                 }
22694                 var kv = s.split(":");
22695                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22696                     return;
22697                 }
22698                 // what ever is left... we allow.
22699                 nstyle.push(s);
22700             });
22701             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22702             if (!nstyle.length) {
22703                 node.removeAttribute('style');
22704             }
22705         }
22706         this.iterateChildren(node, this.cleanWord);
22707         
22708         
22709         
22710     },
22711     /**
22712      * iterateChildren of a Node, calling fn each time, using this as the scole..
22713      * @param {DomNode} node node to iterate children of.
22714      * @param {Function} fn method of this class to call on each item.
22715      */
22716     iterateChildren : function(node, fn)
22717     {
22718         if (!node.childNodes.length) {
22719                 return;
22720         }
22721         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22722            fn.call(this, node.childNodes[i])
22723         }
22724     },
22725     
22726     
22727     /**
22728      * cleanTableWidths.
22729      *
22730      * Quite often pasting from word etc.. results in tables with column and widths.
22731      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22732      *
22733      */
22734     cleanTableWidths : function(node)
22735     {
22736          
22737          
22738         if (!node) {
22739             this.cleanTableWidths(this.doc.body);
22740             return;
22741         }
22742         
22743         // ignore list...
22744         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22745             return; 
22746         }
22747         Roo.log(node.tagName);
22748         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22749             this.iterateChildren(node, this.cleanTableWidths);
22750             return;
22751         }
22752         if (node.hasAttribute('width')) {
22753             node.removeAttribute('width');
22754         }
22755         
22756          
22757         if (node.hasAttribute("style")) {
22758             // pretty basic...
22759             
22760             var styles = node.getAttribute("style").split(";");
22761             var nstyle = [];
22762             Roo.each(styles, function(s) {
22763                 if (!s.match(/:/)) {
22764                     return;
22765                 }
22766                 var kv = s.split(":");
22767                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22768                     return;
22769                 }
22770                 // what ever is left... we allow.
22771                 nstyle.push(s);
22772             });
22773             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22774             if (!nstyle.length) {
22775                 node.removeAttribute('style');
22776             }
22777         }
22778         
22779         this.iterateChildren(node, this.cleanTableWidths);
22780         
22781         
22782     },
22783     
22784     
22785     
22786     
22787     domToHTML : function(currentElement, depth, nopadtext) {
22788         
22789         depth = depth || 0;
22790         nopadtext = nopadtext || false;
22791     
22792         if (!currentElement) {
22793             return this.domToHTML(this.doc.body);
22794         }
22795         
22796         //Roo.log(currentElement);
22797         var j;
22798         var allText = false;
22799         var nodeName = currentElement.nodeName;
22800         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22801         
22802         if  (nodeName == '#text') {
22803             
22804             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22805         }
22806         
22807         
22808         var ret = '';
22809         if (nodeName != 'BODY') {
22810              
22811             var i = 0;
22812             // Prints the node tagName, such as <A>, <IMG>, etc
22813             if (tagName) {
22814                 var attr = [];
22815                 for(i = 0; i < currentElement.attributes.length;i++) {
22816                     // quoting?
22817                     var aname = currentElement.attributes.item(i).name;
22818                     if (!currentElement.attributes.item(i).value.length) {
22819                         continue;
22820                     }
22821                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22822                 }
22823                 
22824                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22825             } 
22826             else {
22827                 
22828                 // eack
22829             }
22830         } else {
22831             tagName = false;
22832         }
22833         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22834             return ret;
22835         }
22836         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22837             nopadtext = true;
22838         }
22839         
22840         
22841         // Traverse the tree
22842         i = 0;
22843         var currentElementChild = currentElement.childNodes.item(i);
22844         var allText = true;
22845         var innerHTML  = '';
22846         lastnode = '';
22847         while (currentElementChild) {
22848             // Formatting code (indent the tree so it looks nice on the screen)
22849             var nopad = nopadtext;
22850             if (lastnode == 'SPAN') {
22851                 nopad  = true;
22852             }
22853             // text
22854             if  (currentElementChild.nodeName == '#text') {
22855                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22856                 toadd = nopadtext ? toadd : toadd.trim();
22857                 if (!nopad && toadd.length > 80) {
22858                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22859                 }
22860                 innerHTML  += toadd;
22861                 
22862                 i++;
22863                 currentElementChild = currentElement.childNodes.item(i);
22864                 lastNode = '';
22865                 continue;
22866             }
22867             allText = false;
22868             
22869             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22870                 
22871             // Recursively traverse the tree structure of the child node
22872             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22873             lastnode = currentElementChild.nodeName;
22874             i++;
22875             currentElementChild=currentElement.childNodes.item(i);
22876         }
22877         
22878         ret += innerHTML;
22879         
22880         if (!allText) {
22881                 // The remaining code is mostly for formatting the tree
22882             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22883         }
22884         
22885         
22886         if (tagName) {
22887             ret+= "</"+tagName+">";
22888         }
22889         return ret;
22890         
22891     },
22892         
22893     applyBlacklists : function()
22894     {
22895         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22896         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22897         
22898         this.white = [];
22899         this.black = [];
22900         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22901             if (b.indexOf(tag) > -1) {
22902                 return;
22903             }
22904             this.white.push(tag);
22905             
22906         }, this);
22907         
22908         Roo.each(w, function(tag) {
22909             if (b.indexOf(tag) > -1) {
22910                 return;
22911             }
22912             if (this.white.indexOf(tag) > -1) {
22913                 return;
22914             }
22915             this.white.push(tag);
22916             
22917         }, this);
22918         
22919         
22920         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22921             if (w.indexOf(tag) > -1) {
22922                 return;
22923             }
22924             this.black.push(tag);
22925             
22926         }, this);
22927         
22928         Roo.each(b, function(tag) {
22929             if (w.indexOf(tag) > -1) {
22930                 return;
22931             }
22932             if (this.black.indexOf(tag) > -1) {
22933                 return;
22934             }
22935             this.black.push(tag);
22936             
22937         }, this);
22938         
22939         
22940         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22941         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22942         
22943         this.cwhite = [];
22944         this.cblack = [];
22945         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22946             if (b.indexOf(tag) > -1) {
22947                 return;
22948             }
22949             this.cwhite.push(tag);
22950             
22951         }, this);
22952         
22953         Roo.each(w, function(tag) {
22954             if (b.indexOf(tag) > -1) {
22955                 return;
22956             }
22957             if (this.cwhite.indexOf(tag) > -1) {
22958                 return;
22959             }
22960             this.cwhite.push(tag);
22961             
22962         }, this);
22963         
22964         
22965         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22966             if (w.indexOf(tag) > -1) {
22967                 return;
22968             }
22969             this.cblack.push(tag);
22970             
22971         }, this);
22972         
22973         Roo.each(b, function(tag) {
22974             if (w.indexOf(tag) > -1) {
22975                 return;
22976             }
22977             if (this.cblack.indexOf(tag) > -1) {
22978                 return;
22979             }
22980             this.cblack.push(tag);
22981             
22982         }, this);
22983     },
22984     
22985     setStylesheets : function(stylesheets)
22986     {
22987         if(typeof(stylesheets) == 'string'){
22988             Roo.get(this.iframe.contentDocument.head).createChild({
22989                 tag : 'link',
22990                 rel : 'stylesheet',
22991                 type : 'text/css',
22992                 href : stylesheets
22993             });
22994             
22995             return;
22996         }
22997         var _this = this;
22998      
22999         Roo.each(stylesheets, function(s) {
23000             if(!s.length){
23001                 return;
23002             }
23003             
23004             Roo.get(_this.iframe.contentDocument.head).createChild({
23005                 tag : 'link',
23006                 rel : 'stylesheet',
23007                 type : 'text/css',
23008                 href : s
23009             });
23010         });
23011
23012         
23013     },
23014     
23015     removeStylesheets : function()
23016     {
23017         var _this = this;
23018         
23019         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23020             s.remove();
23021         });
23022     },
23023     
23024     setStyle : function(style)
23025     {
23026         Roo.get(this.iframe.contentDocument.head).createChild({
23027             tag : 'style',
23028             type : 'text/css',
23029             html : style
23030         });
23031
23032         return;
23033     }
23034     
23035     // hide stuff that is not compatible
23036     /**
23037      * @event blur
23038      * @hide
23039      */
23040     /**
23041      * @event change
23042      * @hide
23043      */
23044     /**
23045      * @event focus
23046      * @hide
23047      */
23048     /**
23049      * @event specialkey
23050      * @hide
23051      */
23052     /**
23053      * @cfg {String} fieldClass @hide
23054      */
23055     /**
23056      * @cfg {String} focusClass @hide
23057      */
23058     /**
23059      * @cfg {String} autoCreate @hide
23060      */
23061     /**
23062      * @cfg {String} inputType @hide
23063      */
23064     /**
23065      * @cfg {String} invalidClass @hide
23066      */
23067     /**
23068      * @cfg {String} invalidText @hide
23069      */
23070     /**
23071      * @cfg {String} msgFx @hide
23072      */
23073     /**
23074      * @cfg {String} validateOnBlur @hide
23075      */
23076 });
23077
23078 Roo.HtmlEditorCore.white = [
23079         'area', 'br', 'img', 'input', 'hr', 'wbr',
23080         
23081        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23082        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23083        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23084        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23085        'table',   'ul',         'xmp', 
23086        
23087        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23088       'thead',   'tr', 
23089      
23090       'dir', 'menu', 'ol', 'ul', 'dl',
23091        
23092       'embed',  'object'
23093 ];
23094
23095
23096 Roo.HtmlEditorCore.black = [
23097     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23098         'applet', // 
23099         'base',   'basefont', 'bgsound', 'blink',  'body', 
23100         'frame',  'frameset', 'head',    'html',   'ilayer', 
23101         'iframe', 'layer',  'link',     'meta',    'object',   
23102         'script', 'style' ,'title',  'xml' // clean later..
23103 ];
23104 Roo.HtmlEditorCore.clean = [
23105     'script', 'style', 'title', 'xml'
23106 ];
23107 Roo.HtmlEditorCore.remove = [
23108     'font'
23109 ];
23110 // attributes..
23111
23112 Roo.HtmlEditorCore.ablack = [
23113     'on'
23114 ];
23115     
23116 Roo.HtmlEditorCore.aclean = [ 
23117     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23118 ];
23119
23120 // protocols..
23121 Roo.HtmlEditorCore.pwhite= [
23122         'http',  'https',  'mailto'
23123 ];
23124
23125 // white listed style attributes.
23126 Roo.HtmlEditorCore.cwhite= [
23127       //  'text-align', /// default is to allow most things..
23128       
23129          
23130 //        'font-size'//??
23131 ];
23132
23133 // black listed style attributes.
23134 Roo.HtmlEditorCore.cblack= [
23135       //  'font-size' -- this can be set by the project 
23136 ];
23137
23138
23139 Roo.HtmlEditorCore.swapCodes   =[ 
23140     [    8211, "--" ], 
23141     [    8212, "--" ], 
23142     [    8216,  "'" ],  
23143     [    8217, "'" ],  
23144     [    8220, '"' ],  
23145     [    8221, '"' ],  
23146     [    8226, "*" ],  
23147     [    8230, "..." ]
23148 ]; 
23149
23150     /*
23151  * - LGPL
23152  *
23153  * HtmlEditor
23154  * 
23155  */
23156
23157 /**
23158  * @class Roo.bootstrap.HtmlEditor
23159  * @extends Roo.bootstrap.TextArea
23160  * Bootstrap HtmlEditor class
23161
23162  * @constructor
23163  * Create a new HtmlEditor
23164  * @param {Object} config The config object
23165  */
23166
23167 Roo.bootstrap.HtmlEditor = function(config){
23168     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23169     if (!this.toolbars) {
23170         this.toolbars = [];
23171     }
23172     
23173     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23174     this.addEvents({
23175             /**
23176              * @event initialize
23177              * Fires when the editor is fully initialized (including the iframe)
23178              * @param {HtmlEditor} this
23179              */
23180             initialize: true,
23181             /**
23182              * @event activate
23183              * Fires when the editor is first receives the focus. Any insertion must wait
23184              * until after this event.
23185              * @param {HtmlEditor} this
23186              */
23187             activate: true,
23188              /**
23189              * @event beforesync
23190              * Fires before the textarea is updated with content from the editor iframe. Return false
23191              * to cancel the sync.
23192              * @param {HtmlEditor} this
23193              * @param {String} html
23194              */
23195             beforesync: true,
23196              /**
23197              * @event beforepush
23198              * Fires before the iframe editor is updated with content from the textarea. Return false
23199              * to cancel the push.
23200              * @param {HtmlEditor} this
23201              * @param {String} html
23202              */
23203             beforepush: true,
23204              /**
23205              * @event sync
23206              * Fires when the textarea is updated with content from the editor iframe.
23207              * @param {HtmlEditor} this
23208              * @param {String} html
23209              */
23210             sync: true,
23211              /**
23212              * @event push
23213              * Fires when the iframe editor is updated with content from the textarea.
23214              * @param {HtmlEditor} this
23215              * @param {String} html
23216              */
23217             push: true,
23218              /**
23219              * @event editmodechange
23220              * Fires when the editor switches edit modes
23221              * @param {HtmlEditor} this
23222              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23223              */
23224             editmodechange: true,
23225             /**
23226              * @event editorevent
23227              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23228              * @param {HtmlEditor} this
23229              */
23230             editorevent: true,
23231             /**
23232              * @event firstfocus
23233              * Fires when on first focus - needed by toolbars..
23234              * @param {HtmlEditor} this
23235              */
23236             firstfocus: true,
23237             /**
23238              * @event autosave
23239              * Auto save the htmlEditor value as a file into Events
23240              * @param {HtmlEditor} this
23241              */
23242             autosave: true,
23243             /**
23244              * @event savedpreview
23245              * preview the saved version of htmlEditor
23246              * @param {HtmlEditor} this
23247              */
23248             savedpreview: true
23249         });
23250 };
23251
23252
23253 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23254     
23255     
23256       /**
23257      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23258      */
23259     toolbars : false,
23260     
23261      /**
23262     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23263     */
23264     btns : [],
23265    
23266      /**
23267      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23268      *                        Roo.resizable.
23269      */
23270     resizable : false,
23271      /**
23272      * @cfg {Number} height (in pixels)
23273      */   
23274     height: 300,
23275    /**
23276      * @cfg {Number} width (in pixels)
23277      */   
23278     width: false,
23279     
23280     /**
23281      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23282      * 
23283      */
23284     stylesheets: false,
23285     
23286     // id of frame..
23287     frameId: false,
23288     
23289     // private properties
23290     validationEvent : false,
23291     deferHeight: true,
23292     initialized : false,
23293     activated : false,
23294     
23295     onFocus : Roo.emptyFn,
23296     iframePad:3,
23297     hideMode:'offsets',
23298     
23299     tbContainer : false,
23300     
23301     bodyCls : '',
23302     
23303     toolbarContainer :function() {
23304         return this.wrap.select('.x-html-editor-tb',true).first();
23305     },
23306
23307     /**
23308      * Protected method that will not generally be called directly. It
23309      * is called when the editor creates its toolbar. Override this method if you need to
23310      * add custom toolbar buttons.
23311      * @param {HtmlEditor} editor
23312      */
23313     createToolbar : function(){
23314         Roo.log('renewing');
23315         Roo.log("create toolbars");
23316         
23317         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23318         this.toolbars[0].render(this.toolbarContainer());
23319         
23320         return;
23321         
23322 //        if (!editor.toolbars || !editor.toolbars.length) {
23323 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23324 //        }
23325 //        
23326 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23327 //            editor.toolbars[i] = Roo.factory(
23328 //                    typeof(editor.toolbars[i]) == 'string' ?
23329 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23330 //                Roo.bootstrap.HtmlEditor);
23331 //            editor.toolbars[i].init(editor);
23332 //        }
23333     },
23334
23335      
23336     // private
23337     onRender : function(ct, position)
23338     {
23339        // Roo.log("Call onRender: " + this.xtype);
23340         var _t = this;
23341         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23342       
23343         this.wrap = this.inputEl().wrap({
23344             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23345         });
23346         
23347         this.editorcore.onRender(ct, position);
23348          
23349         if (this.resizable) {
23350             this.resizeEl = new Roo.Resizable(this.wrap, {
23351                 pinned : true,
23352                 wrap: true,
23353                 dynamic : true,
23354                 minHeight : this.height,
23355                 height: this.height,
23356                 handles : this.resizable,
23357                 width: this.width,
23358                 listeners : {
23359                     resize : function(r, w, h) {
23360                         _t.onResize(w,h); // -something
23361                     }
23362                 }
23363             });
23364             
23365         }
23366         this.createToolbar(this);
23367        
23368         
23369         if(!this.width && this.resizable){
23370             this.setSize(this.wrap.getSize());
23371         }
23372         if (this.resizeEl) {
23373             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23374             // should trigger onReize..
23375         }
23376         
23377     },
23378
23379     // private
23380     onResize : function(w, h)
23381     {
23382         Roo.log('resize: ' +w + ',' + h );
23383         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23384         var ew = false;
23385         var eh = false;
23386         
23387         if(this.inputEl() ){
23388             if(typeof w == 'number'){
23389                 var aw = w - this.wrap.getFrameWidth('lr');
23390                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23391                 ew = aw;
23392             }
23393             if(typeof h == 'number'){
23394                  var tbh = -11;  // fixme it needs to tool bar size!
23395                 for (var i =0; i < this.toolbars.length;i++) {
23396                     // fixme - ask toolbars for heights?
23397                     tbh += this.toolbars[i].el.getHeight();
23398                     //if (this.toolbars[i].footer) {
23399                     //    tbh += this.toolbars[i].footer.el.getHeight();
23400                     //}
23401                 }
23402               
23403                 
23404                 
23405                 
23406                 
23407                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23408                 ah -= 5; // knock a few pixes off for look..
23409                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23410                 var eh = ah;
23411             }
23412         }
23413         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23414         this.editorcore.onResize(ew,eh);
23415         
23416     },
23417
23418     /**
23419      * Toggles the editor between standard and source edit mode.
23420      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23421      */
23422     toggleSourceEdit : function(sourceEditMode)
23423     {
23424         this.editorcore.toggleSourceEdit(sourceEditMode);
23425         
23426         if(this.editorcore.sourceEditMode){
23427             Roo.log('editor - showing textarea');
23428             
23429 //            Roo.log('in');
23430 //            Roo.log(this.syncValue());
23431             this.syncValue();
23432             this.inputEl().removeClass(['hide', 'x-hidden']);
23433             this.inputEl().dom.removeAttribute('tabIndex');
23434             this.inputEl().focus();
23435         }else{
23436             Roo.log('editor - hiding textarea');
23437 //            Roo.log('out')
23438 //            Roo.log(this.pushValue()); 
23439             this.pushValue();
23440             
23441             this.inputEl().addClass(['hide', 'x-hidden']);
23442             this.inputEl().dom.setAttribute('tabIndex', -1);
23443             //this.deferFocus();
23444         }
23445          
23446         if(this.resizable){
23447             this.setSize(this.wrap.getSize());
23448         }
23449         
23450         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23451     },
23452  
23453     // private (for BoxComponent)
23454     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23455
23456     // private (for BoxComponent)
23457     getResizeEl : function(){
23458         return this.wrap;
23459     },
23460
23461     // private (for BoxComponent)
23462     getPositionEl : function(){
23463         return this.wrap;
23464     },
23465
23466     // private
23467     initEvents : function(){
23468         this.originalValue = this.getValue();
23469     },
23470
23471 //    /**
23472 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23473 //     * @method
23474 //     */
23475 //    markInvalid : Roo.emptyFn,
23476 //    /**
23477 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23478 //     * @method
23479 //     */
23480 //    clearInvalid : Roo.emptyFn,
23481
23482     setValue : function(v){
23483         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23484         this.editorcore.pushValue();
23485     },
23486
23487      
23488     // private
23489     deferFocus : function(){
23490         this.focus.defer(10, this);
23491     },
23492
23493     // doc'ed in Field
23494     focus : function(){
23495         this.editorcore.focus();
23496         
23497     },
23498       
23499
23500     // private
23501     onDestroy : function(){
23502         
23503         
23504         
23505         if(this.rendered){
23506             
23507             for (var i =0; i < this.toolbars.length;i++) {
23508                 // fixme - ask toolbars for heights?
23509                 this.toolbars[i].onDestroy();
23510             }
23511             
23512             this.wrap.dom.innerHTML = '';
23513             this.wrap.remove();
23514         }
23515     },
23516
23517     // private
23518     onFirstFocus : function(){
23519         //Roo.log("onFirstFocus");
23520         this.editorcore.onFirstFocus();
23521          for (var i =0; i < this.toolbars.length;i++) {
23522             this.toolbars[i].onFirstFocus();
23523         }
23524         
23525     },
23526     
23527     // private
23528     syncValue : function()
23529     {   
23530         this.editorcore.syncValue();
23531     },
23532     
23533     pushValue : function()
23534     {   
23535         this.editorcore.pushValue();
23536     }
23537      
23538     
23539     // hide stuff that is not compatible
23540     /**
23541      * @event blur
23542      * @hide
23543      */
23544     /**
23545      * @event change
23546      * @hide
23547      */
23548     /**
23549      * @event focus
23550      * @hide
23551      */
23552     /**
23553      * @event specialkey
23554      * @hide
23555      */
23556     /**
23557      * @cfg {String} fieldClass @hide
23558      */
23559     /**
23560      * @cfg {String} focusClass @hide
23561      */
23562     /**
23563      * @cfg {String} autoCreate @hide
23564      */
23565     /**
23566      * @cfg {String} inputType @hide
23567      */
23568     /**
23569      * @cfg {String} invalidClass @hide
23570      */
23571     /**
23572      * @cfg {String} invalidText @hide
23573      */
23574     /**
23575      * @cfg {String} msgFx @hide
23576      */
23577     /**
23578      * @cfg {String} validateOnBlur @hide
23579      */
23580 });
23581  
23582     
23583    
23584    
23585    
23586       
23587 Roo.namespace('Roo.bootstrap.htmleditor');
23588 /**
23589  * @class Roo.bootstrap.HtmlEditorToolbar1
23590  * Basic Toolbar
23591  * 
23592  * Usage:
23593  *
23594  new Roo.bootstrap.HtmlEditor({
23595     ....
23596     toolbars : [
23597         new Roo.bootstrap.HtmlEditorToolbar1({
23598             disable : { fonts: 1 , format: 1, ..., ... , ...],
23599             btns : [ .... ]
23600         })
23601     }
23602      
23603  * 
23604  * @cfg {Object} disable List of elements to disable..
23605  * @cfg {Array} btns List of additional buttons.
23606  * 
23607  * 
23608  * NEEDS Extra CSS? 
23609  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23610  */
23611  
23612 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23613 {
23614     
23615     Roo.apply(this, config);
23616     
23617     // default disabled, based on 'good practice'..
23618     this.disable = this.disable || {};
23619     Roo.applyIf(this.disable, {
23620         fontSize : true,
23621         colors : true,
23622         specialElements : true
23623     });
23624     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23625     
23626     this.editor = config.editor;
23627     this.editorcore = config.editor.editorcore;
23628     
23629     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23630     
23631     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23632     // dont call parent... till later.
23633 }
23634 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23635      
23636     bar : true,
23637     
23638     editor : false,
23639     editorcore : false,
23640     
23641     
23642     formats : [
23643         "p" ,  
23644         "h1","h2","h3","h4","h5","h6", 
23645         "pre", "code", 
23646         "abbr", "acronym", "address", "cite", "samp", "var",
23647         'div','span'
23648     ],
23649     
23650     onRender : function(ct, position)
23651     {
23652        // Roo.log("Call onRender: " + this.xtype);
23653         
23654        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23655        Roo.log(this.el);
23656        this.el.dom.style.marginBottom = '0';
23657        var _this = this;
23658        var editorcore = this.editorcore;
23659        var editor= this.editor;
23660        
23661        var children = [];
23662        var btn = function(id,cmd , toggle, handler, html){
23663        
23664             var  event = toggle ? 'toggle' : 'click';
23665        
23666             var a = {
23667                 size : 'sm',
23668                 xtype: 'Button',
23669                 xns: Roo.bootstrap,
23670                 glyphicon : id,
23671                 cmd : id || cmd,
23672                 enableToggle:toggle !== false,
23673                 html : html || '',
23674                 pressed : toggle ? false : null,
23675                 listeners : {}
23676             };
23677             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23678                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23679             };
23680             children.push(a);
23681             return a;
23682        }
23683        
23684     //    var cb_box = function...
23685         
23686         var style = {
23687                 xtype: 'Button',
23688                 size : 'sm',
23689                 xns: Roo.bootstrap,
23690                 glyphicon : 'font',
23691                 //html : 'submit'
23692                 menu : {
23693                     xtype: 'Menu',
23694                     xns: Roo.bootstrap,
23695                     items:  []
23696                 }
23697         };
23698         Roo.each(this.formats, function(f) {
23699             style.menu.items.push({
23700                 xtype :'MenuItem',
23701                 xns: Roo.bootstrap,
23702                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23703                 tagname : f,
23704                 listeners : {
23705                     click : function()
23706                     {
23707                         editorcore.insertTag(this.tagname);
23708                         editor.focus();
23709                     }
23710                 }
23711                 
23712             });
23713         });
23714         children.push(style);   
23715         
23716         btn('bold',false,true);
23717         btn('italic',false,true);
23718         btn('align-left', 'justifyleft',true);
23719         btn('align-center', 'justifycenter',true);
23720         btn('align-right' , 'justifyright',true);
23721         btn('link', false, false, function(btn) {
23722             //Roo.log("create link?");
23723             var url = prompt(this.createLinkText, this.defaultLinkValue);
23724             if(url && url != 'http:/'+'/'){
23725                 this.editorcore.relayCmd('createlink', url);
23726             }
23727         }),
23728         btn('list','insertunorderedlist',true);
23729         btn('pencil', false,true, function(btn){
23730                 Roo.log(this);
23731                 this.toggleSourceEdit(btn.pressed);
23732         });
23733         
23734         if (this.editor.btns.length > 0) {
23735             for (var i = 0; i<this.editor.btns.length; i++) {
23736                 children.push(this.editor.btns[i]);
23737             }
23738         }
23739         
23740         /*
23741         var cog = {
23742                 xtype: 'Button',
23743                 size : 'sm',
23744                 xns: Roo.bootstrap,
23745                 glyphicon : 'cog',
23746                 //html : 'submit'
23747                 menu : {
23748                     xtype: 'Menu',
23749                     xns: Roo.bootstrap,
23750                     items:  []
23751                 }
23752         };
23753         
23754         cog.menu.items.push({
23755             xtype :'MenuItem',
23756             xns: Roo.bootstrap,
23757             html : Clean styles,
23758             tagname : f,
23759             listeners : {
23760                 click : function()
23761                 {
23762                     editorcore.insertTag(this.tagname);
23763                     editor.focus();
23764                 }
23765             }
23766             
23767         });
23768        */
23769         
23770          
23771        this.xtype = 'NavSimplebar';
23772         
23773         for(var i=0;i< children.length;i++) {
23774             
23775             this.buttons.add(this.addxtypeChild(children[i]));
23776             
23777         }
23778         
23779         editor.on('editorevent', this.updateToolbar, this);
23780     },
23781     onBtnClick : function(id)
23782     {
23783        this.editorcore.relayCmd(id);
23784        this.editorcore.focus();
23785     },
23786     
23787     /**
23788      * Protected method that will not generally be called directly. It triggers
23789      * a toolbar update by reading the markup state of the current selection in the editor.
23790      */
23791     updateToolbar: function(){
23792
23793         if(!this.editorcore.activated){
23794             this.editor.onFirstFocus(); // is this neeed?
23795             return;
23796         }
23797
23798         var btns = this.buttons; 
23799         var doc = this.editorcore.doc;
23800         btns.get('bold').setActive(doc.queryCommandState('bold'));
23801         btns.get('italic').setActive(doc.queryCommandState('italic'));
23802         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23803         
23804         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23805         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23806         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23807         
23808         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23809         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23810          /*
23811         
23812         var ans = this.editorcore.getAllAncestors();
23813         if (this.formatCombo) {
23814             
23815             
23816             var store = this.formatCombo.store;
23817             this.formatCombo.setValue("");
23818             for (var i =0; i < ans.length;i++) {
23819                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23820                     // select it..
23821                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23822                     break;
23823                 }
23824             }
23825         }
23826         
23827         
23828         
23829         // hides menus... - so this cant be on a menu...
23830         Roo.bootstrap.MenuMgr.hideAll();
23831         */
23832         Roo.bootstrap.MenuMgr.hideAll();
23833         //this.editorsyncValue();
23834     },
23835     onFirstFocus: function() {
23836         this.buttons.each(function(item){
23837            item.enable();
23838         });
23839     },
23840     toggleSourceEdit : function(sourceEditMode){
23841         
23842           
23843         if(sourceEditMode){
23844             Roo.log("disabling buttons");
23845            this.buttons.each( function(item){
23846                 if(item.cmd != 'pencil'){
23847                     item.disable();
23848                 }
23849             });
23850           
23851         }else{
23852             Roo.log("enabling buttons");
23853             if(this.editorcore.initialized){
23854                 this.buttons.each( function(item){
23855                     item.enable();
23856                 });
23857             }
23858             
23859         }
23860         Roo.log("calling toggole on editor");
23861         // tell the editor that it's been pressed..
23862         this.editor.toggleSourceEdit(sourceEditMode);
23863        
23864     }
23865 });
23866
23867
23868
23869
23870
23871 /**
23872  * @class Roo.bootstrap.Table.AbstractSelectionModel
23873  * @extends Roo.util.Observable
23874  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23875  * implemented by descendant classes.  This class should not be directly instantiated.
23876  * @constructor
23877  */
23878 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23879     this.locked = false;
23880     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23881 };
23882
23883
23884 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23885     /** @ignore Called by the grid automatically. Do not call directly. */
23886     init : function(grid){
23887         this.grid = grid;
23888         this.initEvents();
23889     },
23890
23891     /**
23892      * Locks the selections.
23893      */
23894     lock : function(){
23895         this.locked = true;
23896     },
23897
23898     /**
23899      * Unlocks the selections.
23900      */
23901     unlock : function(){
23902         this.locked = false;
23903     },
23904
23905     /**
23906      * Returns true if the selections are locked.
23907      * @return {Boolean}
23908      */
23909     isLocked : function(){
23910         return this.locked;
23911     }
23912 });
23913 /**
23914  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23915  * @class Roo.bootstrap.Table.RowSelectionModel
23916  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23917  * It supports multiple selections and keyboard selection/navigation. 
23918  * @constructor
23919  * @param {Object} config
23920  */
23921
23922 Roo.bootstrap.Table.RowSelectionModel = function(config){
23923     Roo.apply(this, config);
23924     this.selections = new Roo.util.MixedCollection(false, function(o){
23925         return o.id;
23926     });
23927
23928     this.last = false;
23929     this.lastActive = false;
23930
23931     this.addEvents({
23932         /**
23933              * @event selectionchange
23934              * Fires when the selection changes
23935              * @param {SelectionModel} this
23936              */
23937             "selectionchange" : true,
23938         /**
23939              * @event afterselectionchange
23940              * Fires after the selection changes (eg. by key press or clicking)
23941              * @param {SelectionModel} this
23942              */
23943             "afterselectionchange" : true,
23944         /**
23945              * @event beforerowselect
23946              * Fires when a row is selected being selected, return false to cancel.
23947              * @param {SelectionModel} this
23948              * @param {Number} rowIndex The selected index
23949              * @param {Boolean} keepExisting False if other selections will be cleared
23950              */
23951             "beforerowselect" : true,
23952         /**
23953              * @event rowselect
23954              * Fires when a row is selected.
23955              * @param {SelectionModel} this
23956              * @param {Number} rowIndex The selected index
23957              * @param {Roo.data.Record} r The record
23958              */
23959             "rowselect" : true,
23960         /**
23961              * @event rowdeselect
23962              * Fires when a row is deselected.
23963              * @param {SelectionModel} this
23964              * @param {Number} rowIndex The selected index
23965              */
23966         "rowdeselect" : true
23967     });
23968     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23969     this.locked = false;
23970  };
23971
23972 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23973     /**
23974      * @cfg {Boolean} singleSelect
23975      * True to allow selection of only one row at a time (defaults to false)
23976      */
23977     singleSelect : false,
23978
23979     // private
23980     initEvents : function()
23981     {
23982
23983         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23984         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23985         //}else{ // allow click to work like normal
23986          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23987         //}
23988         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23989         this.grid.on("rowclick", this.handleMouseDown, this);
23990         
23991         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23992             "up" : function(e){
23993                 if(!e.shiftKey){
23994                     this.selectPrevious(e.shiftKey);
23995                 }else if(this.last !== false && this.lastActive !== false){
23996                     var last = this.last;
23997                     this.selectRange(this.last,  this.lastActive-1);
23998                     this.grid.getView().focusRow(this.lastActive);
23999                     if(last !== false){
24000                         this.last = last;
24001                     }
24002                 }else{
24003                     this.selectFirstRow();
24004                 }
24005                 this.fireEvent("afterselectionchange", this);
24006             },
24007             "down" : function(e){
24008                 if(!e.shiftKey){
24009                     this.selectNext(e.shiftKey);
24010                 }else if(this.last !== false && this.lastActive !== false){
24011                     var last = this.last;
24012                     this.selectRange(this.last,  this.lastActive+1);
24013                     this.grid.getView().focusRow(this.lastActive);
24014                     if(last !== false){
24015                         this.last = last;
24016                     }
24017                 }else{
24018                     this.selectFirstRow();
24019                 }
24020                 this.fireEvent("afterselectionchange", this);
24021             },
24022             scope: this
24023         });
24024         this.grid.store.on('load', function(){
24025             this.selections.clear();
24026         },this);
24027         /*
24028         var view = this.grid.view;
24029         view.on("refresh", this.onRefresh, this);
24030         view.on("rowupdated", this.onRowUpdated, this);
24031         view.on("rowremoved", this.onRemove, this);
24032         */
24033     },
24034
24035     // private
24036     onRefresh : function()
24037     {
24038         var ds = this.grid.store, i, v = this.grid.view;
24039         var s = this.selections;
24040         s.each(function(r){
24041             if((i = ds.indexOfId(r.id)) != -1){
24042                 v.onRowSelect(i);
24043             }else{
24044                 s.remove(r);
24045             }
24046         });
24047     },
24048
24049     // private
24050     onRemove : function(v, index, r){
24051         this.selections.remove(r);
24052     },
24053
24054     // private
24055     onRowUpdated : function(v, index, r){
24056         if(this.isSelected(r)){
24057             v.onRowSelect(index);
24058         }
24059     },
24060
24061     /**
24062      * Select records.
24063      * @param {Array} records The records to select
24064      * @param {Boolean} keepExisting (optional) True to keep existing selections
24065      */
24066     selectRecords : function(records, keepExisting)
24067     {
24068         if(!keepExisting){
24069             this.clearSelections();
24070         }
24071             var ds = this.grid.store;
24072         for(var i = 0, len = records.length; i < len; i++){
24073             this.selectRow(ds.indexOf(records[i]), true);
24074         }
24075     },
24076
24077     /**
24078      * Gets the number of selected rows.
24079      * @return {Number}
24080      */
24081     getCount : function(){
24082         return this.selections.length;
24083     },
24084
24085     /**
24086      * Selects the first row in the grid.
24087      */
24088     selectFirstRow : function(){
24089         this.selectRow(0);
24090     },
24091
24092     /**
24093      * Select the last row.
24094      * @param {Boolean} keepExisting (optional) True to keep existing selections
24095      */
24096     selectLastRow : function(keepExisting){
24097         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24098         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24099     },
24100
24101     /**
24102      * Selects the row immediately following the last selected row.
24103      * @param {Boolean} keepExisting (optional) True to keep existing selections
24104      */
24105     selectNext : function(keepExisting)
24106     {
24107             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24108             this.selectRow(this.last+1, keepExisting);
24109             this.grid.getView().focusRow(this.last);
24110         }
24111     },
24112
24113     /**
24114      * Selects the row that precedes the last selected row.
24115      * @param {Boolean} keepExisting (optional) True to keep existing selections
24116      */
24117     selectPrevious : function(keepExisting){
24118         if(this.last){
24119             this.selectRow(this.last-1, keepExisting);
24120             this.grid.getView().focusRow(this.last);
24121         }
24122     },
24123
24124     /**
24125      * Returns the selected records
24126      * @return {Array} Array of selected records
24127      */
24128     getSelections : function(){
24129         return [].concat(this.selections.items);
24130     },
24131
24132     /**
24133      * Returns the first selected record.
24134      * @return {Record}
24135      */
24136     getSelected : function(){
24137         return this.selections.itemAt(0);
24138     },
24139
24140
24141     /**
24142      * Clears all selections.
24143      */
24144     clearSelections : function(fast)
24145     {
24146         if(this.locked) {
24147             return;
24148         }
24149         if(fast !== true){
24150                 var ds = this.grid.store;
24151             var s = this.selections;
24152             s.each(function(r){
24153                 this.deselectRow(ds.indexOfId(r.id));
24154             }, this);
24155             s.clear();
24156         }else{
24157             this.selections.clear();
24158         }
24159         this.last = false;
24160     },
24161
24162
24163     /**
24164      * Selects all rows.
24165      */
24166     selectAll : function(){
24167         if(this.locked) {
24168             return;
24169         }
24170         this.selections.clear();
24171         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24172             this.selectRow(i, true);
24173         }
24174     },
24175
24176     /**
24177      * Returns True if there is a selection.
24178      * @return {Boolean}
24179      */
24180     hasSelection : function(){
24181         return this.selections.length > 0;
24182     },
24183
24184     /**
24185      * Returns True if the specified row is selected.
24186      * @param {Number/Record} record The record or index of the record to check
24187      * @return {Boolean}
24188      */
24189     isSelected : function(index){
24190             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24191         return (r && this.selections.key(r.id) ? true : false);
24192     },
24193
24194     /**
24195      * Returns True if the specified record id is selected.
24196      * @param {String} id The id of record to check
24197      * @return {Boolean}
24198      */
24199     isIdSelected : function(id){
24200         return (this.selections.key(id) ? true : false);
24201     },
24202
24203
24204     // private
24205     handleMouseDBClick : function(e, t){
24206         
24207     },
24208     // private
24209     handleMouseDown : function(e, t)
24210     {
24211             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24212         if(this.isLocked() || rowIndex < 0 ){
24213             return;
24214         };
24215         if(e.shiftKey && this.last !== false){
24216             var last = this.last;
24217             this.selectRange(last, rowIndex, e.ctrlKey);
24218             this.last = last; // reset the last
24219             t.focus();
24220     
24221         }else{
24222             var isSelected = this.isSelected(rowIndex);
24223             //Roo.log("select row:" + rowIndex);
24224             if(isSelected){
24225                 this.deselectRow(rowIndex);
24226             } else {
24227                         this.selectRow(rowIndex, true);
24228             }
24229     
24230             /*
24231                 if(e.button !== 0 && isSelected){
24232                 alert('rowIndex 2: ' + rowIndex);
24233                     view.focusRow(rowIndex);
24234                 }else if(e.ctrlKey && isSelected){
24235                     this.deselectRow(rowIndex);
24236                 }else if(!isSelected){
24237                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24238                     view.focusRow(rowIndex);
24239                 }
24240             */
24241         }
24242         this.fireEvent("afterselectionchange", this);
24243     },
24244     // private
24245     handleDragableRowClick :  function(grid, rowIndex, e) 
24246     {
24247         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24248             this.selectRow(rowIndex, false);
24249             grid.view.focusRow(rowIndex);
24250              this.fireEvent("afterselectionchange", this);
24251         }
24252     },
24253     
24254     /**
24255      * Selects multiple rows.
24256      * @param {Array} rows Array of the indexes of the row to select
24257      * @param {Boolean} keepExisting (optional) True to keep existing selections
24258      */
24259     selectRows : function(rows, keepExisting){
24260         if(!keepExisting){
24261             this.clearSelections();
24262         }
24263         for(var i = 0, len = rows.length; i < len; i++){
24264             this.selectRow(rows[i], true);
24265         }
24266     },
24267
24268     /**
24269      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24270      * @param {Number} startRow The index of the first row in the range
24271      * @param {Number} endRow The index of the last row in the range
24272      * @param {Boolean} keepExisting (optional) True to retain existing selections
24273      */
24274     selectRange : function(startRow, endRow, keepExisting){
24275         if(this.locked) {
24276             return;
24277         }
24278         if(!keepExisting){
24279             this.clearSelections();
24280         }
24281         if(startRow <= endRow){
24282             for(var i = startRow; i <= endRow; i++){
24283                 this.selectRow(i, true);
24284             }
24285         }else{
24286             for(var i = startRow; i >= endRow; i--){
24287                 this.selectRow(i, true);
24288             }
24289         }
24290     },
24291
24292     /**
24293      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24294      * @param {Number} startRow The index of the first row in the range
24295      * @param {Number} endRow The index of the last row in the range
24296      */
24297     deselectRange : function(startRow, endRow, preventViewNotify){
24298         if(this.locked) {
24299             return;
24300         }
24301         for(var i = startRow; i <= endRow; i++){
24302             this.deselectRow(i, preventViewNotify);
24303         }
24304     },
24305
24306     /**
24307      * Selects a row.
24308      * @param {Number} row The index of the row to select
24309      * @param {Boolean} keepExisting (optional) True to keep existing selections
24310      */
24311     selectRow : function(index, keepExisting, preventViewNotify)
24312     {
24313             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24314             return;
24315         }
24316         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24317             if(!keepExisting || this.singleSelect){
24318                 this.clearSelections();
24319             }
24320             
24321             var r = this.grid.store.getAt(index);
24322             //console.log('selectRow - record id :' + r.id);
24323             
24324             this.selections.add(r);
24325             this.last = this.lastActive = index;
24326             if(!preventViewNotify){
24327                 var proxy = new Roo.Element(
24328                                 this.grid.getRowDom(index)
24329                 );
24330                 proxy.addClass('bg-info info');
24331             }
24332             this.fireEvent("rowselect", this, index, r);
24333             this.fireEvent("selectionchange", this);
24334         }
24335     },
24336
24337     /**
24338      * Deselects a row.
24339      * @param {Number} row The index of the row to deselect
24340      */
24341     deselectRow : function(index, preventViewNotify)
24342     {
24343         if(this.locked) {
24344             return;
24345         }
24346         if(this.last == index){
24347             this.last = false;
24348         }
24349         if(this.lastActive == index){
24350             this.lastActive = false;
24351         }
24352         
24353         var r = this.grid.store.getAt(index);
24354         if (!r) {
24355             return;
24356         }
24357         
24358         this.selections.remove(r);
24359         //.console.log('deselectRow - record id :' + r.id);
24360         if(!preventViewNotify){
24361         
24362             var proxy = new Roo.Element(
24363                 this.grid.getRowDom(index)
24364             );
24365             proxy.removeClass('bg-info info');
24366         }
24367         this.fireEvent("rowdeselect", this, index);
24368         this.fireEvent("selectionchange", this);
24369     },
24370
24371     // private
24372     restoreLast : function(){
24373         if(this._last){
24374             this.last = this._last;
24375         }
24376     },
24377
24378     // private
24379     acceptsNav : function(row, col, cm){
24380         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24381     },
24382
24383     // private
24384     onEditorKey : function(field, e){
24385         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24386         if(k == e.TAB){
24387             e.stopEvent();
24388             ed.completeEdit();
24389             if(e.shiftKey){
24390                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24391             }else{
24392                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24393             }
24394         }else if(k == e.ENTER && !e.ctrlKey){
24395             e.stopEvent();
24396             ed.completeEdit();
24397             if(e.shiftKey){
24398                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24399             }else{
24400                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24401             }
24402         }else if(k == e.ESC){
24403             ed.cancelEdit();
24404         }
24405         if(newCell){
24406             g.startEditing(newCell[0], newCell[1]);
24407         }
24408     }
24409 });
24410 /*
24411  * Based on:
24412  * Ext JS Library 1.1.1
24413  * Copyright(c) 2006-2007, Ext JS, LLC.
24414  *
24415  * Originally Released Under LGPL - original licence link has changed is not relivant.
24416  *
24417  * Fork - LGPL
24418  * <script type="text/javascript">
24419  */
24420  
24421 /**
24422  * @class Roo.bootstrap.PagingToolbar
24423  * @extends Roo.bootstrap.NavSimplebar
24424  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24425  * @constructor
24426  * Create a new PagingToolbar
24427  * @param {Object} config The config object
24428  * @param {Roo.data.Store} store
24429  */
24430 Roo.bootstrap.PagingToolbar = function(config)
24431 {
24432     // old args format still supported... - xtype is prefered..
24433         // created from xtype...
24434     
24435     this.ds = config.dataSource;
24436     
24437     if (config.store && !this.ds) {
24438         this.store= Roo.factory(config.store, Roo.data);
24439         this.ds = this.store;
24440         this.ds.xmodule = this.xmodule || false;
24441     }
24442     
24443     this.toolbarItems = [];
24444     if (config.items) {
24445         this.toolbarItems = config.items;
24446     }
24447     
24448     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24449     
24450     this.cursor = 0;
24451     
24452     if (this.ds) { 
24453         this.bind(this.ds);
24454     }
24455     
24456     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24457     
24458 };
24459
24460 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24461     /**
24462      * @cfg {Roo.data.Store} dataSource
24463      * The underlying data store providing the paged data
24464      */
24465     /**
24466      * @cfg {String/HTMLElement/Element} container
24467      * container The id or element that will contain the toolbar
24468      */
24469     /**
24470      * @cfg {Boolean} displayInfo
24471      * True to display the displayMsg (defaults to false)
24472      */
24473     /**
24474      * @cfg {Number} pageSize
24475      * The number of records to display per page (defaults to 20)
24476      */
24477     pageSize: 20,
24478     /**
24479      * @cfg {String} displayMsg
24480      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24481      */
24482     displayMsg : 'Displaying {0} - {1} of {2}',
24483     /**
24484      * @cfg {String} emptyMsg
24485      * The message to display when no records are found (defaults to "No data to display")
24486      */
24487     emptyMsg : 'No data to display',
24488     /**
24489      * Customizable piece of the default paging text (defaults to "Page")
24490      * @type String
24491      */
24492     beforePageText : "Page",
24493     /**
24494      * Customizable piece of the default paging text (defaults to "of %0")
24495      * @type String
24496      */
24497     afterPageText : "of {0}",
24498     /**
24499      * Customizable piece of the default paging text (defaults to "First Page")
24500      * @type String
24501      */
24502     firstText : "First Page",
24503     /**
24504      * Customizable piece of the default paging text (defaults to "Previous Page")
24505      * @type String
24506      */
24507     prevText : "Previous Page",
24508     /**
24509      * Customizable piece of the default paging text (defaults to "Next Page")
24510      * @type String
24511      */
24512     nextText : "Next Page",
24513     /**
24514      * Customizable piece of the default paging text (defaults to "Last Page")
24515      * @type String
24516      */
24517     lastText : "Last Page",
24518     /**
24519      * Customizable piece of the default paging text (defaults to "Refresh")
24520      * @type String
24521      */
24522     refreshText : "Refresh",
24523
24524     buttons : false,
24525     // private
24526     onRender : function(ct, position) 
24527     {
24528         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24529         this.navgroup.parentId = this.id;
24530         this.navgroup.onRender(this.el, null);
24531         // add the buttons to the navgroup
24532         
24533         if(this.displayInfo){
24534             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24535             this.displayEl = this.el.select('.x-paging-info', true).first();
24536 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24537 //            this.displayEl = navel.el.select('span',true).first();
24538         }
24539         
24540         var _this = this;
24541         
24542         if(this.buttons){
24543             Roo.each(_this.buttons, function(e){ // this might need to use render????
24544                Roo.factory(e).render(_this.el);
24545             });
24546         }
24547             
24548         Roo.each(_this.toolbarItems, function(e) {
24549             _this.navgroup.addItem(e);
24550         });
24551         
24552         
24553         this.first = this.navgroup.addItem({
24554             tooltip: this.firstText,
24555             cls: "prev",
24556             icon : 'fa fa-backward',
24557             disabled: true,
24558             preventDefault: true,
24559             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24560         });
24561         
24562         this.prev =  this.navgroup.addItem({
24563             tooltip: this.prevText,
24564             cls: "prev",
24565             icon : 'fa fa-step-backward',
24566             disabled: true,
24567             preventDefault: true,
24568             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24569         });
24570     //this.addSeparator();
24571         
24572         
24573         var field = this.navgroup.addItem( {
24574             tagtype : 'span',
24575             cls : 'x-paging-position',
24576             
24577             html : this.beforePageText  +
24578                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24579                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24580          } ); //?? escaped?
24581         
24582         this.field = field.el.select('input', true).first();
24583         this.field.on("keydown", this.onPagingKeydown, this);
24584         this.field.on("focus", function(){this.dom.select();});
24585     
24586     
24587         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24588         //this.field.setHeight(18);
24589         //this.addSeparator();
24590         this.next = this.navgroup.addItem({
24591             tooltip: this.nextText,
24592             cls: "next",
24593             html : ' <i class="fa fa-step-forward">',
24594             disabled: true,
24595             preventDefault: true,
24596             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24597         });
24598         this.last = this.navgroup.addItem({
24599             tooltip: this.lastText,
24600             icon : 'fa fa-forward',
24601             cls: "next",
24602             disabled: true,
24603             preventDefault: true,
24604             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24605         });
24606     //this.addSeparator();
24607         this.loading = this.navgroup.addItem({
24608             tooltip: this.refreshText,
24609             icon: 'fa fa-refresh',
24610             preventDefault: true,
24611             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24612         });
24613         
24614     },
24615
24616     // private
24617     updateInfo : function(){
24618         if(this.displayEl){
24619             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24620             var msg = count == 0 ?
24621                 this.emptyMsg :
24622                 String.format(
24623                     this.displayMsg,
24624                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24625                 );
24626             this.displayEl.update(msg);
24627         }
24628     },
24629
24630     // private
24631     onLoad : function(ds, r, o)
24632     {
24633         this.cursor = o.params.start ? o.params.start : 0;
24634         
24635         var d = this.getPageData(),
24636             ap = d.activePage,
24637             ps = d.pages;
24638         
24639         
24640         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24641         this.field.dom.value = ap;
24642         this.first.setDisabled(ap == 1);
24643         this.prev.setDisabled(ap == 1);
24644         this.next.setDisabled(ap == ps);
24645         this.last.setDisabled(ap == ps);
24646         this.loading.enable();
24647         this.updateInfo();
24648     },
24649
24650     // private
24651     getPageData : function(){
24652         var total = this.ds.getTotalCount();
24653         return {
24654             total : total,
24655             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24656             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24657         };
24658     },
24659
24660     // private
24661     onLoadError : function(){
24662         this.loading.enable();
24663     },
24664
24665     // private
24666     onPagingKeydown : function(e){
24667         var k = e.getKey();
24668         var d = this.getPageData();
24669         if(k == e.RETURN){
24670             var v = this.field.dom.value, pageNum;
24671             if(!v || isNaN(pageNum = parseInt(v, 10))){
24672                 this.field.dom.value = d.activePage;
24673                 return;
24674             }
24675             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24676             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24677             e.stopEvent();
24678         }
24679         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))
24680         {
24681           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24682           this.field.dom.value = pageNum;
24683           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24684           e.stopEvent();
24685         }
24686         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24687         {
24688           var v = this.field.dom.value, pageNum; 
24689           var increment = (e.shiftKey) ? 10 : 1;
24690           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24691                 increment *= -1;
24692           }
24693           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24694             this.field.dom.value = d.activePage;
24695             return;
24696           }
24697           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24698           {
24699             this.field.dom.value = parseInt(v, 10) + increment;
24700             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24701             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24702           }
24703           e.stopEvent();
24704         }
24705     },
24706
24707     // private
24708     beforeLoad : function(){
24709         if(this.loading){
24710             this.loading.disable();
24711         }
24712     },
24713
24714     // private
24715     onClick : function(which){
24716         
24717         var ds = this.ds;
24718         if (!ds) {
24719             return;
24720         }
24721         
24722         switch(which){
24723             case "first":
24724                 ds.load({params:{start: 0, limit: this.pageSize}});
24725             break;
24726             case "prev":
24727                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24728             break;
24729             case "next":
24730                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24731             break;
24732             case "last":
24733                 var total = ds.getTotalCount();
24734                 var extra = total % this.pageSize;
24735                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24736                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24737             break;
24738             case "refresh":
24739                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24740             break;
24741         }
24742     },
24743
24744     /**
24745      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24746      * @param {Roo.data.Store} store The data store to unbind
24747      */
24748     unbind : function(ds){
24749         ds.un("beforeload", this.beforeLoad, this);
24750         ds.un("load", this.onLoad, this);
24751         ds.un("loadexception", this.onLoadError, this);
24752         ds.un("remove", this.updateInfo, this);
24753         ds.un("add", this.updateInfo, this);
24754         this.ds = undefined;
24755     },
24756
24757     /**
24758      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24759      * @param {Roo.data.Store} store The data store to bind
24760      */
24761     bind : function(ds){
24762         ds.on("beforeload", this.beforeLoad, this);
24763         ds.on("load", this.onLoad, this);
24764         ds.on("loadexception", this.onLoadError, this);
24765         ds.on("remove", this.updateInfo, this);
24766         ds.on("add", this.updateInfo, this);
24767         this.ds = ds;
24768     }
24769 });/*
24770  * - LGPL
24771  *
24772  * element
24773  * 
24774  */
24775
24776 /**
24777  * @class Roo.bootstrap.MessageBar
24778  * @extends Roo.bootstrap.Component
24779  * Bootstrap MessageBar class
24780  * @cfg {String} html contents of the MessageBar
24781  * @cfg {String} weight (info | success | warning | danger) default info
24782  * @cfg {String} beforeClass insert the bar before the given class
24783  * @cfg {Boolean} closable (true | false) default false
24784  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24785  * 
24786  * @constructor
24787  * Create a new Element
24788  * @param {Object} config The config object
24789  */
24790
24791 Roo.bootstrap.MessageBar = function(config){
24792     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24793 };
24794
24795 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24796     
24797     html: '',
24798     weight: 'info',
24799     closable: false,
24800     fixed: false,
24801     beforeClass: 'bootstrap-sticky-wrap',
24802     
24803     getAutoCreate : function(){
24804         
24805         var cfg = {
24806             tag: 'div',
24807             cls: 'alert alert-dismissable alert-' + this.weight,
24808             cn: [
24809                 {
24810                     tag: 'span',
24811                     cls: 'message',
24812                     html: this.html || ''
24813                 }
24814             ]
24815         };
24816         
24817         if(this.fixed){
24818             cfg.cls += ' alert-messages-fixed';
24819         }
24820         
24821         if(this.closable){
24822             cfg.cn.push({
24823                 tag: 'button',
24824                 cls: 'close',
24825                 html: 'x'
24826             });
24827         }
24828         
24829         return cfg;
24830     },
24831     
24832     onRender : function(ct, position)
24833     {
24834         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24835         
24836         if(!this.el){
24837             var cfg = Roo.apply({},  this.getAutoCreate());
24838             cfg.id = Roo.id();
24839             
24840             if (this.cls) {
24841                 cfg.cls += ' ' + this.cls;
24842             }
24843             if (this.style) {
24844                 cfg.style = this.style;
24845             }
24846             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24847             
24848             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24849         }
24850         
24851         this.el.select('>button.close').on('click', this.hide, this);
24852         
24853     },
24854     
24855     show : function()
24856     {
24857         if (!this.rendered) {
24858             this.render();
24859         }
24860         
24861         this.el.show();
24862         
24863         this.fireEvent('show', this);
24864         
24865     },
24866     
24867     hide : function()
24868     {
24869         if (!this.rendered) {
24870             this.render();
24871         }
24872         
24873         this.el.hide();
24874         
24875         this.fireEvent('hide', this);
24876     },
24877     
24878     update : function()
24879     {
24880 //        var e = this.el.dom.firstChild;
24881 //        
24882 //        if(this.closable){
24883 //            e = e.nextSibling;
24884 //        }
24885 //        
24886 //        e.data = this.html || '';
24887
24888         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24889     }
24890    
24891 });
24892
24893  
24894
24895      /*
24896  * - LGPL
24897  *
24898  * Graph
24899  * 
24900  */
24901
24902
24903 /**
24904  * @class Roo.bootstrap.Graph
24905  * @extends Roo.bootstrap.Component
24906  * Bootstrap Graph class
24907 > Prameters
24908  -sm {number} sm 4
24909  -md {number} md 5
24910  @cfg {String} graphtype  bar | vbar | pie
24911  @cfg {number} g_x coodinator | centre x (pie)
24912  @cfg {number} g_y coodinator | centre y (pie)
24913  @cfg {number} g_r radius (pie)
24914  @cfg {number} g_height height of the chart (respected by all elements in the set)
24915  @cfg {number} g_width width of the chart (respected by all elements in the set)
24916  @cfg {Object} title The title of the chart
24917     
24918  -{Array}  values
24919  -opts (object) options for the chart 
24920      o {
24921      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24922      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24923      o vgutter (number)
24924      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.
24925      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24926      o to
24927      o stretch (boolean)
24928      o }
24929  -opts (object) options for the pie
24930      o{
24931      o cut
24932      o startAngle (number)
24933      o endAngle (number)
24934      } 
24935  *
24936  * @constructor
24937  * Create a new Input
24938  * @param {Object} config The config object
24939  */
24940
24941 Roo.bootstrap.Graph = function(config){
24942     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24943     
24944     this.addEvents({
24945         // img events
24946         /**
24947          * @event click
24948          * The img click event for the img.
24949          * @param {Roo.EventObject} e
24950          */
24951         "click" : true
24952     });
24953 };
24954
24955 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24956     
24957     sm: 4,
24958     md: 5,
24959     graphtype: 'bar',
24960     g_height: 250,
24961     g_width: 400,
24962     g_x: 50,
24963     g_y: 50,
24964     g_r: 30,
24965     opts:{
24966         //g_colors: this.colors,
24967         g_type: 'soft',
24968         g_gutter: '20%'
24969
24970     },
24971     title : false,
24972
24973     getAutoCreate : function(){
24974         
24975         var cfg = {
24976             tag: 'div',
24977             html : null
24978         };
24979         
24980         
24981         return  cfg;
24982     },
24983
24984     onRender : function(ct,position){
24985         
24986         
24987         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24988         
24989         if (typeof(Raphael) == 'undefined') {
24990             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24991             return;
24992         }
24993         
24994         this.raphael = Raphael(this.el.dom);
24995         
24996                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24997                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24998                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24999                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25000                 /*
25001                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25002                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25003                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25004                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25005                 
25006                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25007                 r.barchart(330, 10, 300, 220, data1);
25008                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25009                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25010                 */
25011                 
25012                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25013                 // r.barchart(30, 30, 560, 250,  xdata, {
25014                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25015                 //     axis : "0 0 1 1",
25016                 //     axisxlabels :  xdata
25017                 //     //yvalues : cols,
25018                    
25019                 // });
25020 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25021 //        
25022 //        this.load(null,xdata,{
25023 //                axis : "0 0 1 1",
25024 //                axisxlabels :  xdata
25025 //                });
25026
25027     },
25028
25029     load : function(graphtype,xdata,opts)
25030     {
25031         this.raphael.clear();
25032         if(!graphtype) {
25033             graphtype = this.graphtype;
25034         }
25035         if(!opts){
25036             opts = this.opts;
25037         }
25038         var r = this.raphael,
25039             fin = function () {
25040                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25041             },
25042             fout = function () {
25043                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25044             },
25045             pfin = function() {
25046                 this.sector.stop();
25047                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25048
25049                 if (this.label) {
25050                     this.label[0].stop();
25051                     this.label[0].attr({ r: 7.5 });
25052                     this.label[1].attr({ "font-weight": 800 });
25053                 }
25054             },
25055             pfout = function() {
25056                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25057
25058                 if (this.label) {
25059                     this.label[0].animate({ r: 5 }, 500, "bounce");
25060                     this.label[1].attr({ "font-weight": 400 });
25061                 }
25062             };
25063
25064         switch(graphtype){
25065             case 'bar':
25066                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25067                 break;
25068             case 'hbar':
25069                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25070                 break;
25071             case 'pie':
25072 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25073 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25074 //            
25075                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25076                 
25077                 break;
25078
25079         }
25080         
25081         if(this.title){
25082             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25083         }
25084         
25085     },
25086     
25087     setTitle: function(o)
25088     {
25089         this.title = o;
25090     },
25091     
25092     initEvents: function() {
25093         
25094         if(!this.href){
25095             this.el.on('click', this.onClick, this);
25096         }
25097     },
25098     
25099     onClick : function(e)
25100     {
25101         Roo.log('img onclick');
25102         this.fireEvent('click', this, e);
25103     }
25104    
25105 });
25106
25107  
25108 /*
25109  * - LGPL
25110  *
25111  * numberBox
25112  * 
25113  */
25114 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25115
25116 /**
25117  * @class Roo.bootstrap.dash.NumberBox
25118  * @extends Roo.bootstrap.Component
25119  * Bootstrap NumberBox class
25120  * @cfg {String} headline Box headline
25121  * @cfg {String} content Box content
25122  * @cfg {String} icon Box icon
25123  * @cfg {String} footer Footer text
25124  * @cfg {String} fhref Footer href
25125  * 
25126  * @constructor
25127  * Create a new NumberBox
25128  * @param {Object} config The config object
25129  */
25130
25131
25132 Roo.bootstrap.dash.NumberBox = function(config){
25133     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25134     
25135 };
25136
25137 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25138     
25139     headline : '',
25140     content : '',
25141     icon : '',
25142     footer : '',
25143     fhref : '',
25144     ficon : '',
25145     
25146     getAutoCreate : function(){
25147         
25148         var cfg = {
25149             tag : 'div',
25150             cls : 'small-box ',
25151             cn : [
25152                 {
25153                     tag : 'div',
25154                     cls : 'inner',
25155                     cn :[
25156                         {
25157                             tag : 'h3',
25158                             cls : 'roo-headline',
25159                             html : this.headline
25160                         },
25161                         {
25162                             tag : 'p',
25163                             cls : 'roo-content',
25164                             html : this.content
25165                         }
25166                     ]
25167                 }
25168             ]
25169         };
25170         
25171         if(this.icon){
25172             cfg.cn.push({
25173                 tag : 'div',
25174                 cls : 'icon',
25175                 cn :[
25176                     {
25177                         tag : 'i',
25178                         cls : 'ion ' + this.icon
25179                     }
25180                 ]
25181             });
25182         }
25183         
25184         if(this.footer){
25185             var footer = {
25186                 tag : 'a',
25187                 cls : 'small-box-footer',
25188                 href : this.fhref || '#',
25189                 html : this.footer
25190             };
25191             
25192             cfg.cn.push(footer);
25193             
25194         }
25195         
25196         return  cfg;
25197     },
25198
25199     onRender : function(ct,position){
25200         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25201
25202
25203        
25204                 
25205     },
25206
25207     setHeadline: function (value)
25208     {
25209         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25210     },
25211     
25212     setFooter: function (value, href)
25213     {
25214         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25215         
25216         if(href){
25217             this.el.select('a.small-box-footer',true).first().attr('href', href);
25218         }
25219         
25220     },
25221
25222     setContent: function (value)
25223     {
25224         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25225     },
25226
25227     initEvents: function() 
25228     {   
25229         
25230     }
25231     
25232 });
25233
25234  
25235 /*
25236  * - LGPL
25237  *
25238  * TabBox
25239  * 
25240  */
25241 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25242
25243 /**
25244  * @class Roo.bootstrap.dash.TabBox
25245  * @extends Roo.bootstrap.Component
25246  * Bootstrap TabBox class
25247  * @cfg {String} title Title of the TabBox
25248  * @cfg {String} icon Icon of the TabBox
25249  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25250  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25251  * 
25252  * @constructor
25253  * Create a new TabBox
25254  * @param {Object} config The config object
25255  */
25256
25257
25258 Roo.bootstrap.dash.TabBox = function(config){
25259     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25260     this.addEvents({
25261         // raw events
25262         /**
25263          * @event addpane
25264          * When a pane is added
25265          * @param {Roo.bootstrap.dash.TabPane} pane
25266          */
25267         "addpane" : true,
25268         /**
25269          * @event activatepane
25270          * When a pane is activated
25271          * @param {Roo.bootstrap.dash.TabPane} pane
25272          */
25273         "activatepane" : true
25274         
25275          
25276     });
25277     
25278     this.panes = [];
25279 };
25280
25281 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25282
25283     title : '',
25284     icon : false,
25285     showtabs : true,
25286     tabScrollable : false,
25287     
25288     getChildContainer : function()
25289     {
25290         return this.el.select('.tab-content', true).first();
25291     },
25292     
25293     getAutoCreate : function(){
25294         
25295         var header = {
25296             tag: 'li',
25297             cls: 'pull-left header',
25298             html: this.title,
25299             cn : []
25300         };
25301         
25302         if(this.icon){
25303             header.cn.push({
25304                 tag: 'i',
25305                 cls: 'fa ' + this.icon
25306             });
25307         }
25308         
25309         var h = {
25310             tag: 'ul',
25311             cls: 'nav nav-tabs pull-right',
25312             cn: [
25313                 header
25314             ]
25315         };
25316         
25317         if(this.tabScrollable){
25318             h = {
25319                 tag: 'div',
25320                 cls: 'tab-header',
25321                 cn: [
25322                     {
25323                         tag: 'ul',
25324                         cls: 'nav nav-tabs pull-right',
25325                         cn: [
25326                             header
25327                         ]
25328                     }
25329                 ]
25330             };
25331         }
25332         
25333         var cfg = {
25334             tag: 'div',
25335             cls: 'nav-tabs-custom',
25336             cn: [
25337                 h,
25338                 {
25339                     tag: 'div',
25340                     cls: 'tab-content no-padding',
25341                     cn: []
25342                 }
25343             ]
25344         };
25345
25346         return  cfg;
25347     },
25348     initEvents : function()
25349     {
25350         //Roo.log('add add pane handler');
25351         this.on('addpane', this.onAddPane, this);
25352     },
25353      /**
25354      * Updates the box title
25355      * @param {String} html to set the title to.
25356      */
25357     setTitle : function(value)
25358     {
25359         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25360     },
25361     onAddPane : function(pane)
25362     {
25363         this.panes.push(pane);
25364         //Roo.log('addpane');
25365         //Roo.log(pane);
25366         // tabs are rendere left to right..
25367         if(!this.showtabs){
25368             return;
25369         }
25370         
25371         var ctr = this.el.select('.nav-tabs', true).first();
25372          
25373          
25374         var existing = ctr.select('.nav-tab',true);
25375         var qty = existing.getCount();;
25376         
25377         
25378         var tab = ctr.createChild({
25379             tag : 'li',
25380             cls : 'nav-tab' + (qty ? '' : ' active'),
25381             cn : [
25382                 {
25383                     tag : 'a',
25384                     href:'#',
25385                     html : pane.title
25386                 }
25387             ]
25388         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25389         pane.tab = tab;
25390         
25391         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25392         if (!qty) {
25393             pane.el.addClass('active');
25394         }
25395         
25396                 
25397     },
25398     onTabClick : function(ev,un,ob,pane)
25399     {
25400         //Roo.log('tab - prev default');
25401         ev.preventDefault();
25402         
25403         
25404         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25405         pane.tab.addClass('active');
25406         //Roo.log(pane.title);
25407         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25408         // technically we should have a deactivate event.. but maybe add later.
25409         // and it should not de-activate the selected tab...
25410         this.fireEvent('activatepane', pane);
25411         pane.el.addClass('active');
25412         pane.fireEvent('activate');
25413         
25414         
25415     },
25416     
25417     getActivePane : function()
25418     {
25419         var r = false;
25420         Roo.each(this.panes, function(p) {
25421             if(p.el.hasClass('active')){
25422                 r = p;
25423                 return false;
25424             }
25425             
25426             return;
25427         });
25428         
25429         return r;
25430     }
25431     
25432     
25433 });
25434
25435  
25436 /*
25437  * - LGPL
25438  *
25439  * Tab pane
25440  * 
25441  */
25442 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25443 /**
25444  * @class Roo.bootstrap.TabPane
25445  * @extends Roo.bootstrap.Component
25446  * Bootstrap TabPane class
25447  * @cfg {Boolean} active (false | true) Default false
25448  * @cfg {String} title title of panel
25449
25450  * 
25451  * @constructor
25452  * Create a new TabPane
25453  * @param {Object} config The config object
25454  */
25455
25456 Roo.bootstrap.dash.TabPane = function(config){
25457     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25458     
25459     this.addEvents({
25460         // raw events
25461         /**
25462          * @event activate
25463          * When a pane is activated
25464          * @param {Roo.bootstrap.dash.TabPane} pane
25465          */
25466         "activate" : true
25467          
25468     });
25469 };
25470
25471 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25472     
25473     active : false,
25474     title : '',
25475     
25476     // the tabBox that this is attached to.
25477     tab : false,
25478      
25479     getAutoCreate : function() 
25480     {
25481         var cfg = {
25482             tag: 'div',
25483             cls: 'tab-pane'
25484         };
25485         
25486         if(this.active){
25487             cfg.cls += ' active';
25488         }
25489         
25490         return cfg;
25491     },
25492     initEvents  : function()
25493     {
25494         //Roo.log('trigger add pane handler');
25495         this.parent().fireEvent('addpane', this)
25496     },
25497     
25498      /**
25499      * Updates the tab title 
25500      * @param {String} html to set the title to.
25501      */
25502     setTitle: function(str)
25503     {
25504         if (!this.tab) {
25505             return;
25506         }
25507         this.title = str;
25508         this.tab.select('a', true).first().dom.innerHTML = str;
25509         
25510     }
25511     
25512     
25513     
25514 });
25515
25516  
25517
25518
25519  /*
25520  * - LGPL
25521  *
25522  * menu
25523  * 
25524  */
25525 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25526
25527 /**
25528  * @class Roo.bootstrap.menu.Menu
25529  * @extends Roo.bootstrap.Component
25530  * Bootstrap Menu class - container for Menu
25531  * @cfg {String} html Text of the menu
25532  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25533  * @cfg {String} icon Font awesome icon
25534  * @cfg {String} pos Menu align to (top | bottom) default bottom
25535  * 
25536  * 
25537  * @constructor
25538  * Create a new Menu
25539  * @param {Object} config The config object
25540  */
25541
25542
25543 Roo.bootstrap.menu.Menu = function(config){
25544     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25545     
25546     this.addEvents({
25547         /**
25548          * @event beforeshow
25549          * Fires before this menu is displayed
25550          * @param {Roo.bootstrap.menu.Menu} this
25551          */
25552         beforeshow : true,
25553         /**
25554          * @event beforehide
25555          * Fires before this menu is hidden
25556          * @param {Roo.bootstrap.menu.Menu} this
25557          */
25558         beforehide : true,
25559         /**
25560          * @event show
25561          * Fires after this menu is displayed
25562          * @param {Roo.bootstrap.menu.Menu} this
25563          */
25564         show : true,
25565         /**
25566          * @event hide
25567          * Fires after this menu is hidden
25568          * @param {Roo.bootstrap.menu.Menu} this
25569          */
25570         hide : true,
25571         /**
25572          * @event click
25573          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25574          * @param {Roo.bootstrap.menu.Menu} this
25575          * @param {Roo.EventObject} e
25576          */
25577         click : true
25578     });
25579     
25580 };
25581
25582 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25583     
25584     submenu : false,
25585     html : '',
25586     weight : 'default',
25587     icon : false,
25588     pos : 'bottom',
25589     
25590     
25591     getChildContainer : function() {
25592         if(this.isSubMenu){
25593             return this.el;
25594         }
25595         
25596         return this.el.select('ul.dropdown-menu', true).first();  
25597     },
25598     
25599     getAutoCreate : function()
25600     {
25601         var text = [
25602             {
25603                 tag : 'span',
25604                 cls : 'roo-menu-text',
25605                 html : this.html
25606             }
25607         ];
25608         
25609         if(this.icon){
25610             text.unshift({
25611                 tag : 'i',
25612                 cls : 'fa ' + this.icon
25613             })
25614         }
25615         
25616         
25617         var cfg = {
25618             tag : 'div',
25619             cls : 'btn-group',
25620             cn : [
25621                 {
25622                     tag : 'button',
25623                     cls : 'dropdown-button btn btn-' + this.weight,
25624                     cn : text
25625                 },
25626                 {
25627                     tag : 'button',
25628                     cls : 'dropdown-toggle btn btn-' + this.weight,
25629                     cn : [
25630                         {
25631                             tag : 'span',
25632                             cls : 'caret'
25633                         }
25634                     ]
25635                 },
25636                 {
25637                     tag : 'ul',
25638                     cls : 'dropdown-menu'
25639                 }
25640             ]
25641             
25642         };
25643         
25644         if(this.pos == 'top'){
25645             cfg.cls += ' dropup';
25646         }
25647         
25648         if(this.isSubMenu){
25649             cfg = {
25650                 tag : 'ul',
25651                 cls : 'dropdown-menu'
25652             }
25653         }
25654         
25655         return cfg;
25656     },
25657     
25658     onRender : function(ct, position)
25659     {
25660         this.isSubMenu = ct.hasClass('dropdown-submenu');
25661         
25662         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25663     },
25664     
25665     initEvents : function() 
25666     {
25667         if(this.isSubMenu){
25668             return;
25669         }
25670         
25671         this.hidden = true;
25672         
25673         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25674         this.triggerEl.on('click', this.onTriggerPress, this);
25675         
25676         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25677         this.buttonEl.on('click', this.onClick, this);
25678         
25679     },
25680     
25681     list : function()
25682     {
25683         if(this.isSubMenu){
25684             return this.el;
25685         }
25686         
25687         return this.el.select('ul.dropdown-menu', true).first();
25688     },
25689     
25690     onClick : function(e)
25691     {
25692         this.fireEvent("click", this, e);
25693     },
25694     
25695     onTriggerPress  : function(e)
25696     {   
25697         if (this.isVisible()) {
25698             this.hide();
25699         } else {
25700             this.show();
25701         }
25702     },
25703     
25704     isVisible : function(){
25705         return !this.hidden;
25706     },
25707     
25708     show : function()
25709     {
25710         this.fireEvent("beforeshow", this);
25711         
25712         this.hidden = false;
25713         this.el.addClass('open');
25714         
25715         Roo.get(document).on("mouseup", this.onMouseUp, this);
25716         
25717         this.fireEvent("show", this);
25718         
25719         
25720     },
25721     
25722     hide : function()
25723     {
25724         this.fireEvent("beforehide", this);
25725         
25726         this.hidden = true;
25727         this.el.removeClass('open');
25728         
25729         Roo.get(document).un("mouseup", this.onMouseUp);
25730         
25731         this.fireEvent("hide", this);
25732     },
25733     
25734     onMouseUp : function()
25735     {
25736         this.hide();
25737     }
25738     
25739 });
25740
25741  
25742  /*
25743  * - LGPL
25744  *
25745  * menu item
25746  * 
25747  */
25748 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25749
25750 /**
25751  * @class Roo.bootstrap.menu.Item
25752  * @extends Roo.bootstrap.Component
25753  * Bootstrap MenuItem class
25754  * @cfg {Boolean} submenu (true | false) default false
25755  * @cfg {String} html text of the item
25756  * @cfg {String} href the link
25757  * @cfg {Boolean} disable (true | false) default false
25758  * @cfg {Boolean} preventDefault (true | false) default true
25759  * @cfg {String} icon Font awesome icon
25760  * @cfg {String} pos Submenu align to (left | right) default right 
25761  * 
25762  * 
25763  * @constructor
25764  * Create a new Item
25765  * @param {Object} config The config object
25766  */
25767
25768
25769 Roo.bootstrap.menu.Item = function(config){
25770     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25771     this.addEvents({
25772         /**
25773          * @event mouseover
25774          * Fires when the mouse is hovering over this menu
25775          * @param {Roo.bootstrap.menu.Item} this
25776          * @param {Roo.EventObject} e
25777          */
25778         mouseover : true,
25779         /**
25780          * @event mouseout
25781          * Fires when the mouse exits this menu
25782          * @param {Roo.bootstrap.menu.Item} this
25783          * @param {Roo.EventObject} e
25784          */
25785         mouseout : true,
25786         // raw events
25787         /**
25788          * @event click
25789          * The raw click event for the entire grid.
25790          * @param {Roo.EventObject} e
25791          */
25792         click : true
25793     });
25794 };
25795
25796 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25797     
25798     submenu : false,
25799     href : '',
25800     html : '',
25801     preventDefault: true,
25802     disable : false,
25803     icon : false,
25804     pos : 'right',
25805     
25806     getAutoCreate : function()
25807     {
25808         var text = [
25809             {
25810                 tag : 'span',
25811                 cls : 'roo-menu-item-text',
25812                 html : this.html
25813             }
25814         ];
25815         
25816         if(this.icon){
25817             text.unshift({
25818                 tag : 'i',
25819                 cls : 'fa ' + this.icon
25820             })
25821         }
25822         
25823         var cfg = {
25824             tag : 'li',
25825             cn : [
25826                 {
25827                     tag : 'a',
25828                     href : this.href || '#',
25829                     cn : text
25830                 }
25831             ]
25832         };
25833         
25834         if(this.disable){
25835             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25836         }
25837         
25838         if(this.submenu){
25839             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25840             
25841             if(this.pos == 'left'){
25842                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25843             }
25844         }
25845         
25846         return cfg;
25847     },
25848     
25849     initEvents : function() 
25850     {
25851         this.el.on('mouseover', this.onMouseOver, this);
25852         this.el.on('mouseout', this.onMouseOut, this);
25853         
25854         this.el.select('a', true).first().on('click', this.onClick, this);
25855         
25856     },
25857     
25858     onClick : function(e)
25859     {
25860         if(this.preventDefault){
25861             e.preventDefault();
25862         }
25863         
25864         this.fireEvent("click", this, e);
25865     },
25866     
25867     onMouseOver : function(e)
25868     {
25869         if(this.submenu && this.pos == 'left'){
25870             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25871         }
25872         
25873         this.fireEvent("mouseover", this, e);
25874     },
25875     
25876     onMouseOut : function(e)
25877     {
25878         this.fireEvent("mouseout", this, e);
25879     }
25880 });
25881
25882  
25883
25884  /*
25885  * - LGPL
25886  *
25887  * menu separator
25888  * 
25889  */
25890 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25891
25892 /**
25893  * @class Roo.bootstrap.menu.Separator
25894  * @extends Roo.bootstrap.Component
25895  * Bootstrap Separator class
25896  * 
25897  * @constructor
25898  * Create a new Separator
25899  * @param {Object} config The config object
25900  */
25901
25902
25903 Roo.bootstrap.menu.Separator = function(config){
25904     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25905 };
25906
25907 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25908     
25909     getAutoCreate : function(){
25910         var cfg = {
25911             tag : 'li',
25912             cls: 'divider'
25913         };
25914         
25915         return cfg;
25916     }
25917    
25918 });
25919
25920  
25921
25922  /*
25923  * - LGPL
25924  *
25925  * Tooltip
25926  * 
25927  */
25928
25929 /**
25930  * @class Roo.bootstrap.Tooltip
25931  * Bootstrap Tooltip class
25932  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25933  * to determine which dom element triggers the tooltip.
25934  * 
25935  * It needs to add support for additional attributes like tooltip-position
25936  * 
25937  * @constructor
25938  * Create a new Toolti
25939  * @param {Object} config The config object
25940  */
25941
25942 Roo.bootstrap.Tooltip = function(config){
25943     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25944     
25945     this.alignment = Roo.bootstrap.Tooltip.alignment;
25946     
25947     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25948         this.alignment = config.alignment;
25949     }
25950     
25951 };
25952
25953 Roo.apply(Roo.bootstrap.Tooltip, {
25954     /**
25955      * @function init initialize tooltip monitoring.
25956      * @static
25957      */
25958     currentEl : false,
25959     currentTip : false,
25960     currentRegion : false,
25961     
25962     //  init : delay?
25963     
25964     init : function()
25965     {
25966         Roo.get(document).on('mouseover', this.enter ,this);
25967         Roo.get(document).on('mouseout', this.leave, this);
25968          
25969         
25970         this.currentTip = new Roo.bootstrap.Tooltip();
25971     },
25972     
25973     enter : function(ev)
25974     {
25975         var dom = ev.getTarget();
25976         
25977         //Roo.log(['enter',dom]);
25978         var el = Roo.fly(dom);
25979         if (this.currentEl) {
25980             //Roo.log(dom);
25981             //Roo.log(this.currentEl);
25982             //Roo.log(this.currentEl.contains(dom));
25983             if (this.currentEl == el) {
25984                 return;
25985             }
25986             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25987                 return;
25988             }
25989
25990         }
25991         
25992         if (this.currentTip.el) {
25993             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25994         }    
25995         //Roo.log(ev);
25996         
25997         if(!el || el.dom == document){
25998             return;
25999         }
26000         
26001         var bindEl = el;
26002         
26003         // you can not look for children, as if el is the body.. then everythign is the child..
26004         if (!el.attr('tooltip')) { //
26005             if (!el.select("[tooltip]").elements.length) {
26006                 return;
26007             }
26008             // is the mouse over this child...?
26009             bindEl = el.select("[tooltip]").first();
26010             var xy = ev.getXY();
26011             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26012                 //Roo.log("not in region.");
26013                 return;
26014             }
26015             //Roo.log("child element over..");
26016             
26017         }
26018         this.currentEl = bindEl;
26019         this.currentTip.bind(bindEl);
26020         this.currentRegion = Roo.lib.Region.getRegion(dom);
26021         this.currentTip.enter();
26022         
26023     },
26024     leave : function(ev)
26025     {
26026         var dom = ev.getTarget();
26027         //Roo.log(['leave',dom]);
26028         if (!this.currentEl) {
26029             return;
26030         }
26031         
26032         
26033         if (dom != this.currentEl.dom) {
26034             return;
26035         }
26036         var xy = ev.getXY();
26037         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26038             return;
26039         }
26040         // only activate leave if mouse cursor is outside... bounding box..
26041         
26042         
26043         
26044         
26045         if (this.currentTip) {
26046             this.currentTip.leave();
26047         }
26048         //Roo.log('clear currentEl');
26049         this.currentEl = false;
26050         
26051         
26052     },
26053     alignment : {
26054         'left' : ['r-l', [-2,0], 'right'],
26055         'right' : ['l-r', [2,0], 'left'],
26056         'bottom' : ['t-b', [0,2], 'top'],
26057         'top' : [ 'b-t', [0,-2], 'bottom']
26058     }
26059     
26060 });
26061
26062
26063 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26064     
26065     
26066     bindEl : false,
26067     
26068     delay : null, // can be { show : 300 , hide: 500}
26069     
26070     timeout : null,
26071     
26072     hoverState : null, //???
26073     
26074     placement : 'bottom', 
26075     
26076     alignment : false,
26077     
26078     getAutoCreate : function(){
26079     
26080         var cfg = {
26081            cls : 'tooltip',
26082            role : 'tooltip',
26083            cn : [
26084                 {
26085                     cls : 'tooltip-arrow'
26086                 },
26087                 {
26088                     cls : 'tooltip-inner'
26089                 }
26090            ]
26091         };
26092         
26093         return cfg;
26094     },
26095     bind : function(el)
26096     {
26097         this.bindEl = el;
26098     },
26099       
26100     
26101     enter : function () {
26102        
26103         if (this.timeout != null) {
26104             clearTimeout(this.timeout);
26105         }
26106         
26107         this.hoverState = 'in';
26108          //Roo.log("enter - show");
26109         if (!this.delay || !this.delay.show) {
26110             this.show();
26111             return;
26112         }
26113         var _t = this;
26114         this.timeout = setTimeout(function () {
26115             if (_t.hoverState == 'in') {
26116                 _t.show();
26117             }
26118         }, this.delay.show);
26119     },
26120     leave : function()
26121     {
26122         clearTimeout(this.timeout);
26123     
26124         this.hoverState = 'out';
26125          if (!this.delay || !this.delay.hide) {
26126             this.hide();
26127             return;
26128         }
26129        
26130         var _t = this;
26131         this.timeout = setTimeout(function () {
26132             //Roo.log("leave - timeout");
26133             
26134             if (_t.hoverState == 'out') {
26135                 _t.hide();
26136                 Roo.bootstrap.Tooltip.currentEl = false;
26137             }
26138         }, delay);
26139     },
26140     
26141     show : function (msg)
26142     {
26143         if (!this.el) {
26144             this.render(document.body);
26145         }
26146         // set content.
26147         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26148         
26149         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26150         
26151         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26152         
26153         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26154         
26155         var placement = typeof this.placement == 'function' ?
26156             this.placement.call(this, this.el, on_el) :
26157             this.placement;
26158             
26159         var autoToken = /\s?auto?\s?/i;
26160         var autoPlace = autoToken.test(placement);
26161         if (autoPlace) {
26162             placement = placement.replace(autoToken, '') || 'top';
26163         }
26164         
26165         //this.el.detach()
26166         //this.el.setXY([0,0]);
26167         this.el.show();
26168         //this.el.dom.style.display='block';
26169         
26170         //this.el.appendTo(on_el);
26171         
26172         var p = this.getPosition();
26173         var box = this.el.getBox();
26174         
26175         if (autoPlace) {
26176             // fixme..
26177         }
26178         
26179         var align = this.alignment[placement];
26180         
26181         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26182         
26183         if(placement == 'top' || placement == 'bottom'){
26184             if(xy[0] < 0){
26185                 placement = 'right';
26186             }
26187             
26188             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26189                 placement = 'left';
26190             }
26191             
26192             var scroll = Roo.select('body', true).first().getScroll();
26193             
26194             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26195                 placement = 'top';
26196             }
26197             
26198         }
26199         
26200         this.el.alignTo(this.bindEl, align[0],align[1]);
26201         //var arrow = this.el.select('.arrow',true).first();
26202         //arrow.set(align[2], 
26203         
26204         this.el.addClass(placement);
26205         
26206         this.el.addClass('in fade');
26207         
26208         this.hoverState = null;
26209         
26210         if (this.el.hasClass('fade')) {
26211             // fade it?
26212         }
26213         
26214     },
26215     hide : function()
26216     {
26217          
26218         if (!this.el) {
26219             return;
26220         }
26221         //this.el.setXY([0,0]);
26222         this.el.removeClass('in');
26223         //this.el.hide();
26224         
26225     }
26226     
26227 });
26228  
26229
26230  /*
26231  * - LGPL
26232  *
26233  * Location Picker
26234  * 
26235  */
26236
26237 /**
26238  * @class Roo.bootstrap.LocationPicker
26239  * @extends Roo.bootstrap.Component
26240  * Bootstrap LocationPicker class
26241  * @cfg {Number} latitude Position when init default 0
26242  * @cfg {Number} longitude Position when init default 0
26243  * @cfg {Number} zoom default 15
26244  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26245  * @cfg {Boolean} mapTypeControl default false
26246  * @cfg {Boolean} disableDoubleClickZoom default false
26247  * @cfg {Boolean} scrollwheel default true
26248  * @cfg {Boolean} streetViewControl default false
26249  * @cfg {Number} radius default 0
26250  * @cfg {String} locationName
26251  * @cfg {Boolean} draggable default true
26252  * @cfg {Boolean} enableAutocomplete default false
26253  * @cfg {Boolean} enableReverseGeocode default true
26254  * @cfg {String} markerTitle
26255  * 
26256  * @constructor
26257  * Create a new LocationPicker
26258  * @param {Object} config The config object
26259  */
26260
26261
26262 Roo.bootstrap.LocationPicker = function(config){
26263     
26264     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26265     
26266     this.addEvents({
26267         /**
26268          * @event initial
26269          * Fires when the picker initialized.
26270          * @param {Roo.bootstrap.LocationPicker} this
26271          * @param {Google Location} location
26272          */
26273         initial : true,
26274         /**
26275          * @event positionchanged
26276          * Fires when the picker position changed.
26277          * @param {Roo.bootstrap.LocationPicker} this
26278          * @param {Google Location} location
26279          */
26280         positionchanged : true,
26281         /**
26282          * @event resize
26283          * Fires when the map resize.
26284          * @param {Roo.bootstrap.LocationPicker} this
26285          */
26286         resize : true,
26287         /**
26288          * @event show
26289          * Fires when the map show.
26290          * @param {Roo.bootstrap.LocationPicker} this
26291          */
26292         show : true,
26293         /**
26294          * @event hide
26295          * Fires when the map hide.
26296          * @param {Roo.bootstrap.LocationPicker} this
26297          */
26298         hide : true,
26299         /**
26300          * @event mapClick
26301          * Fires when click the map.
26302          * @param {Roo.bootstrap.LocationPicker} this
26303          * @param {Map event} e
26304          */
26305         mapClick : true,
26306         /**
26307          * @event mapRightClick
26308          * Fires when right click the map.
26309          * @param {Roo.bootstrap.LocationPicker} this
26310          * @param {Map event} e
26311          */
26312         mapRightClick : true,
26313         /**
26314          * @event markerClick
26315          * Fires when click the marker.
26316          * @param {Roo.bootstrap.LocationPicker} this
26317          * @param {Map event} e
26318          */
26319         markerClick : true,
26320         /**
26321          * @event markerRightClick
26322          * Fires when right click the marker.
26323          * @param {Roo.bootstrap.LocationPicker} this
26324          * @param {Map event} e
26325          */
26326         markerRightClick : true,
26327         /**
26328          * @event OverlayViewDraw
26329          * Fires when OverlayView Draw
26330          * @param {Roo.bootstrap.LocationPicker} this
26331          */
26332         OverlayViewDraw : true,
26333         /**
26334          * @event OverlayViewOnAdd
26335          * Fires when OverlayView Draw
26336          * @param {Roo.bootstrap.LocationPicker} this
26337          */
26338         OverlayViewOnAdd : true,
26339         /**
26340          * @event OverlayViewOnRemove
26341          * Fires when OverlayView Draw
26342          * @param {Roo.bootstrap.LocationPicker} this
26343          */
26344         OverlayViewOnRemove : true,
26345         /**
26346          * @event OverlayViewShow
26347          * Fires when OverlayView Draw
26348          * @param {Roo.bootstrap.LocationPicker} this
26349          * @param {Pixel} cpx
26350          */
26351         OverlayViewShow : true,
26352         /**
26353          * @event OverlayViewHide
26354          * Fires when OverlayView Draw
26355          * @param {Roo.bootstrap.LocationPicker} this
26356          */
26357         OverlayViewHide : true,
26358         /**
26359          * @event loadexception
26360          * Fires when load google lib failed.
26361          * @param {Roo.bootstrap.LocationPicker} this
26362          */
26363         loadexception : true
26364     });
26365         
26366 };
26367
26368 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26369     
26370     gMapContext: false,
26371     
26372     latitude: 0,
26373     longitude: 0,
26374     zoom: 15,
26375     mapTypeId: false,
26376     mapTypeControl: false,
26377     disableDoubleClickZoom: false,
26378     scrollwheel: true,
26379     streetViewControl: false,
26380     radius: 0,
26381     locationName: '',
26382     draggable: true,
26383     enableAutocomplete: false,
26384     enableReverseGeocode: true,
26385     markerTitle: '',
26386     
26387     getAutoCreate: function()
26388     {
26389
26390         var cfg = {
26391             tag: 'div',
26392             cls: 'roo-location-picker'
26393         };
26394         
26395         return cfg
26396     },
26397     
26398     initEvents: function(ct, position)
26399     {       
26400         if(!this.el.getWidth() || this.isApplied()){
26401             return;
26402         }
26403         
26404         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26405         
26406         this.initial();
26407     },
26408     
26409     initial: function()
26410     {
26411         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26412             this.fireEvent('loadexception', this);
26413             return;
26414         }
26415         
26416         if(!this.mapTypeId){
26417             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26418         }
26419         
26420         this.gMapContext = this.GMapContext();
26421         
26422         this.initOverlayView();
26423         
26424         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26425         
26426         var _this = this;
26427                 
26428         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26429             _this.setPosition(_this.gMapContext.marker.position);
26430         });
26431         
26432         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26433             _this.fireEvent('mapClick', this, event);
26434             
26435         });
26436
26437         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26438             _this.fireEvent('mapRightClick', this, event);
26439             
26440         });
26441         
26442         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26443             _this.fireEvent('markerClick', this, event);
26444             
26445         });
26446
26447         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26448             _this.fireEvent('markerRightClick', this, event);
26449             
26450         });
26451         
26452         this.setPosition(this.gMapContext.location);
26453         
26454         this.fireEvent('initial', this, this.gMapContext.location);
26455     },
26456     
26457     initOverlayView: function()
26458     {
26459         var _this = this;
26460         
26461         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26462             
26463             draw: function()
26464             {
26465                 _this.fireEvent('OverlayViewDraw', _this);
26466             },
26467             
26468             onAdd: function()
26469             {
26470                 _this.fireEvent('OverlayViewOnAdd', _this);
26471             },
26472             
26473             onRemove: function()
26474             {
26475                 _this.fireEvent('OverlayViewOnRemove', _this);
26476             },
26477             
26478             show: function(cpx)
26479             {
26480                 _this.fireEvent('OverlayViewShow', _this, cpx);
26481             },
26482             
26483             hide: function()
26484             {
26485                 _this.fireEvent('OverlayViewHide', _this);
26486             }
26487             
26488         });
26489     },
26490     
26491     fromLatLngToContainerPixel: function(event)
26492     {
26493         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26494     },
26495     
26496     isApplied: function() 
26497     {
26498         return this.getGmapContext() == false ? false : true;
26499     },
26500     
26501     getGmapContext: function() 
26502     {
26503         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26504     },
26505     
26506     GMapContext: function() 
26507     {
26508         var position = new google.maps.LatLng(this.latitude, this.longitude);
26509         
26510         var _map = new google.maps.Map(this.el.dom, {
26511             center: position,
26512             zoom: this.zoom,
26513             mapTypeId: this.mapTypeId,
26514             mapTypeControl: this.mapTypeControl,
26515             disableDoubleClickZoom: this.disableDoubleClickZoom,
26516             scrollwheel: this.scrollwheel,
26517             streetViewControl: this.streetViewControl,
26518             locationName: this.locationName,
26519             draggable: this.draggable,
26520             enableAutocomplete: this.enableAutocomplete,
26521             enableReverseGeocode: this.enableReverseGeocode
26522         });
26523         
26524         var _marker = new google.maps.Marker({
26525             position: position,
26526             map: _map,
26527             title: this.markerTitle,
26528             draggable: this.draggable
26529         });
26530         
26531         return {
26532             map: _map,
26533             marker: _marker,
26534             circle: null,
26535             location: position,
26536             radius: this.radius,
26537             locationName: this.locationName,
26538             addressComponents: {
26539                 formatted_address: null,
26540                 addressLine1: null,
26541                 addressLine2: null,
26542                 streetName: null,
26543                 streetNumber: null,
26544                 city: null,
26545                 district: null,
26546                 state: null,
26547                 stateOrProvince: null
26548             },
26549             settings: this,
26550             domContainer: this.el.dom,
26551             geodecoder: new google.maps.Geocoder()
26552         };
26553     },
26554     
26555     drawCircle: function(center, radius, options) 
26556     {
26557         if (this.gMapContext.circle != null) {
26558             this.gMapContext.circle.setMap(null);
26559         }
26560         if (radius > 0) {
26561             radius *= 1;
26562             options = Roo.apply({}, options, {
26563                 strokeColor: "#0000FF",
26564                 strokeOpacity: .35,
26565                 strokeWeight: 2,
26566                 fillColor: "#0000FF",
26567                 fillOpacity: .2
26568             });
26569             
26570             options.map = this.gMapContext.map;
26571             options.radius = radius;
26572             options.center = center;
26573             this.gMapContext.circle = new google.maps.Circle(options);
26574             return this.gMapContext.circle;
26575         }
26576         
26577         return null;
26578     },
26579     
26580     setPosition: function(location) 
26581     {
26582         this.gMapContext.location = location;
26583         this.gMapContext.marker.setPosition(location);
26584         this.gMapContext.map.panTo(location);
26585         this.drawCircle(location, this.gMapContext.radius, {});
26586         
26587         var _this = this;
26588         
26589         if (this.gMapContext.settings.enableReverseGeocode) {
26590             this.gMapContext.geodecoder.geocode({
26591                 latLng: this.gMapContext.location
26592             }, function(results, status) {
26593                 
26594                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26595                     _this.gMapContext.locationName = results[0].formatted_address;
26596                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26597                     
26598                     _this.fireEvent('positionchanged', this, location);
26599                 }
26600             });
26601             
26602             return;
26603         }
26604         
26605         this.fireEvent('positionchanged', this, location);
26606     },
26607     
26608     resize: function()
26609     {
26610         google.maps.event.trigger(this.gMapContext.map, "resize");
26611         
26612         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26613         
26614         this.fireEvent('resize', this);
26615     },
26616     
26617     setPositionByLatLng: function(latitude, longitude)
26618     {
26619         this.setPosition(new google.maps.LatLng(latitude, longitude));
26620     },
26621     
26622     getCurrentPosition: function() 
26623     {
26624         return {
26625             latitude: this.gMapContext.location.lat(),
26626             longitude: this.gMapContext.location.lng()
26627         };
26628     },
26629     
26630     getAddressName: function() 
26631     {
26632         return this.gMapContext.locationName;
26633     },
26634     
26635     getAddressComponents: function() 
26636     {
26637         return this.gMapContext.addressComponents;
26638     },
26639     
26640     address_component_from_google_geocode: function(address_components) 
26641     {
26642         var result = {};
26643         
26644         for (var i = 0; i < address_components.length; i++) {
26645             var component = address_components[i];
26646             if (component.types.indexOf("postal_code") >= 0) {
26647                 result.postalCode = component.short_name;
26648             } else if (component.types.indexOf("street_number") >= 0) {
26649                 result.streetNumber = component.short_name;
26650             } else if (component.types.indexOf("route") >= 0) {
26651                 result.streetName = component.short_name;
26652             } else if (component.types.indexOf("neighborhood") >= 0) {
26653                 result.city = component.short_name;
26654             } else if (component.types.indexOf("locality") >= 0) {
26655                 result.city = component.short_name;
26656             } else if (component.types.indexOf("sublocality") >= 0) {
26657                 result.district = component.short_name;
26658             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26659                 result.stateOrProvince = component.short_name;
26660             } else if (component.types.indexOf("country") >= 0) {
26661                 result.country = component.short_name;
26662             }
26663         }
26664         
26665         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26666         result.addressLine2 = "";
26667         return result;
26668     },
26669     
26670     setZoomLevel: function(zoom)
26671     {
26672         this.gMapContext.map.setZoom(zoom);
26673     },
26674     
26675     show: function()
26676     {
26677         if(!this.el){
26678             return;
26679         }
26680         
26681         this.el.show();
26682         
26683         this.resize();
26684         
26685         this.fireEvent('show', this);
26686     },
26687     
26688     hide: function()
26689     {
26690         if(!this.el){
26691             return;
26692         }
26693         
26694         this.el.hide();
26695         
26696         this.fireEvent('hide', this);
26697     }
26698     
26699 });
26700
26701 Roo.apply(Roo.bootstrap.LocationPicker, {
26702     
26703     OverlayView : function(map, options)
26704     {
26705         options = options || {};
26706         
26707         this.setMap(map);
26708     }
26709     
26710     
26711 });/*
26712  * - LGPL
26713  *
26714  * Alert
26715  * 
26716  */
26717
26718 /**
26719  * @class Roo.bootstrap.Alert
26720  * @extends Roo.bootstrap.Component
26721  * Bootstrap Alert class
26722  * @cfg {String} title The title of alert
26723  * @cfg {String} html The content of alert
26724  * @cfg {String} weight (  success | info | warning | danger )
26725  * @cfg {String} faicon font-awesomeicon
26726  * 
26727  * @constructor
26728  * Create a new alert
26729  * @param {Object} config The config object
26730  */
26731
26732
26733 Roo.bootstrap.Alert = function(config){
26734     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26735     
26736 };
26737
26738 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26739     
26740     title: '',
26741     html: '',
26742     weight: false,
26743     faicon: false,
26744     
26745     getAutoCreate : function()
26746     {
26747         
26748         var cfg = {
26749             tag : 'div',
26750             cls : 'alert',
26751             cn : [
26752                 {
26753                     tag : 'i',
26754                     cls : 'roo-alert-icon'
26755                     
26756                 },
26757                 {
26758                     tag : 'b',
26759                     cls : 'roo-alert-title',
26760                     html : this.title
26761                 },
26762                 {
26763                     tag : 'span',
26764                     cls : 'roo-alert-text',
26765                     html : this.html
26766                 }
26767             ]
26768         };
26769         
26770         if(this.faicon){
26771             cfg.cn[0].cls += ' fa ' + this.faicon;
26772         }
26773         
26774         if(this.weight){
26775             cfg.cls += ' alert-' + this.weight;
26776         }
26777         
26778         return cfg;
26779     },
26780     
26781     initEvents: function() 
26782     {
26783         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26784     },
26785     
26786     setTitle : function(str)
26787     {
26788         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26789     },
26790     
26791     setText : function(str)
26792     {
26793         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26794     },
26795     
26796     setWeight : function(weight)
26797     {
26798         if(this.weight){
26799             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26800         }
26801         
26802         this.weight = weight;
26803         
26804         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26805     },
26806     
26807     setIcon : function(icon)
26808     {
26809         if(this.faicon){
26810             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26811         }
26812         
26813         this.faicon = icon;
26814         
26815         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26816     },
26817     
26818     hide: function() 
26819     {
26820         this.el.hide();   
26821     },
26822     
26823     show: function() 
26824     {  
26825         this.el.show();   
26826     }
26827     
26828 });
26829
26830  
26831 /*
26832 * Licence: LGPL
26833 */
26834
26835 /**
26836  * @class Roo.bootstrap.UploadCropbox
26837  * @extends Roo.bootstrap.Component
26838  * Bootstrap UploadCropbox class
26839  * @cfg {String} emptyText show when image has been loaded
26840  * @cfg {String} rotateNotify show when image too small to rotate
26841  * @cfg {Number} errorTimeout default 3000
26842  * @cfg {Number} minWidth default 300
26843  * @cfg {Number} minHeight default 300
26844  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26845  * @cfg {Boolean} isDocument (true|false) default false
26846  * @cfg {String} url action url
26847  * @cfg {String} paramName default 'imageUpload'
26848  * @cfg {String} method default POST
26849  * @cfg {Boolean} loadMask (true|false) default true
26850  * @cfg {Boolean} loadingText default 'Loading...'
26851  * 
26852  * @constructor
26853  * Create a new UploadCropbox
26854  * @param {Object} config The config object
26855  */
26856
26857 Roo.bootstrap.UploadCropbox = function(config){
26858     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26859     
26860     this.addEvents({
26861         /**
26862          * @event beforeselectfile
26863          * Fire before select file
26864          * @param {Roo.bootstrap.UploadCropbox} this
26865          */
26866         "beforeselectfile" : true,
26867         /**
26868          * @event initial
26869          * Fire after initEvent
26870          * @param {Roo.bootstrap.UploadCropbox} this
26871          */
26872         "initial" : true,
26873         /**
26874          * @event crop
26875          * Fire after initEvent
26876          * @param {Roo.bootstrap.UploadCropbox} this
26877          * @param {String} data
26878          */
26879         "crop" : true,
26880         /**
26881          * @event prepare
26882          * Fire when preparing the file data
26883          * @param {Roo.bootstrap.UploadCropbox} this
26884          * @param {Object} file
26885          */
26886         "prepare" : true,
26887         /**
26888          * @event exception
26889          * Fire when get exception
26890          * @param {Roo.bootstrap.UploadCropbox} this
26891          * @param {XMLHttpRequest} xhr
26892          */
26893         "exception" : true,
26894         /**
26895          * @event beforeloadcanvas
26896          * Fire before load the canvas
26897          * @param {Roo.bootstrap.UploadCropbox} this
26898          * @param {String} src
26899          */
26900         "beforeloadcanvas" : true,
26901         /**
26902          * @event trash
26903          * Fire when trash image
26904          * @param {Roo.bootstrap.UploadCropbox} this
26905          */
26906         "trash" : true,
26907         /**
26908          * @event download
26909          * Fire when download the image
26910          * @param {Roo.bootstrap.UploadCropbox} this
26911          */
26912         "download" : true,
26913         /**
26914          * @event footerbuttonclick
26915          * Fire when footerbuttonclick
26916          * @param {Roo.bootstrap.UploadCropbox} this
26917          * @param {String} type
26918          */
26919         "footerbuttonclick" : true,
26920         /**
26921          * @event resize
26922          * Fire when resize
26923          * @param {Roo.bootstrap.UploadCropbox} this
26924          */
26925         "resize" : true,
26926         /**
26927          * @event rotate
26928          * Fire when rotate the image
26929          * @param {Roo.bootstrap.UploadCropbox} this
26930          * @param {String} pos
26931          */
26932         "rotate" : true,
26933         /**
26934          * @event inspect
26935          * Fire when inspect the file
26936          * @param {Roo.bootstrap.UploadCropbox} this
26937          * @param {Object} file
26938          */
26939         "inspect" : true,
26940         /**
26941          * @event upload
26942          * Fire when xhr upload the file
26943          * @param {Roo.bootstrap.UploadCropbox} this
26944          * @param {Object} data
26945          */
26946         "upload" : true,
26947         /**
26948          * @event arrange
26949          * Fire when arrange the file data
26950          * @param {Roo.bootstrap.UploadCropbox} this
26951          * @param {Object} formData
26952          */
26953         "arrange" : true
26954     });
26955     
26956     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26957 };
26958
26959 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26960     
26961     emptyText : 'Click to upload image',
26962     rotateNotify : 'Image is too small to rotate',
26963     errorTimeout : 3000,
26964     scale : 0,
26965     baseScale : 1,
26966     rotate : 0,
26967     dragable : false,
26968     pinching : false,
26969     mouseX : 0,
26970     mouseY : 0,
26971     cropData : false,
26972     minWidth : 300,
26973     minHeight : 300,
26974     file : false,
26975     exif : {},
26976     baseRotate : 1,
26977     cropType : 'image/jpeg',
26978     buttons : false,
26979     canvasLoaded : false,
26980     isDocument : false,
26981     method : 'POST',
26982     paramName : 'imageUpload',
26983     loadMask : true,
26984     loadingText : 'Loading...',
26985     maskEl : false,
26986     
26987     getAutoCreate : function()
26988     {
26989         var cfg = {
26990             tag : 'div',
26991             cls : 'roo-upload-cropbox',
26992             cn : [
26993                 {
26994                     tag : 'input',
26995                     cls : 'roo-upload-cropbox-selector',
26996                     type : 'file'
26997                 },
26998                 {
26999                     tag : 'div',
27000                     cls : 'roo-upload-cropbox-body',
27001                     style : 'cursor:pointer',
27002                     cn : [
27003                         {
27004                             tag : 'div',
27005                             cls : 'roo-upload-cropbox-preview'
27006                         },
27007                         {
27008                             tag : 'div',
27009                             cls : 'roo-upload-cropbox-thumb'
27010                         },
27011                         {
27012                             tag : 'div',
27013                             cls : 'roo-upload-cropbox-empty-notify',
27014                             html : this.emptyText
27015                         },
27016                         {
27017                             tag : 'div',
27018                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27019                             html : this.rotateNotify
27020                         }
27021                     ]
27022                 },
27023                 {
27024                     tag : 'div',
27025                     cls : 'roo-upload-cropbox-footer',
27026                     cn : {
27027                         tag : 'div',
27028                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27029                         cn : []
27030                     }
27031                 }
27032             ]
27033         };
27034         
27035         return cfg;
27036     },
27037     
27038     onRender : function(ct, position)
27039     {
27040         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27041         
27042         if (this.buttons.length) {
27043             
27044             Roo.each(this.buttons, function(bb) {
27045                 
27046                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27047                 
27048                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27049                 
27050             }, this);
27051         }
27052         
27053         if(this.loadMask){
27054             this.maskEl = this.el;
27055         }
27056     },
27057     
27058     initEvents : function()
27059     {
27060         this.urlAPI = (window.createObjectURL && window) || 
27061                                 (window.URL && URL.revokeObjectURL && URL) || 
27062                                 (window.webkitURL && webkitURL);
27063                         
27064         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27065         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27066         
27067         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27068         this.selectorEl.hide();
27069         
27070         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27071         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27072         
27073         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27074         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27075         this.thumbEl.hide();
27076         
27077         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27078         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27079         
27080         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27081         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27082         this.errorEl.hide();
27083         
27084         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27085         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27086         this.footerEl.hide();
27087         
27088         this.setThumbBoxSize();
27089         
27090         this.bind();
27091         
27092         this.resize();
27093         
27094         this.fireEvent('initial', this);
27095     },
27096
27097     bind : function()
27098     {
27099         var _this = this;
27100         
27101         window.addEventListener("resize", function() { _this.resize(); } );
27102         
27103         this.bodyEl.on('click', this.beforeSelectFile, this);
27104         
27105         if(Roo.isTouch){
27106             this.bodyEl.on('touchstart', this.onTouchStart, this);
27107             this.bodyEl.on('touchmove', this.onTouchMove, this);
27108             this.bodyEl.on('touchend', this.onTouchEnd, this);
27109         }
27110         
27111         if(!Roo.isTouch){
27112             this.bodyEl.on('mousedown', this.onMouseDown, this);
27113             this.bodyEl.on('mousemove', this.onMouseMove, this);
27114             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27115             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27116             Roo.get(document).on('mouseup', this.onMouseUp, this);
27117         }
27118         
27119         this.selectorEl.on('change', this.onFileSelected, this);
27120     },
27121     
27122     reset : function()
27123     {    
27124         this.scale = 0;
27125         this.baseScale = 1;
27126         this.rotate = 0;
27127         this.baseRotate = 1;
27128         this.dragable = false;
27129         this.pinching = false;
27130         this.mouseX = 0;
27131         this.mouseY = 0;
27132         this.cropData = false;
27133         this.notifyEl.dom.innerHTML = this.emptyText;
27134         
27135         this.selectorEl.dom.value = '';
27136         
27137     },
27138     
27139     resize : function()
27140     {
27141         if(this.fireEvent('resize', this) != false){
27142             this.setThumbBoxPosition();
27143             this.setCanvasPosition();
27144         }
27145     },
27146     
27147     onFooterButtonClick : function(e, el, o, type)
27148     {
27149         switch (type) {
27150             case 'rotate-left' :
27151                 this.onRotateLeft(e);
27152                 break;
27153             case 'rotate-right' :
27154                 this.onRotateRight(e);
27155                 break;
27156             case 'picture' :
27157                 this.beforeSelectFile(e);
27158                 break;
27159             case 'trash' :
27160                 this.trash(e);
27161                 break;
27162             case 'crop' :
27163                 this.crop(e);
27164                 break;
27165             case 'download' :
27166                 this.download(e);
27167                 break;
27168             default :
27169                 break;
27170         }
27171         
27172         this.fireEvent('footerbuttonclick', this, type);
27173     },
27174     
27175     beforeSelectFile : function(e)
27176     {
27177         e.preventDefault();
27178         
27179         if(this.fireEvent('beforeselectfile', this) != false){
27180             this.selectorEl.dom.click();
27181         }
27182     },
27183     
27184     onFileSelected : function(e)
27185     {
27186         e.preventDefault();
27187         
27188         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27189             return;
27190         }
27191         
27192         var file = this.selectorEl.dom.files[0];
27193         
27194         if(this.fireEvent('inspect', this, file) != false){
27195             this.prepare(file);
27196         }
27197         
27198     },
27199     
27200     trash : function(e)
27201     {
27202         this.fireEvent('trash', this);
27203     },
27204     
27205     download : function(e)
27206     {
27207         this.fireEvent('download', this);
27208     },
27209     
27210     loadCanvas : function(src)
27211     {   
27212         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27213             
27214             this.reset();
27215             
27216             this.imageEl = document.createElement('img');
27217             
27218             var _this = this;
27219             
27220             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27221             
27222             this.imageEl.src = src;
27223         }
27224     },
27225     
27226     onLoadCanvas : function()
27227     {   
27228         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27229         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27230         
27231         this.bodyEl.un('click', this.beforeSelectFile, this);
27232         
27233         this.notifyEl.hide();
27234         this.thumbEl.show();
27235         this.footerEl.show();
27236         
27237         this.baseRotateLevel();
27238         
27239         if(this.isDocument){
27240             this.setThumbBoxSize();
27241         }
27242         
27243         this.setThumbBoxPosition();
27244         
27245         this.baseScaleLevel();
27246         
27247         this.draw();
27248         
27249         this.resize();
27250         
27251         this.canvasLoaded = true;
27252         
27253         if(this.loadMask){
27254             this.maskEl.unmask();
27255         }
27256         
27257     },
27258     
27259     setCanvasPosition : function()
27260     {   
27261         if(!this.canvasEl){
27262             return;
27263         }
27264         
27265         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27266         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27267         
27268         this.previewEl.setLeft(pw);
27269         this.previewEl.setTop(ph);
27270         
27271     },
27272     
27273     onMouseDown : function(e)
27274     {   
27275         e.stopEvent();
27276         
27277         this.dragable = true;
27278         this.pinching = false;
27279         
27280         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27281             this.dragable = false;
27282             return;
27283         }
27284         
27285         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27286         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27287         
27288     },
27289     
27290     onMouseMove : function(e)
27291     {   
27292         e.stopEvent();
27293         
27294         if(!this.canvasLoaded){
27295             return;
27296         }
27297         
27298         if (!this.dragable){
27299             return;
27300         }
27301         
27302         var minX = Math.ceil(this.thumbEl.getLeft(true));
27303         var minY = Math.ceil(this.thumbEl.getTop(true));
27304         
27305         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27306         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27307         
27308         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27309         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27310         
27311         x = x - this.mouseX;
27312         y = y - this.mouseY;
27313         
27314         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27315         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27316         
27317         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27318         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27319         
27320         this.previewEl.setLeft(bgX);
27321         this.previewEl.setTop(bgY);
27322         
27323         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27324         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27325     },
27326     
27327     onMouseUp : function(e)
27328     {   
27329         e.stopEvent();
27330         
27331         this.dragable = false;
27332     },
27333     
27334     onMouseWheel : function(e)
27335     {   
27336         e.stopEvent();
27337         
27338         this.startScale = this.scale;
27339         
27340         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27341         
27342         if(!this.zoomable()){
27343             this.scale = this.startScale;
27344             return;
27345         }
27346         
27347         this.draw();
27348         
27349         return;
27350     },
27351     
27352     zoomable : function()
27353     {
27354         var minScale = this.thumbEl.getWidth() / this.minWidth;
27355         
27356         if(this.minWidth < this.minHeight){
27357             minScale = this.thumbEl.getHeight() / this.minHeight;
27358         }
27359         
27360         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27361         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27362         
27363         if(
27364                 this.isDocument &&
27365                 (this.rotate == 0 || this.rotate == 180) && 
27366                 (
27367                     width > this.imageEl.OriginWidth || 
27368                     height > this.imageEl.OriginHeight ||
27369                     (width < this.minWidth && height < this.minHeight)
27370                 )
27371         ){
27372             return false;
27373         }
27374         
27375         if(
27376                 this.isDocument &&
27377                 (this.rotate == 90 || this.rotate == 270) && 
27378                 (
27379                     width > this.imageEl.OriginWidth || 
27380                     height > this.imageEl.OriginHeight ||
27381                     (width < this.minHeight && height < this.minWidth)
27382                 )
27383         ){
27384             return false;
27385         }
27386         
27387         if(
27388                 !this.isDocument &&
27389                 (this.rotate == 0 || this.rotate == 180) && 
27390                 (
27391                     width < this.minWidth || 
27392                     width > this.imageEl.OriginWidth || 
27393                     height < this.minHeight || 
27394                     height > this.imageEl.OriginHeight
27395                 )
27396         ){
27397             return false;
27398         }
27399         
27400         if(
27401                 !this.isDocument &&
27402                 (this.rotate == 90 || this.rotate == 270) && 
27403                 (
27404                     width < this.minHeight || 
27405                     width > this.imageEl.OriginWidth || 
27406                     height < this.minWidth || 
27407                     height > this.imageEl.OriginHeight
27408                 )
27409         ){
27410             return false;
27411         }
27412         
27413         return true;
27414         
27415     },
27416     
27417     onRotateLeft : function(e)
27418     {   
27419         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27420             
27421             var minScale = this.thumbEl.getWidth() / this.minWidth;
27422             
27423             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27424             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27425             
27426             this.startScale = this.scale;
27427             
27428             while (this.getScaleLevel() < minScale){
27429             
27430                 this.scale = this.scale + 1;
27431                 
27432                 if(!this.zoomable()){
27433                     break;
27434                 }
27435                 
27436                 if(
27437                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27438                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27439                 ){
27440                     continue;
27441                 }
27442                 
27443                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27444
27445                 this.draw();
27446                 
27447                 return;
27448             }
27449             
27450             this.scale = this.startScale;
27451             
27452             this.onRotateFail();
27453             
27454             return false;
27455         }
27456         
27457         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27458
27459         if(this.isDocument){
27460             this.setThumbBoxSize();
27461             this.setThumbBoxPosition();
27462             this.setCanvasPosition();
27463         }
27464         
27465         this.draw();
27466         
27467         this.fireEvent('rotate', this, 'left');
27468         
27469     },
27470     
27471     onRotateRight : function(e)
27472     {
27473         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27474             
27475             var minScale = this.thumbEl.getWidth() / this.minWidth;
27476         
27477             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27478             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27479             
27480             this.startScale = this.scale;
27481             
27482             while (this.getScaleLevel() < minScale){
27483             
27484                 this.scale = this.scale + 1;
27485                 
27486                 if(!this.zoomable()){
27487                     break;
27488                 }
27489                 
27490                 if(
27491                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27492                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27493                 ){
27494                     continue;
27495                 }
27496                 
27497                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27498
27499                 this.draw();
27500                 
27501                 return;
27502             }
27503             
27504             this.scale = this.startScale;
27505             
27506             this.onRotateFail();
27507             
27508             return false;
27509         }
27510         
27511         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27512
27513         if(this.isDocument){
27514             this.setThumbBoxSize();
27515             this.setThumbBoxPosition();
27516             this.setCanvasPosition();
27517         }
27518         
27519         this.draw();
27520         
27521         this.fireEvent('rotate', this, 'right');
27522     },
27523     
27524     onRotateFail : function()
27525     {
27526         this.errorEl.show(true);
27527         
27528         var _this = this;
27529         
27530         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27531     },
27532     
27533     draw : function()
27534     {
27535         this.previewEl.dom.innerHTML = '';
27536         
27537         var canvasEl = document.createElement("canvas");
27538         
27539         var contextEl = canvasEl.getContext("2d");
27540         
27541         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27542         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27543         var center = this.imageEl.OriginWidth / 2;
27544         
27545         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27546             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27547             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27548             center = this.imageEl.OriginHeight / 2;
27549         }
27550         
27551         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27552         
27553         contextEl.translate(center, center);
27554         contextEl.rotate(this.rotate * Math.PI / 180);
27555
27556         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27557         
27558         this.canvasEl = document.createElement("canvas");
27559         
27560         this.contextEl = this.canvasEl.getContext("2d");
27561         
27562         switch (this.rotate) {
27563             case 0 :
27564                 
27565                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27566                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27567                 
27568                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27569                 
27570                 break;
27571             case 90 : 
27572                 
27573                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27574                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27575                 
27576                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27577                     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);
27578                     break;
27579                 }
27580                 
27581                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27582                 
27583                 break;
27584             case 180 :
27585                 
27586                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27587                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27588                 
27589                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27590                     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);
27591                     break;
27592                 }
27593                 
27594                 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);
27595                 
27596                 break;
27597             case 270 :
27598                 
27599                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27600                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27601         
27602                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27603                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27604                     break;
27605                 }
27606                 
27607                 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);
27608                 
27609                 break;
27610             default : 
27611                 break;
27612         }
27613         
27614         this.previewEl.appendChild(this.canvasEl);
27615         
27616         this.setCanvasPosition();
27617     },
27618     
27619     crop : function()
27620     {
27621         if(!this.canvasLoaded){
27622             return;
27623         }
27624         
27625         var imageCanvas = document.createElement("canvas");
27626         
27627         var imageContext = imageCanvas.getContext("2d");
27628         
27629         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27630         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27631         
27632         var center = imageCanvas.width / 2;
27633         
27634         imageContext.translate(center, center);
27635         
27636         imageContext.rotate(this.rotate * Math.PI / 180);
27637         
27638         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27639         
27640         var canvas = document.createElement("canvas");
27641         
27642         var context = canvas.getContext("2d");
27643                 
27644         canvas.width = this.minWidth;
27645         canvas.height = this.minHeight;
27646
27647         switch (this.rotate) {
27648             case 0 :
27649                 
27650                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27651                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27652                 
27653                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27654                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27655                 
27656                 var targetWidth = this.minWidth - 2 * x;
27657                 var targetHeight = this.minHeight - 2 * y;
27658                 
27659                 var scale = 1;
27660                 
27661                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27662                     scale = targetWidth / width;
27663                 }
27664                 
27665                 if(x > 0 && y == 0){
27666                     scale = targetHeight / height;
27667                 }
27668                 
27669                 if(x > 0 && y > 0){
27670                     scale = targetWidth / width;
27671                     
27672                     if(width < height){
27673                         scale = targetHeight / height;
27674                     }
27675                 }
27676                 
27677                 context.scale(scale, scale);
27678                 
27679                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27680                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27681
27682                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27683                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27684
27685                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27686                 
27687                 break;
27688             case 90 : 
27689                 
27690                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27691                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27692                 
27693                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27694                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27695                 
27696                 var targetWidth = this.minWidth - 2 * x;
27697                 var targetHeight = this.minHeight - 2 * y;
27698                 
27699                 var scale = 1;
27700                 
27701                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27702                     scale = targetWidth / width;
27703                 }
27704                 
27705                 if(x > 0 && y == 0){
27706                     scale = targetHeight / height;
27707                 }
27708                 
27709                 if(x > 0 && y > 0){
27710                     scale = targetWidth / width;
27711                     
27712                     if(width < height){
27713                         scale = targetHeight / height;
27714                     }
27715                 }
27716                 
27717                 context.scale(scale, scale);
27718                 
27719                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27720                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27721
27722                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27723                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27724                 
27725                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27726                 
27727                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27728                 
27729                 break;
27730             case 180 :
27731                 
27732                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27733                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27734                 
27735                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27736                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27737                 
27738                 var targetWidth = this.minWidth - 2 * x;
27739                 var targetHeight = this.minHeight - 2 * y;
27740                 
27741                 var scale = 1;
27742                 
27743                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27744                     scale = targetWidth / width;
27745                 }
27746                 
27747                 if(x > 0 && y == 0){
27748                     scale = targetHeight / height;
27749                 }
27750                 
27751                 if(x > 0 && y > 0){
27752                     scale = targetWidth / width;
27753                     
27754                     if(width < height){
27755                         scale = targetHeight / height;
27756                     }
27757                 }
27758                 
27759                 context.scale(scale, scale);
27760                 
27761                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27762                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27763
27764                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27765                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27766
27767                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27768                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27769                 
27770                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27771                 
27772                 break;
27773             case 270 :
27774                 
27775                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27776                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27777                 
27778                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27779                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27780                 
27781                 var targetWidth = this.minWidth - 2 * x;
27782                 var targetHeight = this.minHeight - 2 * y;
27783                 
27784                 var scale = 1;
27785                 
27786                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27787                     scale = targetWidth / width;
27788                 }
27789                 
27790                 if(x > 0 && y == 0){
27791                     scale = targetHeight / height;
27792                 }
27793                 
27794                 if(x > 0 && y > 0){
27795                     scale = targetWidth / width;
27796                     
27797                     if(width < height){
27798                         scale = targetHeight / height;
27799                     }
27800                 }
27801                 
27802                 context.scale(scale, scale);
27803                 
27804                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27805                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27806
27807                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27808                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27809                 
27810                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27811                 
27812                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27813                 
27814                 break;
27815             default : 
27816                 break;
27817         }
27818         
27819         this.cropData = canvas.toDataURL(this.cropType);
27820         
27821         if(this.fireEvent('crop', this, this.cropData) !== false){
27822             this.process(this.file, this.cropData);
27823         }
27824         
27825         return;
27826         
27827     },
27828     
27829     setThumbBoxSize : function()
27830     {
27831         var width, height;
27832         
27833         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27834             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27835             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27836             
27837             this.minWidth = width;
27838             this.minHeight = height;
27839             
27840             if(this.rotate == 90 || this.rotate == 270){
27841                 this.minWidth = height;
27842                 this.minHeight = width;
27843             }
27844         }
27845         
27846         height = 300;
27847         width = Math.ceil(this.minWidth * height / this.minHeight);
27848         
27849         if(this.minWidth > this.minHeight){
27850             width = 300;
27851             height = Math.ceil(this.minHeight * width / this.minWidth);
27852         }
27853         
27854         this.thumbEl.setStyle({
27855             width : width + 'px',
27856             height : height + 'px'
27857         });
27858
27859         return;
27860             
27861     },
27862     
27863     setThumbBoxPosition : function()
27864     {
27865         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27866         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27867         
27868         this.thumbEl.setLeft(x);
27869         this.thumbEl.setTop(y);
27870         
27871     },
27872     
27873     baseRotateLevel : function()
27874     {
27875         this.baseRotate = 1;
27876         
27877         if(
27878                 typeof(this.exif) != 'undefined' &&
27879                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27880                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27881         ){
27882             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27883         }
27884         
27885         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27886         
27887     },
27888     
27889     baseScaleLevel : function()
27890     {
27891         var width, height;
27892         
27893         if(this.isDocument){
27894             
27895             if(this.baseRotate == 6 || this.baseRotate == 8){
27896             
27897                 height = this.thumbEl.getHeight();
27898                 this.baseScale = height / this.imageEl.OriginWidth;
27899
27900                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27901                     width = this.thumbEl.getWidth();
27902                     this.baseScale = width / this.imageEl.OriginHeight;
27903                 }
27904
27905                 return;
27906             }
27907
27908             height = this.thumbEl.getHeight();
27909             this.baseScale = height / this.imageEl.OriginHeight;
27910
27911             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27912                 width = this.thumbEl.getWidth();
27913                 this.baseScale = width / this.imageEl.OriginWidth;
27914             }
27915
27916             return;
27917         }
27918         
27919         if(this.baseRotate == 6 || this.baseRotate == 8){
27920             
27921             width = this.thumbEl.getHeight();
27922             this.baseScale = width / this.imageEl.OriginHeight;
27923             
27924             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27925                 height = this.thumbEl.getWidth();
27926                 this.baseScale = height / this.imageEl.OriginHeight;
27927             }
27928             
27929             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27930                 height = this.thumbEl.getWidth();
27931                 this.baseScale = height / this.imageEl.OriginHeight;
27932                 
27933                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27934                     width = this.thumbEl.getHeight();
27935                     this.baseScale = width / this.imageEl.OriginWidth;
27936                 }
27937             }
27938             
27939             return;
27940         }
27941         
27942         width = this.thumbEl.getWidth();
27943         this.baseScale = width / this.imageEl.OriginWidth;
27944         
27945         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27946             height = this.thumbEl.getHeight();
27947             this.baseScale = height / this.imageEl.OriginHeight;
27948         }
27949         
27950         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27951             
27952             height = this.thumbEl.getHeight();
27953             this.baseScale = height / this.imageEl.OriginHeight;
27954             
27955             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27956                 width = this.thumbEl.getWidth();
27957                 this.baseScale = width / this.imageEl.OriginWidth;
27958             }
27959             
27960         }
27961         
27962         return;
27963     },
27964     
27965     getScaleLevel : function()
27966     {
27967         return this.baseScale * Math.pow(1.1, this.scale);
27968     },
27969     
27970     onTouchStart : function(e)
27971     {
27972         if(!this.canvasLoaded){
27973             this.beforeSelectFile(e);
27974             return;
27975         }
27976         
27977         var touches = e.browserEvent.touches;
27978         
27979         if(!touches){
27980             return;
27981         }
27982         
27983         if(touches.length == 1){
27984             this.onMouseDown(e);
27985             return;
27986         }
27987         
27988         if(touches.length != 2){
27989             return;
27990         }
27991         
27992         var coords = [];
27993         
27994         for(var i = 0, finger; finger = touches[i]; i++){
27995             coords.push(finger.pageX, finger.pageY);
27996         }
27997         
27998         var x = Math.pow(coords[0] - coords[2], 2);
27999         var y = Math.pow(coords[1] - coords[3], 2);
28000         
28001         this.startDistance = Math.sqrt(x + y);
28002         
28003         this.startScale = this.scale;
28004         
28005         this.pinching = true;
28006         this.dragable = false;
28007         
28008     },
28009     
28010     onTouchMove : function(e)
28011     {
28012         if(!this.pinching && !this.dragable){
28013             return;
28014         }
28015         
28016         var touches = e.browserEvent.touches;
28017         
28018         if(!touches){
28019             return;
28020         }
28021         
28022         if(this.dragable){
28023             this.onMouseMove(e);
28024             return;
28025         }
28026         
28027         var coords = [];
28028         
28029         for(var i = 0, finger; finger = touches[i]; i++){
28030             coords.push(finger.pageX, finger.pageY);
28031         }
28032         
28033         var x = Math.pow(coords[0] - coords[2], 2);
28034         var y = Math.pow(coords[1] - coords[3], 2);
28035         
28036         this.endDistance = Math.sqrt(x + y);
28037         
28038         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28039         
28040         if(!this.zoomable()){
28041             this.scale = this.startScale;
28042             return;
28043         }
28044         
28045         this.draw();
28046         
28047     },
28048     
28049     onTouchEnd : function(e)
28050     {
28051         this.pinching = false;
28052         this.dragable = false;
28053         
28054     },
28055     
28056     process : function(file, crop)
28057     {
28058         if(this.loadMask){
28059             this.maskEl.mask(this.loadingText);
28060         }
28061         
28062         this.xhr = new XMLHttpRequest();
28063         
28064         file.xhr = this.xhr;
28065
28066         this.xhr.open(this.method, this.url, true);
28067         
28068         var headers = {
28069             "Accept": "application/json",
28070             "Cache-Control": "no-cache",
28071             "X-Requested-With": "XMLHttpRequest"
28072         };
28073         
28074         for (var headerName in headers) {
28075             var headerValue = headers[headerName];
28076             if (headerValue) {
28077                 this.xhr.setRequestHeader(headerName, headerValue);
28078             }
28079         }
28080         
28081         var _this = this;
28082         
28083         this.xhr.onload = function()
28084         {
28085             _this.xhrOnLoad(_this.xhr);
28086         }
28087         
28088         this.xhr.onerror = function()
28089         {
28090             _this.xhrOnError(_this.xhr);
28091         }
28092         
28093         var formData = new FormData();
28094
28095         formData.append('returnHTML', 'NO');
28096         
28097         if(crop){
28098             formData.append('crop', crop);
28099         }
28100         
28101         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28102             formData.append(this.paramName, file, file.name);
28103         }
28104         
28105         if(typeof(file.filename) != 'undefined'){
28106             formData.append('filename', file.filename);
28107         }
28108         
28109         if(typeof(file.mimetype) != 'undefined'){
28110             formData.append('mimetype', file.mimetype);
28111         }
28112         
28113         if(this.fireEvent('arrange', this, formData) != false){
28114             this.xhr.send(formData);
28115         };
28116     },
28117     
28118     xhrOnLoad : function(xhr)
28119     {
28120         if(this.loadMask){
28121             this.maskEl.unmask();
28122         }
28123         
28124         if (xhr.readyState !== 4) {
28125             this.fireEvent('exception', this, xhr);
28126             return;
28127         }
28128
28129         var response = Roo.decode(xhr.responseText);
28130         
28131         if(!response.success){
28132             this.fireEvent('exception', this, xhr);
28133             return;
28134         }
28135         
28136         var response = Roo.decode(xhr.responseText);
28137         
28138         this.fireEvent('upload', this, response);
28139         
28140     },
28141     
28142     xhrOnError : function()
28143     {
28144         if(this.loadMask){
28145             this.maskEl.unmask();
28146         }
28147         
28148         Roo.log('xhr on error');
28149         
28150         var response = Roo.decode(xhr.responseText);
28151           
28152         Roo.log(response);
28153         
28154     },
28155     
28156     prepare : function(file)
28157     {   
28158         if(this.loadMask){
28159             this.maskEl.mask(this.loadingText);
28160         }
28161         
28162         this.file = false;
28163         this.exif = {};
28164         
28165         if(typeof(file) === 'string'){
28166             this.loadCanvas(file);
28167             return;
28168         }
28169         
28170         if(!file || !this.urlAPI){
28171             return;
28172         }
28173         
28174         this.file = file;
28175         this.cropType = file.type;
28176         
28177         var _this = this;
28178         
28179         if(this.fireEvent('prepare', this, this.file) != false){
28180             
28181             var reader = new FileReader();
28182             
28183             reader.onload = function (e) {
28184                 if (e.target.error) {
28185                     Roo.log(e.target.error);
28186                     return;
28187                 }
28188                 
28189                 var buffer = e.target.result,
28190                     dataView = new DataView(buffer),
28191                     offset = 2,
28192                     maxOffset = dataView.byteLength - 4,
28193                     markerBytes,
28194                     markerLength;
28195                 
28196                 if (dataView.getUint16(0) === 0xffd8) {
28197                     while (offset < maxOffset) {
28198                         markerBytes = dataView.getUint16(offset);
28199                         
28200                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28201                             markerLength = dataView.getUint16(offset + 2) + 2;
28202                             if (offset + markerLength > dataView.byteLength) {
28203                                 Roo.log('Invalid meta data: Invalid segment size.');
28204                                 break;
28205                             }
28206                             
28207                             if(markerBytes == 0xffe1){
28208                                 _this.parseExifData(
28209                                     dataView,
28210                                     offset,
28211                                     markerLength
28212                                 );
28213                             }
28214                             
28215                             offset += markerLength;
28216                             
28217                             continue;
28218                         }
28219                         
28220                         break;
28221                     }
28222                     
28223                 }
28224                 
28225                 var url = _this.urlAPI.createObjectURL(_this.file);
28226                 
28227                 _this.loadCanvas(url);
28228                 
28229                 return;
28230             }
28231             
28232             reader.readAsArrayBuffer(this.file);
28233             
28234         }
28235         
28236     },
28237     
28238     parseExifData : function(dataView, offset, length)
28239     {
28240         var tiffOffset = offset + 10,
28241             littleEndian,
28242             dirOffset;
28243     
28244         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28245             // No Exif data, might be XMP data instead
28246             return;
28247         }
28248         
28249         // Check for the ASCII code for "Exif" (0x45786966):
28250         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28251             // No Exif data, might be XMP data instead
28252             return;
28253         }
28254         if (tiffOffset + 8 > dataView.byteLength) {
28255             Roo.log('Invalid Exif data: Invalid segment size.');
28256             return;
28257         }
28258         // Check for the two null bytes:
28259         if (dataView.getUint16(offset + 8) !== 0x0000) {
28260             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28261             return;
28262         }
28263         // Check the byte alignment:
28264         switch (dataView.getUint16(tiffOffset)) {
28265         case 0x4949:
28266             littleEndian = true;
28267             break;
28268         case 0x4D4D:
28269             littleEndian = false;
28270             break;
28271         default:
28272             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28273             return;
28274         }
28275         // Check for the TIFF tag marker (0x002A):
28276         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28277             Roo.log('Invalid Exif data: Missing TIFF marker.');
28278             return;
28279         }
28280         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28281         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28282         
28283         this.parseExifTags(
28284             dataView,
28285             tiffOffset,
28286             tiffOffset + dirOffset,
28287             littleEndian
28288         );
28289     },
28290     
28291     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28292     {
28293         var tagsNumber,
28294             dirEndOffset,
28295             i;
28296         if (dirOffset + 6 > dataView.byteLength) {
28297             Roo.log('Invalid Exif data: Invalid directory offset.');
28298             return;
28299         }
28300         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28301         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28302         if (dirEndOffset + 4 > dataView.byteLength) {
28303             Roo.log('Invalid Exif data: Invalid directory size.');
28304             return;
28305         }
28306         for (i = 0; i < tagsNumber; i += 1) {
28307             this.parseExifTag(
28308                 dataView,
28309                 tiffOffset,
28310                 dirOffset + 2 + 12 * i, // tag offset
28311                 littleEndian
28312             );
28313         }
28314         // Return the offset to the next directory:
28315         return dataView.getUint32(dirEndOffset, littleEndian);
28316     },
28317     
28318     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28319     {
28320         var tag = dataView.getUint16(offset, littleEndian);
28321         
28322         this.exif[tag] = this.getExifValue(
28323             dataView,
28324             tiffOffset,
28325             offset,
28326             dataView.getUint16(offset + 2, littleEndian), // tag type
28327             dataView.getUint32(offset + 4, littleEndian), // tag length
28328             littleEndian
28329         );
28330     },
28331     
28332     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28333     {
28334         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28335             tagSize,
28336             dataOffset,
28337             values,
28338             i,
28339             str,
28340             c;
28341     
28342         if (!tagType) {
28343             Roo.log('Invalid Exif data: Invalid tag type.');
28344             return;
28345         }
28346         
28347         tagSize = tagType.size * length;
28348         // Determine if the value is contained in the dataOffset bytes,
28349         // or if the value at the dataOffset is a pointer to the actual data:
28350         dataOffset = tagSize > 4 ?
28351                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28352         if (dataOffset + tagSize > dataView.byteLength) {
28353             Roo.log('Invalid Exif data: Invalid data offset.');
28354             return;
28355         }
28356         if (length === 1) {
28357             return tagType.getValue(dataView, dataOffset, littleEndian);
28358         }
28359         values = [];
28360         for (i = 0; i < length; i += 1) {
28361             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28362         }
28363         
28364         if (tagType.ascii) {
28365             str = '';
28366             // Concatenate the chars:
28367             for (i = 0; i < values.length; i += 1) {
28368                 c = values[i];
28369                 // Ignore the terminating NULL byte(s):
28370                 if (c === '\u0000') {
28371                     break;
28372                 }
28373                 str += c;
28374             }
28375             return str;
28376         }
28377         return values;
28378     }
28379     
28380 });
28381
28382 Roo.apply(Roo.bootstrap.UploadCropbox, {
28383     tags : {
28384         'Orientation': 0x0112
28385     },
28386     
28387     Orientation: {
28388             1: 0, //'top-left',
28389 //            2: 'top-right',
28390             3: 180, //'bottom-right',
28391 //            4: 'bottom-left',
28392 //            5: 'left-top',
28393             6: 90, //'right-top',
28394 //            7: 'right-bottom',
28395             8: 270 //'left-bottom'
28396     },
28397     
28398     exifTagTypes : {
28399         // byte, 8-bit unsigned int:
28400         1: {
28401             getValue: function (dataView, dataOffset) {
28402                 return dataView.getUint8(dataOffset);
28403             },
28404             size: 1
28405         },
28406         // ascii, 8-bit byte:
28407         2: {
28408             getValue: function (dataView, dataOffset) {
28409                 return String.fromCharCode(dataView.getUint8(dataOffset));
28410             },
28411             size: 1,
28412             ascii: true
28413         },
28414         // short, 16 bit int:
28415         3: {
28416             getValue: function (dataView, dataOffset, littleEndian) {
28417                 return dataView.getUint16(dataOffset, littleEndian);
28418             },
28419             size: 2
28420         },
28421         // long, 32 bit int:
28422         4: {
28423             getValue: function (dataView, dataOffset, littleEndian) {
28424                 return dataView.getUint32(dataOffset, littleEndian);
28425             },
28426             size: 4
28427         },
28428         // rational = two long values, first is numerator, second is denominator:
28429         5: {
28430             getValue: function (dataView, dataOffset, littleEndian) {
28431                 return dataView.getUint32(dataOffset, littleEndian) /
28432                     dataView.getUint32(dataOffset + 4, littleEndian);
28433             },
28434             size: 8
28435         },
28436         // slong, 32 bit signed int:
28437         9: {
28438             getValue: function (dataView, dataOffset, littleEndian) {
28439                 return dataView.getInt32(dataOffset, littleEndian);
28440             },
28441             size: 4
28442         },
28443         // srational, two slongs, first is numerator, second is denominator:
28444         10: {
28445             getValue: function (dataView, dataOffset, littleEndian) {
28446                 return dataView.getInt32(dataOffset, littleEndian) /
28447                     dataView.getInt32(dataOffset + 4, littleEndian);
28448             },
28449             size: 8
28450         }
28451     },
28452     
28453     footer : {
28454         STANDARD : [
28455             {
28456                 tag : 'div',
28457                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28458                 action : 'rotate-left',
28459                 cn : [
28460                     {
28461                         tag : 'button',
28462                         cls : 'btn btn-default',
28463                         html : '<i class="fa fa-undo"></i>'
28464                     }
28465                 ]
28466             },
28467             {
28468                 tag : 'div',
28469                 cls : 'btn-group roo-upload-cropbox-picture',
28470                 action : 'picture',
28471                 cn : [
28472                     {
28473                         tag : 'button',
28474                         cls : 'btn btn-default',
28475                         html : '<i class="fa fa-picture-o"></i>'
28476                     }
28477                 ]
28478             },
28479             {
28480                 tag : 'div',
28481                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28482                 action : 'rotate-right',
28483                 cn : [
28484                     {
28485                         tag : 'button',
28486                         cls : 'btn btn-default',
28487                         html : '<i class="fa fa-repeat"></i>'
28488                     }
28489                 ]
28490             }
28491         ],
28492         DOCUMENT : [
28493             {
28494                 tag : 'div',
28495                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28496                 action : 'rotate-left',
28497                 cn : [
28498                     {
28499                         tag : 'button',
28500                         cls : 'btn btn-default',
28501                         html : '<i class="fa fa-undo"></i>'
28502                     }
28503                 ]
28504             },
28505             {
28506                 tag : 'div',
28507                 cls : 'btn-group roo-upload-cropbox-download',
28508                 action : 'download',
28509                 cn : [
28510                     {
28511                         tag : 'button',
28512                         cls : 'btn btn-default',
28513                         html : '<i class="fa fa-download"></i>'
28514                     }
28515                 ]
28516             },
28517             {
28518                 tag : 'div',
28519                 cls : 'btn-group roo-upload-cropbox-crop',
28520                 action : 'crop',
28521                 cn : [
28522                     {
28523                         tag : 'button',
28524                         cls : 'btn btn-default',
28525                         html : '<i class="fa fa-crop"></i>'
28526                     }
28527                 ]
28528             },
28529             {
28530                 tag : 'div',
28531                 cls : 'btn-group roo-upload-cropbox-trash',
28532                 action : 'trash',
28533                 cn : [
28534                     {
28535                         tag : 'button',
28536                         cls : 'btn btn-default',
28537                         html : '<i class="fa fa-trash"></i>'
28538                     }
28539                 ]
28540             },
28541             {
28542                 tag : 'div',
28543                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28544                 action : 'rotate-right',
28545                 cn : [
28546                     {
28547                         tag : 'button',
28548                         cls : 'btn btn-default',
28549                         html : '<i class="fa fa-repeat"></i>'
28550                     }
28551                 ]
28552             }
28553         ],
28554         ROTATOR : [
28555             {
28556                 tag : 'div',
28557                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28558                 action : 'rotate-left',
28559                 cn : [
28560                     {
28561                         tag : 'button',
28562                         cls : 'btn btn-default',
28563                         html : '<i class="fa fa-undo"></i>'
28564                     }
28565                 ]
28566             },
28567             {
28568                 tag : 'div',
28569                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28570                 action : 'rotate-right',
28571                 cn : [
28572                     {
28573                         tag : 'button',
28574                         cls : 'btn btn-default',
28575                         html : '<i class="fa fa-repeat"></i>'
28576                     }
28577                 ]
28578             }
28579         ]
28580     }
28581 });
28582
28583 /*
28584 * Licence: LGPL
28585 */
28586
28587 /**
28588  * @class Roo.bootstrap.DocumentManager
28589  * @extends Roo.bootstrap.Component
28590  * Bootstrap DocumentManager class
28591  * @cfg {String} paramName default 'imageUpload'
28592  * @cfg {String} toolTipName default 'filename'
28593  * @cfg {String} method default POST
28594  * @cfg {String} url action url
28595  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28596  * @cfg {Boolean} multiple multiple upload default true
28597  * @cfg {Number} thumbSize default 300
28598  * @cfg {String} fieldLabel
28599  * @cfg {Number} labelWidth default 4
28600  * @cfg {String} labelAlign (left|top) default left
28601  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28602 * @cfg {Number} labellg set the width of label (1-12)
28603  * @cfg {Number} labelmd set the width of label (1-12)
28604  * @cfg {Number} labelsm set the width of label (1-12)
28605  * @cfg {Number} labelxs set the width of label (1-12)
28606  * 
28607  * @constructor
28608  * Create a new DocumentManager
28609  * @param {Object} config The config object
28610  */
28611
28612 Roo.bootstrap.DocumentManager = function(config){
28613     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28614     
28615     this.files = [];
28616     this.delegates = [];
28617     
28618     this.addEvents({
28619         /**
28620          * @event initial
28621          * Fire when initial the DocumentManager
28622          * @param {Roo.bootstrap.DocumentManager} this
28623          */
28624         "initial" : true,
28625         /**
28626          * @event inspect
28627          * inspect selected file
28628          * @param {Roo.bootstrap.DocumentManager} this
28629          * @param {File} file
28630          */
28631         "inspect" : true,
28632         /**
28633          * @event exception
28634          * Fire when xhr load exception
28635          * @param {Roo.bootstrap.DocumentManager} this
28636          * @param {XMLHttpRequest} xhr
28637          */
28638         "exception" : true,
28639         /**
28640          * @event afterupload
28641          * Fire when xhr load exception
28642          * @param {Roo.bootstrap.DocumentManager} this
28643          * @param {XMLHttpRequest} xhr
28644          */
28645         "afterupload" : true,
28646         /**
28647          * @event prepare
28648          * prepare the form data
28649          * @param {Roo.bootstrap.DocumentManager} this
28650          * @param {Object} formData
28651          */
28652         "prepare" : true,
28653         /**
28654          * @event remove
28655          * Fire when remove the file
28656          * @param {Roo.bootstrap.DocumentManager} this
28657          * @param {Object} file
28658          */
28659         "remove" : true,
28660         /**
28661          * @event refresh
28662          * Fire after refresh the file
28663          * @param {Roo.bootstrap.DocumentManager} this
28664          */
28665         "refresh" : true,
28666         /**
28667          * @event click
28668          * Fire after click the image
28669          * @param {Roo.bootstrap.DocumentManager} this
28670          * @param {Object} file
28671          */
28672         "click" : true,
28673         /**
28674          * @event edit
28675          * Fire when upload a image and editable set to true
28676          * @param {Roo.bootstrap.DocumentManager} this
28677          * @param {Object} file
28678          */
28679         "edit" : true,
28680         /**
28681          * @event beforeselectfile
28682          * Fire before select file
28683          * @param {Roo.bootstrap.DocumentManager} this
28684          */
28685         "beforeselectfile" : true,
28686         /**
28687          * @event process
28688          * Fire before process file
28689          * @param {Roo.bootstrap.DocumentManager} this
28690          * @param {Object} file
28691          */
28692         "process" : true,
28693         /**
28694          * @event previewrendered
28695          * Fire when preview rendered
28696          * @param {Roo.bootstrap.DocumentManager} this
28697          * @param {Object} file
28698          */
28699         "previewrendered" : true
28700         
28701     });
28702 };
28703
28704 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28705     
28706     boxes : 0,
28707     inputName : '',
28708     thumbSize : 300,
28709     multiple : true,
28710     files : false,
28711     method : 'POST',
28712     url : '',
28713     paramName : 'imageUpload',
28714     toolTipName : 'filename',
28715     fieldLabel : '',
28716     labelWidth : 4,
28717     labelAlign : 'left',
28718     editable : true,
28719     delegates : false,
28720     xhr : false, 
28721     
28722     labellg : 0,
28723     labelmd : 0,
28724     labelsm : 0,
28725     labelxs : 0,
28726     
28727     getAutoCreate : function()
28728     {   
28729         var managerWidget = {
28730             tag : 'div',
28731             cls : 'roo-document-manager',
28732             cn : [
28733                 {
28734                     tag : 'input',
28735                     cls : 'roo-document-manager-selector',
28736                     type : 'file'
28737                 },
28738                 {
28739                     tag : 'div',
28740                     cls : 'roo-document-manager-uploader',
28741                     cn : [
28742                         {
28743                             tag : 'div',
28744                             cls : 'roo-document-manager-upload-btn',
28745                             html : '<i class="fa fa-plus"></i>'
28746                         }
28747                     ]
28748                     
28749                 }
28750             ]
28751         };
28752         
28753         var content = [
28754             {
28755                 tag : 'div',
28756                 cls : 'column col-md-12',
28757                 cn : managerWidget
28758             }
28759         ];
28760         
28761         if(this.fieldLabel.length){
28762             
28763             content = [
28764                 {
28765                     tag : 'div',
28766                     cls : 'column col-md-12',
28767                     html : this.fieldLabel
28768                 },
28769                 {
28770                     tag : 'div',
28771                     cls : 'column col-md-12',
28772                     cn : managerWidget
28773                 }
28774             ];
28775
28776             if(this.labelAlign == 'left'){
28777                 content = [
28778                     {
28779                         tag : 'div',
28780                         cls : 'column',
28781                         html : this.fieldLabel
28782                     },
28783                     {
28784                         tag : 'div',
28785                         cls : 'column',
28786                         cn : managerWidget
28787                     }
28788                 ];
28789                 
28790                 if(this.labelWidth > 12){
28791                     content[0].style = "width: " + this.labelWidth + 'px';
28792                 }
28793
28794                 if(this.labelWidth < 13 && this.labelmd == 0){
28795                     this.labelmd = this.labelWidth;
28796                 }
28797
28798                 if(this.labellg > 0){
28799                     content[0].cls += ' col-lg-' + this.labellg;
28800                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28801                 }
28802
28803                 if(this.labelmd > 0){
28804                     content[0].cls += ' col-md-' + this.labelmd;
28805                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28806                 }
28807
28808                 if(this.labelsm > 0){
28809                     content[0].cls += ' col-sm-' + this.labelsm;
28810                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28811                 }
28812
28813                 if(this.labelxs > 0){
28814                     content[0].cls += ' col-xs-' + this.labelxs;
28815                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28816                 }
28817                 
28818             }
28819         }
28820         
28821         var cfg = {
28822             tag : 'div',
28823             cls : 'row clearfix',
28824             cn : content
28825         };
28826         
28827         return cfg;
28828         
28829     },
28830     
28831     initEvents : function()
28832     {
28833         this.managerEl = this.el.select('.roo-document-manager', true).first();
28834         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28835         
28836         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28837         this.selectorEl.hide();
28838         
28839         if(this.multiple){
28840             this.selectorEl.attr('multiple', 'multiple');
28841         }
28842         
28843         this.selectorEl.on('change', this.onFileSelected, this);
28844         
28845         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28846         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28847         
28848         this.uploader.on('click', this.onUploaderClick, this);
28849         
28850         this.renderProgressDialog();
28851         
28852         var _this = this;
28853         
28854         window.addEventListener("resize", function() { _this.refresh(); } );
28855         
28856         this.fireEvent('initial', this);
28857     },
28858     
28859     renderProgressDialog : function()
28860     {
28861         var _this = this;
28862         
28863         this.progressDialog = new Roo.bootstrap.Modal({
28864             cls : 'roo-document-manager-progress-dialog',
28865             allow_close : false,
28866             title : '',
28867             buttons : [
28868                 {
28869                     name  :'cancel',
28870                     weight : 'danger',
28871                     html : 'Cancel'
28872                 }
28873             ], 
28874             listeners : { 
28875                 btnclick : function() {
28876                     _this.uploadCancel();
28877                     this.hide();
28878                 }
28879             }
28880         });
28881          
28882         this.progressDialog.render(Roo.get(document.body));
28883          
28884         this.progress = new Roo.bootstrap.Progress({
28885             cls : 'roo-document-manager-progress',
28886             active : true,
28887             striped : true
28888         });
28889         
28890         this.progress.render(this.progressDialog.getChildContainer());
28891         
28892         this.progressBar = new Roo.bootstrap.ProgressBar({
28893             cls : 'roo-document-manager-progress-bar',
28894             aria_valuenow : 0,
28895             aria_valuemin : 0,
28896             aria_valuemax : 12,
28897             panel : 'success'
28898         });
28899         
28900         this.progressBar.render(this.progress.getChildContainer());
28901     },
28902     
28903     onUploaderClick : function(e)
28904     {
28905         e.preventDefault();
28906      
28907         if(this.fireEvent('beforeselectfile', this) != false){
28908             this.selectorEl.dom.click();
28909         }
28910         
28911     },
28912     
28913     onFileSelected : function(e)
28914     {
28915         e.preventDefault();
28916         
28917         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28918             return;
28919         }
28920         
28921         Roo.each(this.selectorEl.dom.files, function(file){
28922             if(this.fireEvent('inspect', this, file) != false){
28923                 this.files.push(file);
28924             }
28925         }, this);
28926         
28927         this.queue();
28928         
28929     },
28930     
28931     queue : function()
28932     {
28933         this.selectorEl.dom.value = '';
28934         
28935         if(!this.files || !this.files.length){
28936             return;
28937         }
28938         
28939         if(this.boxes > 0 && this.files.length > this.boxes){
28940             this.files = this.files.slice(0, this.boxes);
28941         }
28942         
28943         this.uploader.show();
28944         
28945         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28946             this.uploader.hide();
28947         }
28948         
28949         var _this = this;
28950         
28951         var files = [];
28952         
28953         var docs = [];
28954         
28955         Roo.each(this.files, function(file){
28956             
28957             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28958                 var f = this.renderPreview(file);
28959                 files.push(f);
28960                 return;
28961             }
28962             
28963             if(file.type.indexOf('image') != -1){
28964                 this.delegates.push(
28965                     (function(){
28966                         _this.process(file);
28967                     }).createDelegate(this)
28968                 );
28969         
28970                 return;
28971             }
28972             
28973             docs.push(
28974                 (function(){
28975                     _this.process(file);
28976                 }).createDelegate(this)
28977             );
28978             
28979         }, this);
28980         
28981         this.files = files;
28982         
28983         this.delegates = this.delegates.concat(docs);
28984         
28985         if(!this.delegates.length){
28986             this.refresh();
28987             return;
28988         }
28989         
28990         this.progressBar.aria_valuemax = this.delegates.length;
28991         
28992         this.arrange();
28993         
28994         return;
28995     },
28996     
28997     arrange : function()
28998     {
28999         if(!this.delegates.length){
29000             this.progressDialog.hide();
29001             this.refresh();
29002             return;
29003         }
29004         
29005         var delegate = this.delegates.shift();
29006         
29007         this.progressDialog.show();
29008         
29009         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29010         
29011         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29012         
29013         delegate();
29014     },
29015     
29016     refresh : function()
29017     {
29018         this.uploader.show();
29019         
29020         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29021             this.uploader.hide();
29022         }
29023         
29024         Roo.isTouch ? this.closable(false) : this.closable(true);
29025         
29026         this.fireEvent('refresh', this);
29027     },
29028     
29029     onRemove : function(e, el, o)
29030     {
29031         e.preventDefault();
29032         
29033         this.fireEvent('remove', this, o);
29034         
29035     },
29036     
29037     remove : function(o)
29038     {
29039         var files = [];
29040         
29041         Roo.each(this.files, function(file){
29042             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29043                 files.push(file);
29044                 return;
29045             }
29046
29047             o.target.remove();
29048
29049         }, this);
29050         
29051         this.files = files;
29052         
29053         this.refresh();
29054     },
29055     
29056     clear : function()
29057     {
29058         Roo.each(this.files, function(file){
29059             if(!file.target){
29060                 return;
29061             }
29062             
29063             file.target.remove();
29064
29065         }, this);
29066         
29067         this.files = [];
29068         
29069         this.refresh();
29070     },
29071     
29072     onClick : function(e, el, o)
29073     {
29074         e.preventDefault();
29075         
29076         this.fireEvent('click', this, o);
29077         
29078     },
29079     
29080     closable : function(closable)
29081     {
29082         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29083             
29084             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29085             
29086             if(closable){
29087                 el.show();
29088                 return;
29089             }
29090             
29091             el.hide();
29092             
29093         }, this);
29094     },
29095     
29096     xhrOnLoad : function(xhr)
29097     {
29098         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29099             el.remove();
29100         }, this);
29101         
29102         if (xhr.readyState !== 4) {
29103             this.arrange();
29104             this.fireEvent('exception', this, xhr);
29105             return;
29106         }
29107
29108         var response = Roo.decode(xhr.responseText);
29109         
29110         if(!response.success){
29111             this.arrange();
29112             this.fireEvent('exception', this, xhr);
29113             return;
29114         }
29115         
29116         var file = this.renderPreview(response.data);
29117         
29118         this.files.push(file);
29119         
29120         this.arrange();
29121         
29122         this.fireEvent('afterupload', this, xhr);
29123         
29124     },
29125     
29126     xhrOnError : function(xhr)
29127     {
29128         Roo.log('xhr on error');
29129         
29130         var response = Roo.decode(xhr.responseText);
29131           
29132         Roo.log(response);
29133         
29134         this.arrange();
29135     },
29136     
29137     process : function(file)
29138     {
29139         if(this.fireEvent('process', this, file) !== false){
29140             if(this.editable && file.type.indexOf('image') != -1){
29141                 this.fireEvent('edit', this, file);
29142                 return;
29143             }
29144
29145             this.uploadStart(file, false);
29146
29147             return;
29148         }
29149         
29150     },
29151     
29152     uploadStart : function(file, crop)
29153     {
29154         this.xhr = new XMLHttpRequest();
29155         
29156         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29157             this.arrange();
29158             return;
29159         }
29160         
29161         file.xhr = this.xhr;
29162             
29163         this.managerEl.createChild({
29164             tag : 'div',
29165             cls : 'roo-document-manager-loading',
29166             cn : [
29167                 {
29168                     tag : 'div',
29169                     tooltip : file.name,
29170                     cls : 'roo-document-manager-thumb',
29171                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29172                 }
29173             ]
29174
29175         });
29176
29177         this.xhr.open(this.method, this.url, true);
29178         
29179         var headers = {
29180             "Accept": "application/json",
29181             "Cache-Control": "no-cache",
29182             "X-Requested-With": "XMLHttpRequest"
29183         };
29184         
29185         for (var headerName in headers) {
29186             var headerValue = headers[headerName];
29187             if (headerValue) {
29188                 this.xhr.setRequestHeader(headerName, headerValue);
29189             }
29190         }
29191         
29192         var _this = this;
29193         
29194         this.xhr.onload = function()
29195         {
29196             _this.xhrOnLoad(_this.xhr);
29197         }
29198         
29199         this.xhr.onerror = function()
29200         {
29201             _this.xhrOnError(_this.xhr);
29202         }
29203         
29204         var formData = new FormData();
29205
29206         formData.append('returnHTML', 'NO');
29207         
29208         if(crop){
29209             formData.append('crop', crop);
29210         }
29211         
29212         formData.append(this.paramName, file, file.name);
29213         
29214         var options = {
29215             file : file, 
29216             manually : false
29217         };
29218         
29219         if(this.fireEvent('prepare', this, formData, options) != false){
29220             
29221             if(options.manually){
29222                 return;
29223             }
29224             
29225             this.xhr.send(formData);
29226             return;
29227         };
29228         
29229         this.uploadCancel();
29230     },
29231     
29232     uploadCancel : function()
29233     {
29234         if (this.xhr) {
29235             this.xhr.abort();
29236         }
29237         
29238         this.delegates = [];
29239         
29240         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29241             el.remove();
29242         }, this);
29243         
29244         this.arrange();
29245     },
29246     
29247     renderPreview : function(file)
29248     {
29249         if(typeof(file.target) != 'undefined' && file.target){
29250             return file;
29251         }
29252         
29253         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29254         
29255         var previewEl = this.managerEl.createChild({
29256             tag : 'div',
29257             cls : 'roo-document-manager-preview',
29258             cn : [
29259                 {
29260                     tag : 'div',
29261                     tooltip : file[this.toolTipName],
29262                     cls : 'roo-document-manager-thumb',
29263                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29264                 },
29265                 {
29266                     tag : 'button',
29267                     cls : 'close',
29268                     html : '<i class="fa fa-times-circle"></i>'
29269                 }
29270             ]
29271         });
29272
29273         var close = previewEl.select('button.close', true).first();
29274
29275         close.on('click', this.onRemove, this, file);
29276
29277         file.target = previewEl;
29278
29279         var image = previewEl.select('img', true).first();
29280         
29281         var _this = this;
29282         
29283         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29284         
29285         image.on('click', this.onClick, this, file);
29286         
29287         this.fireEvent('previewrendered', this, file);
29288         
29289         return file;
29290         
29291     },
29292     
29293     onPreviewLoad : function(file, image)
29294     {
29295         if(typeof(file.target) == 'undefined' || !file.target){
29296             return;
29297         }
29298         
29299         var width = image.dom.naturalWidth || image.dom.width;
29300         var height = image.dom.naturalHeight || image.dom.height;
29301         
29302         if(width > height){
29303             file.target.addClass('wide');
29304             return;
29305         }
29306         
29307         file.target.addClass('tall');
29308         return;
29309         
29310     },
29311     
29312     uploadFromSource : function(file, crop)
29313     {
29314         this.xhr = new XMLHttpRequest();
29315         
29316         this.managerEl.createChild({
29317             tag : 'div',
29318             cls : 'roo-document-manager-loading',
29319             cn : [
29320                 {
29321                     tag : 'div',
29322                     tooltip : file.name,
29323                     cls : 'roo-document-manager-thumb',
29324                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29325                 }
29326             ]
29327
29328         });
29329
29330         this.xhr.open(this.method, this.url, true);
29331         
29332         var headers = {
29333             "Accept": "application/json",
29334             "Cache-Control": "no-cache",
29335             "X-Requested-With": "XMLHttpRequest"
29336         };
29337         
29338         for (var headerName in headers) {
29339             var headerValue = headers[headerName];
29340             if (headerValue) {
29341                 this.xhr.setRequestHeader(headerName, headerValue);
29342             }
29343         }
29344         
29345         var _this = this;
29346         
29347         this.xhr.onload = function()
29348         {
29349             _this.xhrOnLoad(_this.xhr);
29350         }
29351         
29352         this.xhr.onerror = function()
29353         {
29354             _this.xhrOnError(_this.xhr);
29355         }
29356         
29357         var formData = new FormData();
29358
29359         formData.append('returnHTML', 'NO');
29360         
29361         formData.append('crop', crop);
29362         
29363         if(typeof(file.filename) != 'undefined'){
29364             formData.append('filename', file.filename);
29365         }
29366         
29367         if(typeof(file.mimetype) != 'undefined'){
29368             formData.append('mimetype', file.mimetype);
29369         }
29370         
29371         Roo.log(formData);
29372         
29373         if(this.fireEvent('prepare', this, formData) != false){
29374             this.xhr.send(formData);
29375         };
29376     }
29377 });
29378
29379 /*
29380 * Licence: LGPL
29381 */
29382
29383 /**
29384  * @class Roo.bootstrap.DocumentViewer
29385  * @extends Roo.bootstrap.Component
29386  * Bootstrap DocumentViewer class
29387  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29388  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29389  * 
29390  * @constructor
29391  * Create a new DocumentViewer
29392  * @param {Object} config The config object
29393  */
29394
29395 Roo.bootstrap.DocumentViewer = function(config){
29396     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29397     
29398     this.addEvents({
29399         /**
29400          * @event initial
29401          * Fire after initEvent
29402          * @param {Roo.bootstrap.DocumentViewer} this
29403          */
29404         "initial" : true,
29405         /**
29406          * @event click
29407          * Fire after click
29408          * @param {Roo.bootstrap.DocumentViewer} this
29409          */
29410         "click" : true,
29411         /**
29412          * @event download
29413          * Fire after download button
29414          * @param {Roo.bootstrap.DocumentViewer} this
29415          */
29416         "download" : true,
29417         /**
29418          * @event trash
29419          * Fire after trash button
29420          * @param {Roo.bootstrap.DocumentViewer} this
29421          */
29422         "trash" : true
29423         
29424     });
29425 };
29426
29427 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29428     
29429     showDownload : true,
29430     
29431     showTrash : true,
29432     
29433     getAutoCreate : function()
29434     {
29435         var cfg = {
29436             tag : 'div',
29437             cls : 'roo-document-viewer',
29438             cn : [
29439                 {
29440                     tag : 'div',
29441                     cls : 'roo-document-viewer-body',
29442                     cn : [
29443                         {
29444                             tag : 'div',
29445                             cls : 'roo-document-viewer-thumb',
29446                             cn : [
29447                                 {
29448                                     tag : 'img',
29449                                     cls : 'roo-document-viewer-image'
29450                                 }
29451                             ]
29452                         }
29453                     ]
29454                 },
29455                 {
29456                     tag : 'div',
29457                     cls : 'roo-document-viewer-footer',
29458                     cn : {
29459                         tag : 'div',
29460                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29461                         cn : [
29462                             {
29463                                 tag : 'div',
29464                                 cls : 'btn-group roo-document-viewer-download',
29465                                 cn : [
29466                                     {
29467                                         tag : 'button',
29468                                         cls : 'btn btn-default',
29469                                         html : '<i class="fa fa-download"></i>'
29470                                     }
29471                                 ]
29472                             },
29473                             {
29474                                 tag : 'div',
29475                                 cls : 'btn-group roo-document-viewer-trash',
29476                                 cn : [
29477                                     {
29478                                         tag : 'button',
29479                                         cls : 'btn btn-default',
29480                                         html : '<i class="fa fa-trash"></i>'
29481                                     }
29482                                 ]
29483                             }
29484                         ]
29485                     }
29486                 }
29487             ]
29488         };
29489         
29490         return cfg;
29491     },
29492     
29493     initEvents : function()
29494     {
29495         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29496         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29497         
29498         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29499         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29500         
29501         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29502         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29503         
29504         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29505         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29506         
29507         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29508         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29509         
29510         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29511         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29512         
29513         this.bodyEl.on('click', this.onClick, this);
29514         this.downloadBtn.on('click', this.onDownload, this);
29515         this.trashBtn.on('click', this.onTrash, this);
29516         
29517         this.downloadBtn.hide();
29518         this.trashBtn.hide();
29519         
29520         if(this.showDownload){
29521             this.downloadBtn.show();
29522         }
29523         
29524         if(this.showTrash){
29525             this.trashBtn.show();
29526         }
29527         
29528         if(!this.showDownload && !this.showTrash) {
29529             this.footerEl.hide();
29530         }
29531         
29532     },
29533     
29534     initial : function()
29535     {
29536         this.fireEvent('initial', this);
29537         
29538     },
29539     
29540     onClick : function(e)
29541     {
29542         e.preventDefault();
29543         
29544         this.fireEvent('click', this);
29545     },
29546     
29547     onDownload : function(e)
29548     {
29549         e.preventDefault();
29550         
29551         this.fireEvent('download', this);
29552     },
29553     
29554     onTrash : function(e)
29555     {
29556         e.preventDefault();
29557         
29558         this.fireEvent('trash', this);
29559     }
29560     
29561 });
29562 /*
29563  * - LGPL
29564  *
29565  * nav progress bar
29566  * 
29567  */
29568
29569 /**
29570  * @class Roo.bootstrap.NavProgressBar
29571  * @extends Roo.bootstrap.Component
29572  * Bootstrap NavProgressBar class
29573  * 
29574  * @constructor
29575  * Create a new nav progress bar
29576  * @param {Object} config The config object
29577  */
29578
29579 Roo.bootstrap.NavProgressBar = function(config){
29580     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29581
29582     this.bullets = this.bullets || [];
29583    
29584 //    Roo.bootstrap.NavProgressBar.register(this);
29585      this.addEvents({
29586         /**
29587              * @event changed
29588              * Fires when the active item changes
29589              * @param {Roo.bootstrap.NavProgressBar} this
29590              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29591              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29592          */
29593         'changed': true
29594      });
29595     
29596 };
29597
29598 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29599     
29600     bullets : [],
29601     barItems : [],
29602     
29603     getAutoCreate : function()
29604     {
29605         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29606         
29607         cfg = {
29608             tag : 'div',
29609             cls : 'roo-navigation-bar-group',
29610             cn : [
29611                 {
29612                     tag : 'div',
29613                     cls : 'roo-navigation-top-bar'
29614                 },
29615                 {
29616                     tag : 'div',
29617                     cls : 'roo-navigation-bullets-bar',
29618                     cn : [
29619                         {
29620                             tag : 'ul',
29621                             cls : 'roo-navigation-bar'
29622                         }
29623                     ]
29624                 },
29625                 
29626                 {
29627                     tag : 'div',
29628                     cls : 'roo-navigation-bottom-bar'
29629                 }
29630             ]
29631             
29632         };
29633         
29634         return cfg;
29635         
29636     },
29637     
29638     initEvents: function() 
29639     {
29640         
29641     },
29642     
29643     onRender : function(ct, position) 
29644     {
29645         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29646         
29647         if(this.bullets.length){
29648             Roo.each(this.bullets, function(b){
29649                this.addItem(b);
29650             }, this);
29651         }
29652         
29653         this.format();
29654         
29655     },
29656     
29657     addItem : function(cfg)
29658     {
29659         var item = new Roo.bootstrap.NavProgressItem(cfg);
29660         
29661         item.parentId = this.id;
29662         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29663         
29664         if(cfg.html){
29665             var top = new Roo.bootstrap.Element({
29666                 tag : 'div',
29667                 cls : 'roo-navigation-bar-text'
29668             });
29669             
29670             var bottom = new Roo.bootstrap.Element({
29671                 tag : 'div',
29672                 cls : 'roo-navigation-bar-text'
29673             });
29674             
29675             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29676             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29677             
29678             var topText = new Roo.bootstrap.Element({
29679                 tag : 'span',
29680                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29681             });
29682             
29683             var bottomText = new Roo.bootstrap.Element({
29684                 tag : 'span',
29685                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29686             });
29687             
29688             topText.onRender(top.el, null);
29689             bottomText.onRender(bottom.el, null);
29690             
29691             item.topEl = top;
29692             item.bottomEl = bottom;
29693         }
29694         
29695         this.barItems.push(item);
29696         
29697         return item;
29698     },
29699     
29700     getActive : function()
29701     {
29702         var active = false;
29703         
29704         Roo.each(this.barItems, function(v){
29705             
29706             if (!v.isActive()) {
29707                 return;
29708             }
29709             
29710             active = v;
29711             return false;
29712             
29713         });
29714         
29715         return active;
29716     },
29717     
29718     setActiveItem : function(item)
29719     {
29720         var prev = false;
29721         
29722         Roo.each(this.barItems, function(v){
29723             if (v.rid == item.rid) {
29724                 return ;
29725             }
29726             
29727             if (v.isActive()) {
29728                 v.setActive(false);
29729                 prev = v;
29730             }
29731         });
29732
29733         item.setActive(true);
29734         
29735         this.fireEvent('changed', this, item, prev);
29736     },
29737     
29738     getBarItem: function(rid)
29739     {
29740         var ret = false;
29741         
29742         Roo.each(this.barItems, function(e) {
29743             if (e.rid != rid) {
29744                 return;
29745             }
29746             
29747             ret =  e;
29748             return false;
29749         });
29750         
29751         return ret;
29752     },
29753     
29754     indexOfItem : function(item)
29755     {
29756         var index = false;
29757         
29758         Roo.each(this.barItems, function(v, i){
29759             
29760             if (v.rid != item.rid) {
29761                 return;
29762             }
29763             
29764             index = i;
29765             return false
29766         });
29767         
29768         return index;
29769     },
29770     
29771     setActiveNext : function()
29772     {
29773         var i = this.indexOfItem(this.getActive());
29774         
29775         if (i > this.barItems.length) {
29776             return;
29777         }
29778         
29779         this.setActiveItem(this.barItems[i+1]);
29780     },
29781     
29782     setActivePrev : function()
29783     {
29784         var i = this.indexOfItem(this.getActive());
29785         
29786         if (i  < 1) {
29787             return;
29788         }
29789         
29790         this.setActiveItem(this.barItems[i-1]);
29791     },
29792     
29793     format : function()
29794     {
29795         if(!this.barItems.length){
29796             return;
29797         }
29798      
29799         var width = 100 / this.barItems.length;
29800         
29801         Roo.each(this.barItems, function(i){
29802             i.el.setStyle('width', width + '%');
29803             i.topEl.el.setStyle('width', width + '%');
29804             i.bottomEl.el.setStyle('width', width + '%');
29805         }, this);
29806         
29807     }
29808     
29809 });
29810 /*
29811  * - LGPL
29812  *
29813  * Nav Progress Item
29814  * 
29815  */
29816
29817 /**
29818  * @class Roo.bootstrap.NavProgressItem
29819  * @extends Roo.bootstrap.Component
29820  * Bootstrap NavProgressItem class
29821  * @cfg {String} rid the reference id
29822  * @cfg {Boolean} active (true|false) Is item active default false
29823  * @cfg {Boolean} disabled (true|false) Is item active default false
29824  * @cfg {String} html
29825  * @cfg {String} position (top|bottom) text position default bottom
29826  * @cfg {String} icon show icon instead of number
29827  * 
29828  * @constructor
29829  * Create a new NavProgressItem
29830  * @param {Object} config The config object
29831  */
29832 Roo.bootstrap.NavProgressItem = function(config){
29833     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29834     this.addEvents({
29835         // raw events
29836         /**
29837          * @event click
29838          * The raw click event for the entire grid.
29839          * @param {Roo.bootstrap.NavProgressItem} this
29840          * @param {Roo.EventObject} e
29841          */
29842         "click" : true
29843     });
29844    
29845 };
29846
29847 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29848     
29849     rid : '',
29850     active : false,
29851     disabled : false,
29852     html : '',
29853     position : 'bottom',
29854     icon : false,
29855     
29856     getAutoCreate : function()
29857     {
29858         var iconCls = 'roo-navigation-bar-item-icon';
29859         
29860         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29861         
29862         var cfg = {
29863             tag: 'li',
29864             cls: 'roo-navigation-bar-item',
29865             cn : [
29866                 {
29867                     tag : 'i',
29868                     cls : iconCls
29869                 }
29870             ]
29871         };
29872         
29873         if(this.active){
29874             cfg.cls += ' active';
29875         }
29876         if(this.disabled){
29877             cfg.cls += ' disabled';
29878         }
29879         
29880         return cfg;
29881     },
29882     
29883     disable : function()
29884     {
29885         this.setDisabled(true);
29886     },
29887     
29888     enable : function()
29889     {
29890         this.setDisabled(false);
29891     },
29892     
29893     initEvents: function() 
29894     {
29895         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29896         
29897         this.iconEl.on('click', this.onClick, this);
29898     },
29899     
29900     onClick : function(e)
29901     {
29902         e.preventDefault();
29903         
29904         if(this.disabled){
29905             return;
29906         }
29907         
29908         if(this.fireEvent('click', this, e) === false){
29909             return;
29910         };
29911         
29912         this.parent().setActiveItem(this);
29913     },
29914     
29915     isActive: function () 
29916     {
29917         return this.active;
29918     },
29919     
29920     setActive : function(state)
29921     {
29922         if(this.active == state){
29923             return;
29924         }
29925         
29926         this.active = state;
29927         
29928         if (state) {
29929             this.el.addClass('active');
29930             return;
29931         }
29932         
29933         this.el.removeClass('active');
29934         
29935         return;
29936     },
29937     
29938     setDisabled : function(state)
29939     {
29940         if(this.disabled == state){
29941             return;
29942         }
29943         
29944         this.disabled = state;
29945         
29946         if (state) {
29947             this.el.addClass('disabled');
29948             return;
29949         }
29950         
29951         this.el.removeClass('disabled');
29952     },
29953     
29954     tooltipEl : function()
29955     {
29956         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29957     }
29958 });
29959  
29960
29961  /*
29962  * - LGPL
29963  *
29964  * FieldLabel
29965  * 
29966  */
29967
29968 /**
29969  * @class Roo.bootstrap.FieldLabel
29970  * @extends Roo.bootstrap.Component
29971  * Bootstrap FieldLabel class
29972  * @cfg {String} html contents of the element
29973  * @cfg {String} tag tag of the element default label
29974  * @cfg {String} cls class of the element
29975  * @cfg {String} target label target 
29976  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29977  * @cfg {String} invalidClass default "text-warning"
29978  * @cfg {String} validClass default "text-success"
29979  * @cfg {String} iconTooltip default "This field is required"
29980  * @cfg {String} indicatorpos (left|right) default left
29981  * 
29982  * @constructor
29983  * Create a new FieldLabel
29984  * @param {Object} config The config object
29985  */
29986
29987 Roo.bootstrap.FieldLabel = function(config){
29988     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29989     
29990     this.addEvents({
29991             /**
29992              * @event invalid
29993              * Fires after the field has been marked as invalid.
29994              * @param {Roo.form.FieldLabel} this
29995              * @param {String} msg The validation message
29996              */
29997             invalid : true,
29998             /**
29999              * @event valid
30000              * Fires after the field has been validated with no errors.
30001              * @param {Roo.form.FieldLabel} this
30002              */
30003             valid : true
30004         });
30005 };
30006
30007 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30008     
30009     tag: 'label',
30010     cls: '',
30011     html: '',
30012     target: '',
30013     allowBlank : true,
30014     invalidClass : 'has-warning',
30015     validClass : 'has-success',
30016     iconTooltip : 'This field is required',
30017     indicatorpos : 'left',
30018     
30019     getAutoCreate : function(){
30020         
30021         var cls = "";
30022         if (!this.allowBlank) {
30023             cls  = "visible";
30024         }
30025         
30026         var cfg = {
30027             tag : this.tag,
30028             cls : 'roo-bootstrap-field-label ' + this.cls,
30029             for : this.target,
30030             cn : [
30031                 {
30032                     tag : 'i',
30033                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30034                     tooltip : this.iconTooltip
30035                 },
30036                 {
30037                     tag : 'span',
30038                     html : this.html
30039                 }
30040             ] 
30041         };
30042         
30043         if(this.indicatorpos == 'right'){
30044             var cfg = {
30045                 tag : this.tag,
30046                 cls : 'roo-bootstrap-field-label ' + this.cls,
30047                 for : this.target,
30048                 cn : [
30049                     {
30050                         tag : 'span',
30051                         html : this.html
30052                     },
30053                     {
30054                         tag : 'i',
30055                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30056                         tooltip : this.iconTooltip
30057                     }
30058                 ] 
30059             };
30060         }
30061         
30062         return cfg;
30063     },
30064     
30065     initEvents: function() 
30066     {
30067         Roo.bootstrap.Element.superclass.initEvents.call(this);
30068         
30069         this.indicator = this.indicatorEl();
30070         
30071         if(this.indicator){
30072             this.indicator.removeClass('visible');
30073             this.indicator.addClass('invisible');
30074         }
30075         
30076         Roo.bootstrap.FieldLabel.register(this);
30077     },
30078     
30079     indicatorEl : function()
30080     {
30081         var indicator = this.el.select('i.roo-required-indicator',true).first();
30082         
30083         if(!indicator){
30084             return false;
30085         }
30086         
30087         return indicator;
30088         
30089     },
30090     
30091     /**
30092      * Mark this field as valid
30093      */
30094     markValid : function()
30095     {
30096         if(this.indicator){
30097             this.indicator.removeClass('visible');
30098             this.indicator.addClass('invisible');
30099         }
30100         
30101         this.el.removeClass(this.invalidClass);
30102         
30103         this.el.addClass(this.validClass);
30104         
30105         this.fireEvent('valid', this);
30106     },
30107     
30108     /**
30109      * Mark this field as invalid
30110      * @param {String} msg The validation message
30111      */
30112     markInvalid : function(msg)
30113     {
30114         if(this.indicator){
30115             this.indicator.removeClass('invisible');
30116             this.indicator.addClass('visible');
30117         }
30118         
30119         this.el.removeClass(this.validClass);
30120         
30121         this.el.addClass(this.invalidClass);
30122         
30123         this.fireEvent('invalid', this, msg);
30124     }
30125     
30126    
30127 });
30128
30129 Roo.apply(Roo.bootstrap.FieldLabel, {
30130     
30131     groups: {},
30132     
30133      /**
30134     * register a FieldLabel Group
30135     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30136     */
30137     register : function(label)
30138     {
30139         if(this.groups.hasOwnProperty(label.target)){
30140             return;
30141         }
30142      
30143         this.groups[label.target] = label;
30144         
30145     },
30146     /**
30147     * fetch a FieldLabel Group based on the target
30148     * @param {string} target
30149     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30150     */
30151     get: function(target) {
30152         if (typeof(this.groups[target]) == 'undefined') {
30153             return false;
30154         }
30155         
30156         return this.groups[target] ;
30157     }
30158 });
30159
30160  
30161
30162  /*
30163  * - LGPL
30164  *
30165  * page DateSplitField.
30166  * 
30167  */
30168
30169
30170 /**
30171  * @class Roo.bootstrap.DateSplitField
30172  * @extends Roo.bootstrap.Component
30173  * Bootstrap DateSplitField class
30174  * @cfg {string} fieldLabel - the label associated
30175  * @cfg {Number} labelWidth set the width of label (0-12)
30176  * @cfg {String} labelAlign (top|left)
30177  * @cfg {Boolean} dayAllowBlank (true|false) default false
30178  * @cfg {Boolean} monthAllowBlank (true|false) default false
30179  * @cfg {Boolean} yearAllowBlank (true|false) default false
30180  * @cfg {string} dayPlaceholder 
30181  * @cfg {string} monthPlaceholder
30182  * @cfg {string} yearPlaceholder
30183  * @cfg {string} dayFormat default 'd'
30184  * @cfg {string} monthFormat default 'm'
30185  * @cfg {string} yearFormat default 'Y'
30186  * @cfg {Number} labellg set the width of label (1-12)
30187  * @cfg {Number} labelmd set the width of label (1-12)
30188  * @cfg {Number} labelsm set the width of label (1-12)
30189  * @cfg {Number} labelxs set the width of label (1-12)
30190
30191  *     
30192  * @constructor
30193  * Create a new DateSplitField
30194  * @param {Object} config The config object
30195  */
30196
30197 Roo.bootstrap.DateSplitField = function(config){
30198     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30199     
30200     this.addEvents({
30201         // raw events
30202          /**
30203          * @event years
30204          * getting the data of years
30205          * @param {Roo.bootstrap.DateSplitField} this
30206          * @param {Object} years
30207          */
30208         "years" : true,
30209         /**
30210          * @event days
30211          * getting the data of days
30212          * @param {Roo.bootstrap.DateSplitField} this
30213          * @param {Object} days
30214          */
30215         "days" : true,
30216         /**
30217          * @event invalid
30218          * Fires after the field has been marked as invalid.
30219          * @param {Roo.form.Field} this
30220          * @param {String} msg The validation message
30221          */
30222         invalid : true,
30223        /**
30224          * @event valid
30225          * Fires after the field has been validated with no errors.
30226          * @param {Roo.form.Field} this
30227          */
30228         valid : true
30229     });
30230 };
30231
30232 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30233     
30234     fieldLabel : '',
30235     labelAlign : 'top',
30236     labelWidth : 3,
30237     dayAllowBlank : false,
30238     monthAllowBlank : false,
30239     yearAllowBlank : false,
30240     dayPlaceholder : '',
30241     monthPlaceholder : '',
30242     yearPlaceholder : '',
30243     dayFormat : 'd',
30244     monthFormat : 'm',
30245     yearFormat : 'Y',
30246     isFormField : true,
30247     labellg : 0,
30248     labelmd : 0,
30249     labelsm : 0,
30250     labelxs : 0,
30251     
30252     getAutoCreate : function()
30253     {
30254         var cfg = {
30255             tag : 'div',
30256             cls : 'row roo-date-split-field-group',
30257             cn : [
30258                 {
30259                     tag : 'input',
30260                     type : 'hidden',
30261                     cls : 'form-hidden-field roo-date-split-field-group-value',
30262                     name : this.name
30263                 }
30264             ]
30265         };
30266         
30267         var labelCls = 'col-md-12';
30268         var contentCls = 'col-md-4';
30269         
30270         if(this.fieldLabel){
30271             
30272             var label = {
30273                 tag : 'div',
30274                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30275                 cn : [
30276                     {
30277                         tag : 'label',
30278                         html : this.fieldLabel
30279                     }
30280                 ]
30281             };
30282             
30283             if(this.labelAlign == 'left'){
30284             
30285                 if(this.labelWidth > 12){
30286                     label.style = "width: " + this.labelWidth + 'px';
30287                 }
30288
30289                 if(this.labelWidth < 13 && this.labelmd == 0){
30290                     this.labelmd = this.labelWidth;
30291                 }
30292
30293                 if(this.labellg > 0){
30294                     labelCls = ' col-lg-' + this.labellg;
30295                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30296                 }
30297
30298                 if(this.labelmd > 0){
30299                     labelCls = ' col-md-' + this.labelmd;
30300                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30301                 }
30302
30303                 if(this.labelsm > 0){
30304                     labelCls = ' col-sm-' + this.labelsm;
30305                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30306                 }
30307
30308                 if(this.labelxs > 0){
30309                     labelCls = ' col-xs-' + this.labelxs;
30310                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30311                 }
30312             }
30313             
30314             label.cls += ' ' + labelCls;
30315             
30316             cfg.cn.push(label);
30317         }
30318         
30319         Roo.each(['day', 'month', 'year'], function(t){
30320             cfg.cn.push({
30321                 tag : 'div',
30322                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30323             });
30324         }, this);
30325         
30326         return cfg;
30327     },
30328     
30329     inputEl: function ()
30330     {
30331         return this.el.select('.roo-date-split-field-group-value', true).first();
30332     },
30333     
30334     onRender : function(ct, position) 
30335     {
30336         var _this = this;
30337         
30338         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30339         
30340         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30341         
30342         this.dayField = new Roo.bootstrap.ComboBox({
30343             allowBlank : this.dayAllowBlank,
30344             alwaysQuery : true,
30345             displayField : 'value',
30346             editable : false,
30347             fieldLabel : '',
30348             forceSelection : true,
30349             mode : 'local',
30350             placeholder : this.dayPlaceholder,
30351             selectOnFocus : true,
30352             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30353             triggerAction : 'all',
30354             typeAhead : true,
30355             valueField : 'value',
30356             store : new Roo.data.SimpleStore({
30357                 data : (function() {    
30358                     var days = [];
30359                     _this.fireEvent('days', _this, days);
30360                     return days;
30361                 })(),
30362                 fields : [ 'value' ]
30363             }),
30364             listeners : {
30365                 select : function (_self, record, index)
30366                 {
30367                     _this.setValue(_this.getValue());
30368                 }
30369             }
30370         });
30371
30372         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30373         
30374         this.monthField = new Roo.bootstrap.MonthField({
30375             after : '<i class=\"fa fa-calendar\"></i>',
30376             allowBlank : this.monthAllowBlank,
30377             placeholder : this.monthPlaceholder,
30378             readOnly : true,
30379             listeners : {
30380                 render : function (_self)
30381                 {
30382                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30383                         e.preventDefault();
30384                         _self.focus();
30385                     });
30386                 },
30387                 select : function (_self, oldvalue, newvalue)
30388                 {
30389                     _this.setValue(_this.getValue());
30390                 }
30391             }
30392         });
30393         
30394         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30395         
30396         this.yearField = new Roo.bootstrap.ComboBox({
30397             allowBlank : this.yearAllowBlank,
30398             alwaysQuery : true,
30399             displayField : 'value',
30400             editable : false,
30401             fieldLabel : '',
30402             forceSelection : true,
30403             mode : 'local',
30404             placeholder : this.yearPlaceholder,
30405             selectOnFocus : true,
30406             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30407             triggerAction : 'all',
30408             typeAhead : true,
30409             valueField : 'value',
30410             store : new Roo.data.SimpleStore({
30411                 data : (function() {
30412                     var years = [];
30413                     _this.fireEvent('years', _this, years);
30414                     return years;
30415                 })(),
30416                 fields : [ 'value' ]
30417             }),
30418             listeners : {
30419                 select : function (_self, record, index)
30420                 {
30421                     _this.setValue(_this.getValue());
30422                 }
30423             }
30424         });
30425
30426         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30427     },
30428     
30429     setValue : function(v, format)
30430     {
30431         this.inputEl.dom.value = v;
30432         
30433         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30434         
30435         var d = Date.parseDate(v, f);
30436         
30437         if(!d){
30438             this.validate();
30439             return;
30440         }
30441         
30442         this.setDay(d.format(this.dayFormat));
30443         this.setMonth(d.format(this.monthFormat));
30444         this.setYear(d.format(this.yearFormat));
30445         
30446         this.validate();
30447         
30448         return;
30449     },
30450     
30451     setDay : function(v)
30452     {
30453         this.dayField.setValue(v);
30454         this.inputEl.dom.value = this.getValue();
30455         this.validate();
30456         return;
30457     },
30458     
30459     setMonth : function(v)
30460     {
30461         this.monthField.setValue(v, true);
30462         this.inputEl.dom.value = this.getValue();
30463         this.validate();
30464         return;
30465     },
30466     
30467     setYear : function(v)
30468     {
30469         this.yearField.setValue(v);
30470         this.inputEl.dom.value = this.getValue();
30471         this.validate();
30472         return;
30473     },
30474     
30475     getDay : function()
30476     {
30477         return this.dayField.getValue();
30478     },
30479     
30480     getMonth : function()
30481     {
30482         return this.monthField.getValue();
30483     },
30484     
30485     getYear : function()
30486     {
30487         return this.yearField.getValue();
30488     },
30489     
30490     getValue : function()
30491     {
30492         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30493         
30494         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30495         
30496         return date;
30497     },
30498     
30499     reset : function()
30500     {
30501         this.setDay('');
30502         this.setMonth('');
30503         this.setYear('');
30504         this.inputEl.dom.value = '';
30505         this.validate();
30506         return;
30507     },
30508     
30509     validate : function()
30510     {
30511         var d = this.dayField.validate();
30512         var m = this.monthField.validate();
30513         var y = this.yearField.validate();
30514         
30515         var valid = true;
30516         
30517         if(
30518                 (!this.dayAllowBlank && !d) ||
30519                 (!this.monthAllowBlank && !m) ||
30520                 (!this.yearAllowBlank && !y)
30521         ){
30522             valid = false;
30523         }
30524         
30525         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30526             return valid;
30527         }
30528         
30529         if(valid){
30530             this.markValid();
30531             return valid;
30532         }
30533         
30534         this.markInvalid();
30535         
30536         return valid;
30537     },
30538     
30539     markValid : function()
30540     {
30541         
30542         var label = this.el.select('label', true).first();
30543         var icon = this.el.select('i.fa-star', true).first();
30544
30545         if(label && icon){
30546             icon.remove();
30547         }
30548         
30549         this.fireEvent('valid', this);
30550     },
30551     
30552      /**
30553      * Mark this field as invalid
30554      * @param {String} msg The validation message
30555      */
30556     markInvalid : function(msg)
30557     {
30558         
30559         var label = this.el.select('label', true).first();
30560         var icon = this.el.select('i.fa-star', true).first();
30561
30562         if(label && !icon){
30563             this.el.select('.roo-date-split-field-label', true).createChild({
30564                 tag : 'i',
30565                 cls : 'text-danger fa fa-lg fa-star',
30566                 tooltip : 'This field is required',
30567                 style : 'margin-right:5px;'
30568             }, label, true);
30569         }
30570         
30571         this.fireEvent('invalid', this, msg);
30572     },
30573     
30574     clearInvalid : function()
30575     {
30576         var label = this.el.select('label', true).first();
30577         var icon = this.el.select('i.fa-star', true).first();
30578
30579         if(label && icon){
30580             icon.remove();
30581         }
30582         
30583         this.fireEvent('valid', this);
30584     },
30585     
30586     getName: function()
30587     {
30588         return this.name;
30589     }
30590     
30591 });
30592
30593  /**
30594  *
30595  * This is based on 
30596  * http://masonry.desandro.com
30597  *
30598  * The idea is to render all the bricks based on vertical width...
30599  *
30600  * The original code extends 'outlayer' - we might need to use that....
30601  * 
30602  */
30603
30604
30605 /**
30606  * @class Roo.bootstrap.LayoutMasonry
30607  * @extends Roo.bootstrap.Component
30608  * Bootstrap Layout Masonry class
30609  * 
30610  * @constructor
30611  * Create a new Element
30612  * @param {Object} config The config object
30613  */
30614
30615 Roo.bootstrap.LayoutMasonry = function(config){
30616     
30617     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30618     
30619     this.bricks = [];
30620     
30621     Roo.bootstrap.LayoutMasonry.register(this);
30622     
30623     this.addEvents({
30624         // raw events
30625         /**
30626          * @event layout
30627          * Fire after layout the items
30628          * @param {Roo.bootstrap.LayoutMasonry} this
30629          * @param {Roo.EventObject} e
30630          */
30631         "layout" : true
30632     });
30633     
30634 };
30635
30636 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30637     
30638     /**
30639      * @cfg {Boolean} isLayoutInstant = no animation?
30640      */   
30641     isLayoutInstant : false, // needed?
30642    
30643     /**
30644      * @cfg {Number} boxWidth  width of the columns
30645      */   
30646     boxWidth : 450,
30647     
30648       /**
30649      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30650      */   
30651     boxHeight : 0,
30652     
30653     /**
30654      * @cfg {Number} padWidth padding below box..
30655      */   
30656     padWidth : 10, 
30657     
30658     /**
30659      * @cfg {Number} gutter gutter width..
30660      */   
30661     gutter : 10,
30662     
30663      /**
30664      * @cfg {Number} maxCols maximum number of columns
30665      */   
30666     
30667     maxCols: 0,
30668     
30669     /**
30670      * @cfg {Boolean} isAutoInitial defalut true
30671      */   
30672     isAutoInitial : true, 
30673     
30674     containerWidth: 0,
30675     
30676     /**
30677      * @cfg {Boolean} isHorizontal defalut false
30678      */   
30679     isHorizontal : false, 
30680
30681     currentSize : null,
30682     
30683     tag: 'div',
30684     
30685     cls: '',
30686     
30687     bricks: null, //CompositeElement
30688     
30689     cols : 1,
30690     
30691     _isLayoutInited : false,
30692     
30693 //    isAlternative : false, // only use for vertical layout...
30694     
30695     /**
30696      * @cfg {Number} alternativePadWidth padding below box..
30697      */   
30698     alternativePadWidth : 50,
30699     
30700     selectedBrick : [],
30701     
30702     getAutoCreate : function(){
30703         
30704         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30705         
30706         var cfg = {
30707             tag: this.tag,
30708             cls: 'blog-masonary-wrapper ' + this.cls,
30709             cn : {
30710                 cls : 'mas-boxes masonary'
30711             }
30712         };
30713         
30714         return cfg;
30715     },
30716     
30717     getChildContainer: function( )
30718     {
30719         if (this.boxesEl) {
30720             return this.boxesEl;
30721         }
30722         
30723         this.boxesEl = this.el.select('.mas-boxes').first();
30724         
30725         return this.boxesEl;
30726     },
30727     
30728     
30729     initEvents : function()
30730     {
30731         var _this = this;
30732         
30733         if(this.isAutoInitial){
30734             Roo.log('hook children rendered');
30735             this.on('childrenrendered', function() {
30736                 Roo.log('children rendered');
30737                 _this.initial();
30738             } ,this);
30739         }
30740     },
30741     
30742     initial : function()
30743     {
30744         this.selectedBrick = [];
30745         
30746         this.currentSize = this.el.getBox(true);
30747         
30748         Roo.EventManager.onWindowResize(this.resize, this); 
30749
30750         if(!this.isAutoInitial){
30751             this.layout();
30752             return;
30753         }
30754         
30755         this.layout();
30756         
30757         return;
30758         //this.layout.defer(500,this);
30759         
30760     },
30761     
30762     resize : function()
30763     {
30764         var cs = this.el.getBox(true);
30765         
30766         if (
30767                 this.currentSize.width == cs.width && 
30768                 this.currentSize.x == cs.x && 
30769                 this.currentSize.height == cs.height && 
30770                 this.currentSize.y == cs.y 
30771         ) {
30772             Roo.log("no change in with or X or Y");
30773             return;
30774         }
30775         
30776         this.currentSize = cs;
30777         
30778         this.layout();
30779         
30780     },
30781     
30782     layout : function()
30783     {   
30784         this._resetLayout();
30785         
30786         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30787         
30788         this.layoutItems( isInstant );
30789       
30790         this._isLayoutInited = true;
30791         
30792         this.fireEvent('layout', this);
30793         
30794     },
30795     
30796     _resetLayout : function()
30797     {
30798         if(this.isHorizontal){
30799             this.horizontalMeasureColumns();
30800             return;
30801         }
30802         
30803         this.verticalMeasureColumns();
30804         
30805     },
30806     
30807     verticalMeasureColumns : function()
30808     {
30809         this.getContainerWidth();
30810         
30811 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30812 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30813 //            return;
30814 //        }
30815         
30816         var boxWidth = this.boxWidth + this.padWidth;
30817         
30818         if(this.containerWidth < this.boxWidth){
30819             boxWidth = this.containerWidth
30820         }
30821         
30822         var containerWidth = this.containerWidth;
30823         
30824         var cols = Math.floor(containerWidth / boxWidth);
30825         
30826         this.cols = Math.max( cols, 1 );
30827         
30828         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30829         
30830         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30831         
30832         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30833         
30834         this.colWidth = boxWidth + avail - this.padWidth;
30835         
30836         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30837         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30838     },
30839     
30840     horizontalMeasureColumns : function()
30841     {
30842         this.getContainerWidth();
30843         
30844         var boxWidth = this.boxWidth;
30845         
30846         if(this.containerWidth < boxWidth){
30847             boxWidth = this.containerWidth;
30848         }
30849         
30850         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30851         
30852         this.el.setHeight(boxWidth);
30853         
30854     },
30855     
30856     getContainerWidth : function()
30857     {
30858         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30859     },
30860     
30861     layoutItems : function( isInstant )
30862     {
30863         Roo.log(this.bricks);
30864         
30865         var items = Roo.apply([], this.bricks);
30866         
30867         if(this.isHorizontal){
30868             this._horizontalLayoutItems( items , isInstant );
30869             return;
30870         }
30871         
30872 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30873 //            this._verticalAlternativeLayoutItems( items , isInstant );
30874 //            return;
30875 //        }
30876         
30877         this._verticalLayoutItems( items , isInstant );
30878         
30879     },
30880     
30881     _verticalLayoutItems : function ( items , isInstant)
30882     {
30883         if ( !items || !items.length ) {
30884             return;
30885         }
30886         
30887         var standard = [
30888             ['xs', 'xs', 'xs', 'tall'],
30889             ['xs', 'xs', 'tall'],
30890             ['xs', 'xs', 'sm'],
30891             ['xs', 'xs', 'xs'],
30892             ['xs', 'tall'],
30893             ['xs', 'sm'],
30894             ['xs', 'xs'],
30895             ['xs'],
30896             
30897             ['sm', 'xs', 'xs'],
30898             ['sm', 'xs'],
30899             ['sm'],
30900             
30901             ['tall', 'xs', 'xs', 'xs'],
30902             ['tall', 'xs', 'xs'],
30903             ['tall', 'xs'],
30904             ['tall']
30905             
30906         ];
30907         
30908         var queue = [];
30909         
30910         var boxes = [];
30911         
30912         var box = [];
30913         
30914         Roo.each(items, function(item, k){
30915             
30916             switch (item.size) {
30917                 // these layouts take up a full box,
30918                 case 'md' :
30919                 case 'md-left' :
30920                 case 'md-right' :
30921                 case 'wide' :
30922                     
30923                     if(box.length){
30924                         boxes.push(box);
30925                         box = [];
30926                     }
30927                     
30928                     boxes.push([item]);
30929                     
30930                     break;
30931                     
30932                 case 'xs' :
30933                 case 'sm' :
30934                 case 'tall' :
30935                     
30936                     box.push(item);
30937                     
30938                     break;
30939                 default :
30940                     break;
30941                     
30942             }
30943             
30944         }, this);
30945         
30946         if(box.length){
30947             boxes.push(box);
30948             box = [];
30949         }
30950         
30951         var filterPattern = function(box, length)
30952         {
30953             if(!box.length){
30954                 return;
30955             }
30956             
30957             var match = false;
30958             
30959             var pattern = box.slice(0, length);
30960             
30961             var format = [];
30962             
30963             Roo.each(pattern, function(i){
30964                 format.push(i.size);
30965             }, this);
30966             
30967             Roo.each(standard, function(s){
30968                 
30969                 if(String(s) != String(format)){
30970                     return;
30971                 }
30972                 
30973                 match = true;
30974                 return false;
30975                 
30976             }, this);
30977             
30978             if(!match && length == 1){
30979                 return;
30980             }
30981             
30982             if(!match){
30983                 filterPattern(box, length - 1);
30984                 return;
30985             }
30986                 
30987             queue.push(pattern);
30988
30989             box = box.slice(length, box.length);
30990
30991             filterPattern(box, 4);
30992
30993             return;
30994             
30995         }
30996         
30997         Roo.each(boxes, function(box, k){
30998             
30999             if(!box.length){
31000                 return;
31001             }
31002             
31003             if(box.length == 1){
31004                 queue.push(box);
31005                 return;
31006             }
31007             
31008             filterPattern(box, 4);
31009             
31010         }, this);
31011         
31012         this._processVerticalLayoutQueue( queue, isInstant );
31013         
31014     },
31015     
31016 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31017 //    {
31018 //        if ( !items || !items.length ) {
31019 //            return;
31020 //        }
31021 //
31022 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31023 //        
31024 //    },
31025     
31026     _horizontalLayoutItems : function ( items , isInstant)
31027     {
31028         if ( !items || !items.length || items.length < 3) {
31029             return;
31030         }
31031         
31032         items.reverse();
31033         
31034         var eItems = items.slice(0, 3);
31035         
31036         items = items.slice(3, items.length);
31037         
31038         var standard = [
31039             ['xs', 'xs', 'xs', 'wide'],
31040             ['xs', 'xs', 'wide'],
31041             ['xs', 'xs', 'sm'],
31042             ['xs', 'xs', 'xs'],
31043             ['xs', 'wide'],
31044             ['xs', 'sm'],
31045             ['xs', 'xs'],
31046             ['xs'],
31047             
31048             ['sm', 'xs', 'xs'],
31049             ['sm', 'xs'],
31050             ['sm'],
31051             
31052             ['wide', 'xs', 'xs', 'xs'],
31053             ['wide', 'xs', 'xs'],
31054             ['wide', 'xs'],
31055             ['wide'],
31056             
31057             ['wide-thin']
31058         ];
31059         
31060         var queue = [];
31061         
31062         var boxes = [];
31063         
31064         var box = [];
31065         
31066         Roo.each(items, function(item, k){
31067             
31068             switch (item.size) {
31069                 case 'md' :
31070                 case 'md-left' :
31071                 case 'md-right' :
31072                 case 'tall' :
31073                     
31074                     if(box.length){
31075                         boxes.push(box);
31076                         box = [];
31077                     }
31078                     
31079                     boxes.push([item]);
31080                     
31081                     break;
31082                     
31083                 case 'xs' :
31084                 case 'sm' :
31085                 case 'wide' :
31086                 case 'wide-thin' :
31087                     
31088                     box.push(item);
31089                     
31090                     break;
31091                 default :
31092                     break;
31093                     
31094             }
31095             
31096         }, this);
31097         
31098         if(box.length){
31099             boxes.push(box);
31100             box = [];
31101         }
31102         
31103         var filterPattern = function(box, length)
31104         {
31105             if(!box.length){
31106                 return;
31107             }
31108             
31109             var match = false;
31110             
31111             var pattern = box.slice(0, length);
31112             
31113             var format = [];
31114             
31115             Roo.each(pattern, function(i){
31116                 format.push(i.size);
31117             }, this);
31118             
31119             Roo.each(standard, function(s){
31120                 
31121                 if(String(s) != String(format)){
31122                     return;
31123                 }
31124                 
31125                 match = true;
31126                 return false;
31127                 
31128             }, this);
31129             
31130             if(!match && length == 1){
31131                 return;
31132             }
31133             
31134             if(!match){
31135                 filterPattern(box, length - 1);
31136                 return;
31137             }
31138                 
31139             queue.push(pattern);
31140
31141             box = box.slice(length, box.length);
31142
31143             filterPattern(box, 4);
31144
31145             return;
31146             
31147         }
31148         
31149         Roo.each(boxes, function(box, k){
31150             
31151             if(!box.length){
31152                 return;
31153             }
31154             
31155             if(box.length == 1){
31156                 queue.push(box);
31157                 return;
31158             }
31159             
31160             filterPattern(box, 4);
31161             
31162         }, this);
31163         
31164         
31165         var prune = [];
31166         
31167         var pos = this.el.getBox(true);
31168         
31169         var minX = pos.x;
31170         
31171         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31172         
31173         var hit_end = false;
31174         
31175         Roo.each(queue, function(box){
31176             
31177             if(hit_end){
31178                 
31179                 Roo.each(box, function(b){
31180                 
31181                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31182                     b.el.hide();
31183
31184                 }, this);
31185
31186                 return;
31187             }
31188             
31189             var mx = 0;
31190             
31191             Roo.each(box, function(b){
31192                 
31193                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31194                 b.el.show();
31195
31196                 mx = Math.max(mx, b.x);
31197                 
31198             }, this);
31199             
31200             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31201             
31202             if(maxX < minX){
31203                 
31204                 Roo.each(box, function(b){
31205                 
31206                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31207                     b.el.hide();
31208                     
31209                 }, this);
31210                 
31211                 hit_end = true;
31212                 
31213                 return;
31214             }
31215             
31216             prune.push(box);
31217             
31218         }, this);
31219         
31220         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31221     },
31222     
31223     /** Sets position of item in DOM
31224     * @param {Element} item
31225     * @param {Number} x - horizontal position
31226     * @param {Number} y - vertical position
31227     * @param {Boolean} isInstant - disables transitions
31228     */
31229     _processVerticalLayoutQueue : function( queue, isInstant )
31230     {
31231         var pos = this.el.getBox(true);
31232         var x = pos.x;
31233         var y = pos.y;
31234         var maxY = [];
31235         
31236         for (var i = 0; i < this.cols; i++){
31237             maxY[i] = pos.y;
31238         }
31239         
31240         Roo.each(queue, function(box, k){
31241             
31242             var col = k % this.cols;
31243             
31244             Roo.each(box, function(b,kk){
31245                 
31246                 b.el.position('absolute');
31247                 
31248                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31249                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31250                 
31251                 if(b.size == 'md-left' || b.size == 'md-right'){
31252                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31253                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31254                 }
31255                 
31256                 b.el.setWidth(width);
31257                 b.el.setHeight(height);
31258                 // iframe?
31259                 b.el.select('iframe',true).setSize(width,height);
31260                 
31261             }, this);
31262             
31263             for (var i = 0; i < this.cols; i++){
31264                 
31265                 if(maxY[i] < maxY[col]){
31266                     col = i;
31267                     continue;
31268                 }
31269                 
31270                 col = Math.min(col, i);
31271                 
31272             }
31273             
31274             x = pos.x + col * (this.colWidth + this.padWidth);
31275             
31276             y = maxY[col];
31277             
31278             var positions = [];
31279             
31280             switch (box.length){
31281                 case 1 :
31282                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31283                     break;
31284                 case 2 :
31285                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31286                     break;
31287                 case 3 :
31288                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31289                     break;
31290                 case 4 :
31291                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31292                     break;
31293                 default :
31294                     break;
31295             }
31296             
31297             Roo.each(box, function(b,kk){
31298                 
31299                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31300                 
31301                 var sz = b.el.getSize();
31302                 
31303                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31304                 
31305             }, this);
31306             
31307         }, this);
31308         
31309         var mY = 0;
31310         
31311         for (var i = 0; i < this.cols; i++){
31312             mY = Math.max(mY, maxY[i]);
31313         }
31314         
31315         this.el.setHeight(mY - pos.y);
31316         
31317     },
31318     
31319 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31320 //    {
31321 //        var pos = this.el.getBox(true);
31322 //        var x = pos.x;
31323 //        var y = pos.y;
31324 //        var maxX = pos.right;
31325 //        
31326 //        var maxHeight = 0;
31327 //        
31328 //        Roo.each(items, function(item, k){
31329 //            
31330 //            var c = k % 2;
31331 //            
31332 //            item.el.position('absolute');
31333 //                
31334 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31335 //
31336 //            item.el.setWidth(width);
31337 //
31338 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31339 //
31340 //            item.el.setHeight(height);
31341 //            
31342 //            if(c == 0){
31343 //                item.el.setXY([x, y], isInstant ? false : true);
31344 //            } else {
31345 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31346 //            }
31347 //            
31348 //            y = y + height + this.alternativePadWidth;
31349 //            
31350 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31351 //            
31352 //        }, this);
31353 //        
31354 //        this.el.setHeight(maxHeight);
31355 //        
31356 //    },
31357     
31358     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31359     {
31360         var pos = this.el.getBox(true);
31361         
31362         var minX = pos.x;
31363         var minY = pos.y;
31364         
31365         var maxX = pos.right;
31366         
31367         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31368         
31369         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31370         
31371         Roo.each(queue, function(box, k){
31372             
31373             Roo.each(box, function(b, kk){
31374                 
31375                 b.el.position('absolute');
31376                 
31377                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31378                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31379                 
31380                 if(b.size == 'md-left' || b.size == 'md-right'){
31381                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31382                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31383                 }
31384                 
31385                 b.el.setWidth(width);
31386                 b.el.setHeight(height);
31387                 
31388             }, this);
31389             
31390             if(!box.length){
31391                 return;
31392             }
31393             
31394             var positions = [];
31395             
31396             switch (box.length){
31397                 case 1 :
31398                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31399                     break;
31400                 case 2 :
31401                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31402                     break;
31403                 case 3 :
31404                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31405                     break;
31406                 case 4 :
31407                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31408                     break;
31409                 default :
31410                     break;
31411             }
31412             
31413             Roo.each(box, function(b,kk){
31414                 
31415                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31416                 
31417                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31418                 
31419             }, this);
31420             
31421         }, this);
31422         
31423     },
31424     
31425     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31426     {
31427         Roo.each(eItems, function(b,k){
31428             
31429             b.size = (k == 0) ? 'sm' : 'xs';
31430             b.x = (k == 0) ? 2 : 1;
31431             b.y = (k == 0) ? 2 : 1;
31432             
31433             b.el.position('absolute');
31434             
31435             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31436                 
31437             b.el.setWidth(width);
31438             
31439             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31440             
31441             b.el.setHeight(height);
31442             
31443         }, this);
31444
31445         var positions = [];
31446         
31447         positions.push({
31448             x : maxX - this.unitWidth * 2 - this.gutter,
31449             y : minY
31450         });
31451         
31452         positions.push({
31453             x : maxX - this.unitWidth,
31454             y : minY + (this.unitWidth + this.gutter) * 2
31455         });
31456         
31457         positions.push({
31458             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31459             y : minY
31460         });
31461         
31462         Roo.each(eItems, function(b,k){
31463             
31464             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31465
31466         }, this);
31467         
31468     },
31469     
31470     getVerticalOneBoxColPositions : function(x, y, box)
31471     {
31472         var pos = [];
31473         
31474         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31475         
31476         if(box[0].size == 'md-left'){
31477             rand = 0;
31478         }
31479         
31480         if(box[0].size == 'md-right'){
31481             rand = 1;
31482         }
31483         
31484         pos.push({
31485             x : x + (this.unitWidth + this.gutter) * rand,
31486             y : y
31487         });
31488         
31489         return pos;
31490     },
31491     
31492     getVerticalTwoBoxColPositions : function(x, y, box)
31493     {
31494         var pos = [];
31495         
31496         if(box[0].size == 'xs'){
31497             
31498             pos.push({
31499                 x : x,
31500                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31501             });
31502
31503             pos.push({
31504                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31505                 y : y
31506             });
31507             
31508             return pos;
31509             
31510         }
31511         
31512         pos.push({
31513             x : x,
31514             y : y
31515         });
31516
31517         pos.push({
31518             x : x + (this.unitWidth + this.gutter) * 2,
31519             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31520         });
31521         
31522         return pos;
31523         
31524     },
31525     
31526     getVerticalThreeBoxColPositions : function(x, y, box)
31527     {
31528         var pos = [];
31529         
31530         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31531             
31532             pos.push({
31533                 x : x,
31534                 y : y
31535             });
31536
31537             pos.push({
31538                 x : x + (this.unitWidth + this.gutter) * 1,
31539                 y : y
31540             });
31541             
31542             pos.push({
31543                 x : x + (this.unitWidth + this.gutter) * 2,
31544                 y : y
31545             });
31546             
31547             return pos;
31548             
31549         }
31550         
31551         if(box[0].size == 'xs' && box[1].size == 'xs'){
31552             
31553             pos.push({
31554                 x : x,
31555                 y : y
31556             });
31557
31558             pos.push({
31559                 x : x,
31560                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31561             });
31562             
31563             pos.push({
31564                 x : x + (this.unitWidth + this.gutter) * 1,
31565                 y : y
31566             });
31567             
31568             return pos;
31569             
31570         }
31571         
31572         pos.push({
31573             x : x,
31574             y : y
31575         });
31576
31577         pos.push({
31578             x : x + (this.unitWidth + this.gutter) * 2,
31579             y : y
31580         });
31581
31582         pos.push({
31583             x : x + (this.unitWidth + this.gutter) * 2,
31584             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31585         });
31586             
31587         return pos;
31588         
31589     },
31590     
31591     getVerticalFourBoxColPositions : function(x, y, box)
31592     {
31593         var pos = [];
31594         
31595         if(box[0].size == 'xs'){
31596             
31597             pos.push({
31598                 x : x,
31599                 y : y
31600             });
31601
31602             pos.push({
31603                 x : x,
31604                 y : y + (this.unitHeight + this.gutter) * 1
31605             });
31606             
31607             pos.push({
31608                 x : x,
31609                 y : y + (this.unitHeight + this.gutter) * 2
31610             });
31611             
31612             pos.push({
31613                 x : x + (this.unitWidth + this.gutter) * 1,
31614                 y : y
31615             });
31616             
31617             return pos;
31618             
31619         }
31620         
31621         pos.push({
31622             x : x,
31623             y : y
31624         });
31625
31626         pos.push({
31627             x : x + (this.unitWidth + this.gutter) * 2,
31628             y : y
31629         });
31630
31631         pos.push({
31632             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31633             y : y + (this.unitHeight + this.gutter) * 1
31634         });
31635
31636         pos.push({
31637             x : x + (this.unitWidth + this.gutter) * 2,
31638             y : y + (this.unitWidth + this.gutter) * 2
31639         });
31640
31641         return pos;
31642         
31643     },
31644     
31645     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31646     {
31647         var pos = [];
31648         
31649         if(box[0].size == 'md-left'){
31650             pos.push({
31651                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31652                 y : minY
31653             });
31654             
31655             return pos;
31656         }
31657         
31658         if(box[0].size == 'md-right'){
31659             pos.push({
31660                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31661                 y : minY + (this.unitWidth + this.gutter) * 1
31662             });
31663             
31664             return pos;
31665         }
31666         
31667         var rand = Math.floor(Math.random() * (4 - box[0].y));
31668         
31669         pos.push({
31670             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31671             y : minY + (this.unitWidth + this.gutter) * rand
31672         });
31673         
31674         return pos;
31675         
31676     },
31677     
31678     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31679     {
31680         var pos = [];
31681         
31682         if(box[0].size == 'xs'){
31683             
31684             pos.push({
31685                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31686                 y : minY
31687             });
31688
31689             pos.push({
31690                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31691                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31692             });
31693             
31694             return pos;
31695             
31696         }
31697         
31698         pos.push({
31699             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31700             y : minY
31701         });
31702
31703         pos.push({
31704             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31705             y : minY + (this.unitWidth + this.gutter) * 2
31706         });
31707         
31708         return pos;
31709         
31710     },
31711     
31712     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31713     {
31714         var pos = [];
31715         
31716         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31717             
31718             pos.push({
31719                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31720                 y : minY
31721             });
31722
31723             pos.push({
31724                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31725                 y : minY + (this.unitWidth + this.gutter) * 1
31726             });
31727             
31728             pos.push({
31729                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31730                 y : minY + (this.unitWidth + this.gutter) * 2
31731             });
31732             
31733             return pos;
31734             
31735         }
31736         
31737         if(box[0].size == 'xs' && box[1].size == 'xs'){
31738             
31739             pos.push({
31740                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31741                 y : minY
31742             });
31743
31744             pos.push({
31745                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31746                 y : minY
31747             });
31748             
31749             pos.push({
31750                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31751                 y : minY + (this.unitWidth + this.gutter) * 1
31752             });
31753             
31754             return pos;
31755             
31756         }
31757         
31758         pos.push({
31759             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31760             y : minY
31761         });
31762
31763         pos.push({
31764             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31765             y : minY + (this.unitWidth + this.gutter) * 2
31766         });
31767
31768         pos.push({
31769             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31770             y : minY + (this.unitWidth + this.gutter) * 2
31771         });
31772             
31773         return pos;
31774         
31775     },
31776     
31777     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31778     {
31779         var pos = [];
31780         
31781         if(box[0].size == 'xs'){
31782             
31783             pos.push({
31784                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31785                 y : minY
31786             });
31787
31788             pos.push({
31789                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31790                 y : minY
31791             });
31792             
31793             pos.push({
31794                 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),
31795                 y : minY
31796             });
31797             
31798             pos.push({
31799                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31800                 y : minY + (this.unitWidth + this.gutter) * 1
31801             });
31802             
31803             return pos;
31804             
31805         }
31806         
31807         pos.push({
31808             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31809             y : minY
31810         });
31811         
31812         pos.push({
31813             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31814             y : minY + (this.unitWidth + this.gutter) * 2
31815         });
31816         
31817         pos.push({
31818             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31819             y : minY + (this.unitWidth + this.gutter) * 2
31820         });
31821         
31822         pos.push({
31823             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),
31824             y : minY + (this.unitWidth + this.gutter) * 2
31825         });
31826
31827         return pos;
31828         
31829     },
31830     
31831     /**
31832     * remove a Masonry Brick
31833     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31834     */
31835     removeBrick : function(brick_id)
31836     {
31837         if (!brick_id) {
31838             return;
31839         }
31840         
31841         for (var i = 0; i<this.bricks.length; i++) {
31842             if (this.bricks[i].id == brick_id) {
31843                 this.bricks.splice(i,1);
31844                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31845                 this.initial();
31846             }
31847         }
31848     },
31849     
31850     /**
31851     * adds a Masonry Brick
31852     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31853     */
31854     addBrick : function(cfg)
31855     {
31856         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31857         //this.register(cn);
31858         cn.parentId = this.id;
31859         cn.onRender(this.el, null);
31860         return cn;
31861     },
31862     
31863     /**
31864     * register a Masonry Brick
31865     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31866     */
31867     
31868     register : function(brick)
31869     {
31870         this.bricks.push(brick);
31871         brick.masonryId = this.id;
31872     },
31873     
31874     /**
31875     * clear all the Masonry Brick
31876     */
31877     clearAll : function()
31878     {
31879         this.bricks = [];
31880         //this.getChildContainer().dom.innerHTML = "";
31881         this.el.dom.innerHTML = '';
31882     },
31883     
31884     getSelected : function()
31885     {
31886         if (!this.selectedBrick) {
31887             return false;
31888         }
31889         
31890         return this.selectedBrick;
31891     }
31892 });
31893
31894 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31895     
31896     groups: {},
31897      /**
31898     * register a Masonry Layout
31899     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31900     */
31901     
31902     register : function(layout)
31903     {
31904         this.groups[layout.id] = layout;
31905     },
31906     /**
31907     * fetch a  Masonry Layout based on the masonry layout ID
31908     * @param {string} the masonry layout to add
31909     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31910     */
31911     
31912     get: function(layout_id) {
31913         if (typeof(this.groups[layout_id]) == 'undefined') {
31914             return false;
31915         }
31916         return this.groups[layout_id] ;
31917     }
31918     
31919     
31920     
31921 });
31922
31923  
31924
31925  /**
31926  *
31927  * This is based on 
31928  * http://masonry.desandro.com
31929  *
31930  * The idea is to render all the bricks based on vertical width...
31931  *
31932  * The original code extends 'outlayer' - we might need to use that....
31933  * 
31934  */
31935
31936
31937 /**
31938  * @class Roo.bootstrap.LayoutMasonryAuto
31939  * @extends Roo.bootstrap.Component
31940  * Bootstrap Layout Masonry class
31941  * 
31942  * @constructor
31943  * Create a new Element
31944  * @param {Object} config The config object
31945  */
31946
31947 Roo.bootstrap.LayoutMasonryAuto = function(config){
31948     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31949 };
31950
31951 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31952     
31953       /**
31954      * @cfg {Boolean} isFitWidth  - resize the width..
31955      */   
31956     isFitWidth : false,  // options..
31957     /**
31958      * @cfg {Boolean} isOriginLeft = left align?
31959      */   
31960     isOriginLeft : true,
31961     /**
31962      * @cfg {Boolean} isOriginTop = top align?
31963      */   
31964     isOriginTop : false,
31965     /**
31966      * @cfg {Boolean} isLayoutInstant = no animation?
31967      */   
31968     isLayoutInstant : false, // needed?
31969     /**
31970      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31971      */   
31972     isResizingContainer : true,
31973     /**
31974      * @cfg {Number} columnWidth  width of the columns 
31975      */   
31976     
31977     columnWidth : 0,
31978     
31979     /**
31980      * @cfg {Number} maxCols maximum number of columns
31981      */   
31982     
31983     maxCols: 0,
31984     /**
31985      * @cfg {Number} padHeight padding below box..
31986      */   
31987     
31988     padHeight : 10, 
31989     
31990     /**
31991      * @cfg {Boolean} isAutoInitial defalut true
31992      */   
31993     
31994     isAutoInitial : true, 
31995     
31996     // private?
31997     gutter : 0,
31998     
31999     containerWidth: 0,
32000     initialColumnWidth : 0,
32001     currentSize : null,
32002     
32003     colYs : null, // array.
32004     maxY : 0,
32005     padWidth: 10,
32006     
32007     
32008     tag: 'div',
32009     cls: '',
32010     bricks: null, //CompositeElement
32011     cols : 0, // array?
32012     // element : null, // wrapped now this.el
32013     _isLayoutInited : null, 
32014     
32015     
32016     getAutoCreate : function(){
32017         
32018         var cfg = {
32019             tag: this.tag,
32020             cls: 'blog-masonary-wrapper ' + this.cls,
32021             cn : {
32022                 cls : 'mas-boxes masonary'
32023             }
32024         };
32025         
32026         return cfg;
32027     },
32028     
32029     getChildContainer: function( )
32030     {
32031         if (this.boxesEl) {
32032             return this.boxesEl;
32033         }
32034         
32035         this.boxesEl = this.el.select('.mas-boxes').first();
32036         
32037         return this.boxesEl;
32038     },
32039     
32040     
32041     initEvents : function()
32042     {
32043         var _this = this;
32044         
32045         if(this.isAutoInitial){
32046             Roo.log('hook children rendered');
32047             this.on('childrenrendered', function() {
32048                 Roo.log('children rendered');
32049                 _this.initial();
32050             } ,this);
32051         }
32052         
32053     },
32054     
32055     initial : function()
32056     {
32057         this.reloadItems();
32058
32059         this.currentSize = this.el.getBox(true);
32060
32061         /// was window resize... - let's see if this works..
32062         Roo.EventManager.onWindowResize(this.resize, this); 
32063
32064         if(!this.isAutoInitial){
32065             this.layout();
32066             return;
32067         }
32068         
32069         this.layout.defer(500,this);
32070     },
32071     
32072     reloadItems: function()
32073     {
32074         this.bricks = this.el.select('.masonry-brick', true);
32075         
32076         this.bricks.each(function(b) {
32077             //Roo.log(b.getSize());
32078             if (!b.attr('originalwidth')) {
32079                 b.attr('originalwidth',  b.getSize().width);
32080             }
32081             
32082         });
32083         
32084         Roo.log(this.bricks.elements.length);
32085     },
32086     
32087     resize : function()
32088     {
32089         Roo.log('resize');
32090         var cs = this.el.getBox(true);
32091         
32092         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32093             Roo.log("no change in with or X");
32094             return;
32095         }
32096         this.currentSize = cs;
32097         this.layout();
32098     },
32099     
32100     layout : function()
32101     {
32102          Roo.log('layout');
32103         this._resetLayout();
32104         //this._manageStamps();
32105       
32106         // don't animate first layout
32107         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32108         this.layoutItems( isInstant );
32109       
32110         // flag for initalized
32111         this._isLayoutInited = true;
32112     },
32113     
32114     layoutItems : function( isInstant )
32115     {
32116         //var items = this._getItemsForLayout( this.items );
32117         // original code supports filtering layout items.. we just ignore it..
32118         
32119         this._layoutItems( this.bricks , isInstant );
32120       
32121         this._postLayout();
32122     },
32123     _layoutItems : function ( items , isInstant)
32124     {
32125        //this.fireEvent( 'layout', this, items );
32126     
32127
32128         if ( !items || !items.elements.length ) {
32129           // no items, emit event with empty array
32130             return;
32131         }
32132
32133         var queue = [];
32134         items.each(function(item) {
32135             Roo.log("layout item");
32136             Roo.log(item);
32137             // get x/y object from method
32138             var position = this._getItemLayoutPosition( item );
32139             // enqueue
32140             position.item = item;
32141             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32142             queue.push( position );
32143         }, this);
32144       
32145         this._processLayoutQueue( queue );
32146     },
32147     /** Sets position of item in DOM
32148     * @param {Element} item
32149     * @param {Number} x - horizontal position
32150     * @param {Number} y - vertical position
32151     * @param {Boolean} isInstant - disables transitions
32152     */
32153     _processLayoutQueue : function( queue )
32154     {
32155         for ( var i=0, len = queue.length; i < len; i++ ) {
32156             var obj = queue[i];
32157             obj.item.position('absolute');
32158             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32159         }
32160     },
32161       
32162     
32163     /**
32164     * Any logic you want to do after each layout,
32165     * i.e. size the container
32166     */
32167     _postLayout : function()
32168     {
32169         this.resizeContainer();
32170     },
32171     
32172     resizeContainer : function()
32173     {
32174         if ( !this.isResizingContainer ) {
32175             return;
32176         }
32177         var size = this._getContainerSize();
32178         if ( size ) {
32179             this.el.setSize(size.width,size.height);
32180             this.boxesEl.setSize(size.width,size.height);
32181         }
32182     },
32183     
32184     
32185     
32186     _resetLayout : function()
32187     {
32188         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32189         this.colWidth = this.el.getWidth();
32190         //this.gutter = this.el.getWidth(); 
32191         
32192         this.measureColumns();
32193
32194         // reset column Y
32195         var i = this.cols;
32196         this.colYs = [];
32197         while (i--) {
32198             this.colYs.push( 0 );
32199         }
32200     
32201         this.maxY = 0;
32202     },
32203
32204     measureColumns : function()
32205     {
32206         this.getContainerWidth();
32207       // if columnWidth is 0, default to outerWidth of first item
32208         if ( !this.columnWidth ) {
32209             var firstItem = this.bricks.first();
32210             Roo.log(firstItem);
32211             this.columnWidth  = this.containerWidth;
32212             if (firstItem && firstItem.attr('originalwidth') ) {
32213                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32214             }
32215             // columnWidth fall back to item of first element
32216             Roo.log("set column width?");
32217                         this.initialColumnWidth = this.columnWidth  ;
32218
32219             // if first elem has no width, default to size of container
32220             
32221         }
32222         
32223         
32224         if (this.initialColumnWidth) {
32225             this.columnWidth = this.initialColumnWidth;
32226         }
32227         
32228         
32229             
32230         // column width is fixed at the top - however if container width get's smaller we should
32231         // reduce it...
32232         
32233         // this bit calcs how man columns..
32234             
32235         var columnWidth = this.columnWidth += this.gutter;
32236       
32237         // calculate columns
32238         var containerWidth = this.containerWidth + this.gutter;
32239         
32240         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32241         // fix rounding errors, typically with gutters
32242         var excess = columnWidth - containerWidth % columnWidth;
32243         
32244         
32245         // if overshoot is less than a pixel, round up, otherwise floor it
32246         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32247         cols = Math[ mathMethod ]( cols );
32248         this.cols = Math.max( cols, 1 );
32249         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32250         
32251          // padding positioning..
32252         var totalColWidth = this.cols * this.columnWidth;
32253         var padavail = this.containerWidth - totalColWidth;
32254         // so for 2 columns - we need 3 'pads'
32255         
32256         var padNeeded = (1+this.cols) * this.padWidth;
32257         
32258         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32259         
32260         this.columnWidth += padExtra
32261         //this.padWidth = Math.floor(padavail /  ( this.cols));
32262         
32263         // adjust colum width so that padding is fixed??
32264         
32265         // we have 3 columns ... total = width * 3
32266         // we have X left over... that should be used by 
32267         
32268         //if (this.expandC) {
32269             
32270         //}
32271         
32272         
32273         
32274     },
32275     
32276     getContainerWidth : function()
32277     {
32278        /* // container is parent if fit width
32279         var container = this.isFitWidth ? this.element.parentNode : this.element;
32280         // check that this.size and size are there
32281         // IE8 triggers resize on body size change, so they might not be
32282         
32283         var size = getSize( container );  //FIXME
32284         this.containerWidth = size && size.innerWidth; //FIXME
32285         */
32286          
32287         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32288         
32289     },
32290     
32291     _getItemLayoutPosition : function( item )  // what is item?
32292     {
32293         // we resize the item to our columnWidth..
32294       
32295         item.setWidth(this.columnWidth);
32296         item.autoBoxAdjust  = false;
32297         
32298         var sz = item.getSize();
32299  
32300         // how many columns does this brick span
32301         var remainder = this.containerWidth % this.columnWidth;
32302         
32303         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32304         // round if off by 1 pixel, otherwise use ceil
32305         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32306         colSpan = Math.min( colSpan, this.cols );
32307         
32308         // normally this should be '1' as we dont' currently allow multi width columns..
32309         
32310         var colGroup = this._getColGroup( colSpan );
32311         // get the minimum Y value from the columns
32312         var minimumY = Math.min.apply( Math, colGroup );
32313         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32314         
32315         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32316          
32317         // position the brick
32318         var position = {
32319             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32320             y: this.currentSize.y + minimumY + this.padHeight
32321         };
32322         
32323         Roo.log(position);
32324         // apply setHeight to necessary columns
32325         var setHeight = minimumY + sz.height + this.padHeight;
32326         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32327         
32328         var setSpan = this.cols + 1 - colGroup.length;
32329         for ( var i = 0; i < setSpan; i++ ) {
32330           this.colYs[ shortColIndex + i ] = setHeight ;
32331         }
32332       
32333         return position;
32334     },
32335     
32336     /**
32337      * @param {Number} colSpan - number of columns the element spans
32338      * @returns {Array} colGroup
32339      */
32340     _getColGroup : function( colSpan )
32341     {
32342         if ( colSpan < 2 ) {
32343           // if brick spans only one column, use all the column Ys
32344           return this.colYs;
32345         }
32346       
32347         var colGroup = [];
32348         // how many different places could this brick fit horizontally
32349         var groupCount = this.cols + 1 - colSpan;
32350         // for each group potential horizontal position
32351         for ( var i = 0; i < groupCount; i++ ) {
32352           // make an array of colY values for that one group
32353           var groupColYs = this.colYs.slice( i, i + colSpan );
32354           // and get the max value of the array
32355           colGroup[i] = Math.max.apply( Math, groupColYs );
32356         }
32357         return colGroup;
32358     },
32359     /*
32360     _manageStamp : function( stamp )
32361     {
32362         var stampSize =  stamp.getSize();
32363         var offset = stamp.getBox();
32364         // get the columns that this stamp affects
32365         var firstX = this.isOriginLeft ? offset.x : offset.right;
32366         var lastX = firstX + stampSize.width;
32367         var firstCol = Math.floor( firstX / this.columnWidth );
32368         firstCol = Math.max( 0, firstCol );
32369         
32370         var lastCol = Math.floor( lastX / this.columnWidth );
32371         // lastCol should not go over if multiple of columnWidth #425
32372         lastCol -= lastX % this.columnWidth ? 0 : 1;
32373         lastCol = Math.min( this.cols - 1, lastCol );
32374         
32375         // set colYs to bottom of the stamp
32376         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32377             stampSize.height;
32378             
32379         for ( var i = firstCol; i <= lastCol; i++ ) {
32380           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32381         }
32382     },
32383     */
32384     
32385     _getContainerSize : function()
32386     {
32387         this.maxY = Math.max.apply( Math, this.colYs );
32388         var size = {
32389             height: this.maxY
32390         };
32391       
32392         if ( this.isFitWidth ) {
32393             size.width = this._getContainerFitWidth();
32394         }
32395       
32396         return size;
32397     },
32398     
32399     _getContainerFitWidth : function()
32400     {
32401         var unusedCols = 0;
32402         // count unused columns
32403         var i = this.cols;
32404         while ( --i ) {
32405           if ( this.colYs[i] !== 0 ) {
32406             break;
32407           }
32408           unusedCols++;
32409         }
32410         // fit container to columns that have been used
32411         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32412     },
32413     
32414     needsResizeLayout : function()
32415     {
32416         var previousWidth = this.containerWidth;
32417         this.getContainerWidth();
32418         return previousWidth !== this.containerWidth;
32419     }
32420  
32421 });
32422
32423  
32424
32425  /*
32426  * - LGPL
32427  *
32428  * element
32429  * 
32430  */
32431
32432 /**
32433  * @class Roo.bootstrap.MasonryBrick
32434  * @extends Roo.bootstrap.Component
32435  * Bootstrap MasonryBrick class
32436  * 
32437  * @constructor
32438  * Create a new MasonryBrick
32439  * @param {Object} config The config object
32440  */
32441
32442 Roo.bootstrap.MasonryBrick = function(config){
32443     
32444     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32445     
32446     Roo.bootstrap.MasonryBrick.register(this);
32447     
32448     this.addEvents({
32449         // raw events
32450         /**
32451          * @event click
32452          * When a MasonryBrick is clcik
32453          * @param {Roo.bootstrap.MasonryBrick} this
32454          * @param {Roo.EventObject} e
32455          */
32456         "click" : true
32457     });
32458 };
32459
32460 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32461     
32462     /**
32463      * @cfg {String} title
32464      */   
32465     title : '',
32466     /**
32467      * @cfg {String} html
32468      */   
32469     html : '',
32470     /**
32471      * @cfg {String} bgimage
32472      */   
32473     bgimage : '',
32474     /**
32475      * @cfg {String} videourl
32476      */   
32477     videourl : '',
32478     /**
32479      * @cfg {String} cls
32480      */   
32481     cls : '',
32482     /**
32483      * @cfg {String} href
32484      */   
32485     href : '',
32486     /**
32487      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32488      */   
32489     size : 'xs',
32490     
32491     /**
32492      * @cfg {String} placetitle (center|bottom)
32493      */   
32494     placetitle : '',
32495     
32496     /**
32497      * @cfg {Boolean} isFitContainer defalut true
32498      */   
32499     isFitContainer : true, 
32500     
32501     /**
32502      * @cfg {Boolean} preventDefault defalut false
32503      */   
32504     preventDefault : false, 
32505     
32506     /**
32507      * @cfg {Boolean} inverse defalut false
32508      */   
32509     maskInverse : false, 
32510     
32511     getAutoCreate : function()
32512     {
32513         if(!this.isFitContainer){
32514             return this.getSplitAutoCreate();
32515         }
32516         
32517         var cls = 'masonry-brick masonry-brick-full';
32518         
32519         if(this.href.length){
32520             cls += ' masonry-brick-link';
32521         }
32522         
32523         if(this.bgimage.length){
32524             cls += ' masonry-brick-image';
32525         }
32526         
32527         if(this.maskInverse){
32528             cls += ' mask-inverse';
32529         }
32530         
32531         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32532             cls += ' enable-mask';
32533         }
32534         
32535         if(this.size){
32536             cls += ' masonry-' + this.size + '-brick';
32537         }
32538         
32539         if(this.placetitle.length){
32540             
32541             switch (this.placetitle) {
32542                 case 'center' :
32543                     cls += ' masonry-center-title';
32544                     break;
32545                 case 'bottom' :
32546                     cls += ' masonry-bottom-title';
32547                     break;
32548                 default:
32549                     break;
32550             }
32551             
32552         } else {
32553             if(!this.html.length && !this.bgimage.length){
32554                 cls += ' masonry-center-title';
32555             }
32556
32557             if(!this.html.length && this.bgimage.length){
32558                 cls += ' masonry-bottom-title';
32559             }
32560         }
32561         
32562         if(this.cls){
32563             cls += ' ' + this.cls;
32564         }
32565         
32566         var cfg = {
32567             tag: (this.href.length) ? 'a' : 'div',
32568             cls: cls,
32569             cn: [
32570                 {
32571                     tag: 'div',
32572                     cls: 'masonry-brick-mask'
32573                 },
32574                 {
32575                     tag: 'div',
32576                     cls: 'masonry-brick-paragraph',
32577                     cn: []
32578                 }
32579             ]
32580         };
32581         
32582         if(this.href.length){
32583             cfg.href = this.href;
32584         }
32585         
32586         var cn = cfg.cn[1].cn;
32587         
32588         if(this.title.length){
32589             cn.push({
32590                 tag: 'h4',
32591                 cls: 'masonry-brick-title',
32592                 html: this.title
32593             });
32594         }
32595         
32596         if(this.html.length){
32597             cn.push({
32598                 tag: 'p',
32599                 cls: 'masonry-brick-text',
32600                 html: this.html
32601             });
32602         }
32603         
32604         if (!this.title.length && !this.html.length) {
32605             cfg.cn[1].cls += ' hide';
32606         }
32607         
32608         if(this.bgimage.length){
32609             cfg.cn.push({
32610                 tag: 'img',
32611                 cls: 'masonry-brick-image-view',
32612                 src: this.bgimage
32613             });
32614         }
32615         
32616         if(this.videourl.length){
32617             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32618             // youtube support only?
32619             cfg.cn.push({
32620                 tag: 'iframe',
32621                 cls: 'masonry-brick-image-view',
32622                 src: vurl,
32623                 frameborder : 0,
32624                 allowfullscreen : true
32625             });
32626         }
32627         
32628         return cfg;
32629         
32630     },
32631     
32632     getSplitAutoCreate : function()
32633     {
32634         var cls = 'masonry-brick masonry-brick-split';
32635         
32636         if(this.href.length){
32637             cls += ' masonry-brick-link';
32638         }
32639         
32640         if(this.bgimage.length){
32641             cls += ' masonry-brick-image';
32642         }
32643         
32644         if(this.size){
32645             cls += ' masonry-' + this.size + '-brick';
32646         }
32647         
32648         switch (this.placetitle) {
32649             case 'center' :
32650                 cls += ' masonry-center-title';
32651                 break;
32652             case 'bottom' :
32653                 cls += ' masonry-bottom-title';
32654                 break;
32655             default:
32656                 if(!this.bgimage.length){
32657                     cls += ' masonry-center-title';
32658                 }
32659
32660                 if(this.bgimage.length){
32661                     cls += ' masonry-bottom-title';
32662                 }
32663                 break;
32664         }
32665         
32666         if(this.cls){
32667             cls += ' ' + this.cls;
32668         }
32669         
32670         var cfg = {
32671             tag: (this.href.length) ? 'a' : 'div',
32672             cls: cls,
32673             cn: [
32674                 {
32675                     tag: 'div',
32676                     cls: 'masonry-brick-split-head',
32677                     cn: [
32678                         {
32679                             tag: 'div',
32680                             cls: 'masonry-brick-paragraph',
32681                             cn: []
32682                         }
32683                     ]
32684                 },
32685                 {
32686                     tag: 'div',
32687                     cls: 'masonry-brick-split-body',
32688                     cn: []
32689                 }
32690             ]
32691         };
32692         
32693         if(this.href.length){
32694             cfg.href = this.href;
32695         }
32696         
32697         if(this.title.length){
32698             cfg.cn[0].cn[0].cn.push({
32699                 tag: 'h4',
32700                 cls: 'masonry-brick-title',
32701                 html: this.title
32702             });
32703         }
32704         
32705         if(this.html.length){
32706             cfg.cn[1].cn.push({
32707                 tag: 'p',
32708                 cls: 'masonry-brick-text',
32709                 html: this.html
32710             });
32711         }
32712
32713         if(this.bgimage.length){
32714             cfg.cn[0].cn.push({
32715                 tag: 'img',
32716                 cls: 'masonry-brick-image-view',
32717                 src: this.bgimage
32718             });
32719         }
32720         
32721         if(this.videourl.length){
32722             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32723             // youtube support only?
32724             cfg.cn[0].cn.cn.push({
32725                 tag: 'iframe',
32726                 cls: 'masonry-brick-image-view',
32727                 src: vurl,
32728                 frameborder : 0,
32729                 allowfullscreen : true
32730             });
32731         }
32732         
32733         return cfg;
32734     },
32735     
32736     initEvents: function() 
32737     {
32738         switch (this.size) {
32739             case 'xs' :
32740                 this.x = 1;
32741                 this.y = 1;
32742                 break;
32743             case 'sm' :
32744                 this.x = 2;
32745                 this.y = 2;
32746                 break;
32747             case 'md' :
32748             case 'md-left' :
32749             case 'md-right' :
32750                 this.x = 3;
32751                 this.y = 3;
32752                 break;
32753             case 'tall' :
32754                 this.x = 2;
32755                 this.y = 3;
32756                 break;
32757             case 'wide' :
32758                 this.x = 3;
32759                 this.y = 2;
32760                 break;
32761             case 'wide-thin' :
32762                 this.x = 3;
32763                 this.y = 1;
32764                 break;
32765                         
32766             default :
32767                 break;
32768         }
32769         
32770         if(Roo.isTouch){
32771             this.el.on('touchstart', this.onTouchStart, this);
32772             this.el.on('touchmove', this.onTouchMove, this);
32773             this.el.on('touchend', this.onTouchEnd, this);
32774             this.el.on('contextmenu', this.onContextMenu, this);
32775         } else {
32776             this.el.on('mouseenter'  ,this.enter, this);
32777             this.el.on('mouseleave', this.leave, this);
32778             this.el.on('click', this.onClick, this);
32779         }
32780         
32781         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32782             this.parent().bricks.push(this);   
32783         }
32784         
32785     },
32786     
32787     onClick: function(e, el)
32788     {
32789         var time = this.endTimer - this.startTimer;
32790         // Roo.log(e.preventDefault());
32791         if(Roo.isTouch){
32792             if(time > 1000){
32793                 e.preventDefault();
32794                 return;
32795             }
32796         }
32797         
32798         if(!this.preventDefault){
32799             return;
32800         }
32801         
32802         e.preventDefault();
32803         
32804         if (this.activeClass != '') {
32805             this.selectBrick();
32806         }
32807         
32808         this.fireEvent('click', this, e);
32809     },
32810     
32811     enter: 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.9, true);
32821         }
32822     },
32823     
32824     leave: function(e, el)
32825     {
32826         e.preventDefault();
32827         
32828         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32829             return;
32830         }
32831         
32832         if(this.bgimage.length && this.html.length){
32833             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32834         }
32835     },
32836     
32837     onTouchStart: function(e, el)
32838     {
32839 //        e.preventDefault();
32840         
32841         this.touchmoved = false;
32842         
32843         if(!this.isFitContainer){
32844             return;
32845         }
32846         
32847         if(!this.bgimage.length || !this.html.length){
32848             return;
32849         }
32850         
32851         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32852         
32853         this.timer = new Date().getTime();
32854         
32855     },
32856     
32857     onTouchMove: function(e, el)
32858     {
32859         this.touchmoved = true;
32860     },
32861     
32862     onContextMenu : function(e,el)
32863     {
32864         e.preventDefault();
32865         e.stopPropagation();
32866         return false;
32867     },
32868     
32869     onTouchEnd: function(e, el)
32870     {
32871 //        e.preventDefault();
32872         
32873         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32874         
32875             this.leave(e,el);
32876             
32877             return;
32878         }
32879         
32880         if(!this.bgimage.length || !this.html.length){
32881             
32882             if(this.href.length){
32883                 window.location.href = this.href;
32884             }
32885             
32886             return;
32887         }
32888         
32889         if(!this.isFitContainer){
32890             return;
32891         }
32892         
32893         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32894         
32895         window.location.href = this.href;
32896     },
32897     
32898     //selection on single brick only
32899     selectBrick : function() {
32900         
32901         if (!this.parentId) {
32902             return;
32903         }
32904         
32905         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32906         var index = m.selectedBrick.indexOf(this.id);
32907         
32908         if ( index > -1) {
32909             m.selectedBrick.splice(index,1);
32910             this.el.removeClass(this.activeClass);
32911             return;
32912         }
32913         
32914         for(var i = 0; i < m.selectedBrick.length; i++) {
32915             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32916             b.el.removeClass(b.activeClass);
32917         }
32918         
32919         m.selectedBrick = [];
32920         
32921         m.selectedBrick.push(this.id);
32922         this.el.addClass(this.activeClass);
32923         return;
32924     },
32925     
32926     isSelected : function(){
32927         return this.el.hasClass(this.activeClass);
32928         
32929     }
32930 });
32931
32932 Roo.apply(Roo.bootstrap.MasonryBrick, {
32933     
32934     //groups: {},
32935     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32936      /**
32937     * register a Masonry Brick
32938     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32939     */
32940     
32941     register : function(brick)
32942     {
32943         //this.groups[brick.id] = brick;
32944         this.groups.add(brick.id, brick);
32945     },
32946     /**
32947     * fetch a  masonry brick based on the masonry brick ID
32948     * @param {string} the masonry brick to add
32949     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32950     */
32951     
32952     get: function(brick_id) 
32953     {
32954         // if (typeof(this.groups[brick_id]) == 'undefined') {
32955         //     return false;
32956         // }
32957         // return this.groups[brick_id] ;
32958         
32959         if(this.groups.key(brick_id)) {
32960             return this.groups.key(brick_id);
32961         }
32962         
32963         return false;
32964     }
32965     
32966     
32967     
32968 });
32969
32970  /*
32971  * - LGPL
32972  *
32973  * element
32974  * 
32975  */
32976
32977 /**
32978  * @class Roo.bootstrap.Brick
32979  * @extends Roo.bootstrap.Component
32980  * Bootstrap Brick class
32981  * 
32982  * @constructor
32983  * Create a new Brick
32984  * @param {Object} config The config object
32985  */
32986
32987 Roo.bootstrap.Brick = function(config){
32988     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32989     
32990     this.addEvents({
32991         // raw events
32992         /**
32993          * @event click
32994          * When a Brick is click
32995          * @param {Roo.bootstrap.Brick} this
32996          * @param {Roo.EventObject} e
32997          */
32998         "click" : true
32999     });
33000 };
33001
33002 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33003     
33004     /**
33005      * @cfg {String} title
33006      */   
33007     title : '',
33008     /**
33009      * @cfg {String} html
33010      */   
33011     html : '',
33012     /**
33013      * @cfg {String} bgimage
33014      */   
33015     bgimage : '',
33016     /**
33017      * @cfg {String} cls
33018      */   
33019     cls : '',
33020     /**
33021      * @cfg {String} href
33022      */   
33023     href : '',
33024     /**
33025      * @cfg {String} video
33026      */   
33027     video : '',
33028     /**
33029      * @cfg {Boolean} square
33030      */   
33031     square : true,
33032     
33033     getAutoCreate : function()
33034     {
33035         var cls = 'roo-brick';
33036         
33037         if(this.href.length){
33038             cls += ' roo-brick-link';
33039         }
33040         
33041         if(this.bgimage.length){
33042             cls += ' roo-brick-image';
33043         }
33044         
33045         if(!this.html.length && !this.bgimage.length){
33046             cls += ' roo-brick-center-title';
33047         }
33048         
33049         if(!this.html.length && this.bgimage.length){
33050             cls += ' roo-brick-bottom-title';
33051         }
33052         
33053         if(this.cls){
33054             cls += ' ' + this.cls;
33055         }
33056         
33057         var cfg = {
33058             tag: (this.href.length) ? 'a' : 'div',
33059             cls: cls,
33060             cn: [
33061                 {
33062                     tag: 'div',
33063                     cls: 'roo-brick-paragraph',
33064                     cn: []
33065                 }
33066             ]
33067         };
33068         
33069         if(this.href.length){
33070             cfg.href = this.href;
33071         }
33072         
33073         var cn = cfg.cn[0].cn;
33074         
33075         if(this.title.length){
33076             cn.push({
33077                 tag: 'h4',
33078                 cls: 'roo-brick-title',
33079                 html: this.title
33080             });
33081         }
33082         
33083         if(this.html.length){
33084             cn.push({
33085                 tag: 'p',
33086                 cls: 'roo-brick-text',
33087                 html: this.html
33088             });
33089         } else {
33090             cn.cls += ' hide';
33091         }
33092         
33093         if(this.bgimage.length){
33094             cfg.cn.push({
33095                 tag: 'img',
33096                 cls: 'roo-brick-image-view',
33097                 src: this.bgimage
33098             });
33099         }
33100         
33101         return cfg;
33102     },
33103     
33104     initEvents: function() 
33105     {
33106         if(this.title.length || this.html.length){
33107             this.el.on('mouseenter'  ,this.enter, this);
33108             this.el.on('mouseleave', this.leave, this);
33109         }
33110         
33111         Roo.EventManager.onWindowResize(this.resize, this); 
33112         
33113         if(this.bgimage.length){
33114             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33115             this.imageEl.on('load', this.onImageLoad, this);
33116             return;
33117         }
33118         
33119         this.resize();
33120     },
33121     
33122     onImageLoad : function()
33123     {
33124         this.resize();
33125     },
33126     
33127     resize : function()
33128     {
33129         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33130         
33131         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33132         
33133         if(this.bgimage.length){
33134             var image = this.el.select('.roo-brick-image-view', true).first();
33135             
33136             image.setWidth(paragraph.getWidth());
33137             
33138             if(this.square){
33139                 image.setHeight(paragraph.getWidth());
33140             }
33141             
33142             this.el.setHeight(image.getHeight());
33143             paragraph.setHeight(image.getHeight());
33144             
33145         }
33146         
33147     },
33148     
33149     enter: function(e, el)
33150     {
33151         e.preventDefault();
33152         
33153         if(this.bgimage.length){
33154             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33155             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33156         }
33157     },
33158     
33159     leave: function(e, el)
33160     {
33161         e.preventDefault();
33162         
33163         if(this.bgimage.length){
33164             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33165             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33166         }
33167     }
33168     
33169 });
33170
33171  
33172
33173  /*
33174  * - LGPL
33175  *
33176  * Number field 
33177  */
33178
33179 /**
33180  * @class Roo.bootstrap.NumberField
33181  * @extends Roo.bootstrap.Input
33182  * Bootstrap NumberField class
33183  * 
33184  * 
33185  * 
33186  * 
33187  * @constructor
33188  * Create a new NumberField
33189  * @param {Object} config The config object
33190  */
33191
33192 Roo.bootstrap.NumberField = function(config){
33193     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33194 };
33195
33196 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33197     
33198     /**
33199      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33200      */
33201     allowDecimals : true,
33202     /**
33203      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33204      */
33205     decimalSeparator : ".",
33206     /**
33207      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33208      */
33209     decimalPrecision : 2,
33210     /**
33211      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33212      */
33213     allowNegative : true,
33214     
33215     /**
33216      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33217      */
33218     allowZero: true,
33219     /**
33220      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33221      */
33222     minValue : Number.NEGATIVE_INFINITY,
33223     /**
33224      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33225      */
33226     maxValue : Number.MAX_VALUE,
33227     /**
33228      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33229      */
33230     minText : "The minimum value for this field is {0}",
33231     /**
33232      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33233      */
33234     maxText : "The maximum value for this field is {0}",
33235     /**
33236      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33237      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33238      */
33239     nanText : "{0} is not a valid number",
33240     /**
33241      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33242      */
33243     castInt : true,
33244     /**
33245      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33246      */
33247     thousandsDelimiter : false,
33248     /**
33249      * @cfg {String} valueAlign alignment of value
33250      */
33251     valueAlign : "left",
33252
33253     getAutoCreate : function()
33254     {
33255         var hiddenInput = {
33256             tag: 'input',
33257             type: 'hidden',
33258             id: Roo.id(),
33259             cls: 'hidden-number-input'
33260         };
33261         
33262         if (this.name) {
33263             hiddenInput.name = this.name;
33264         }
33265         
33266         this.name = '';
33267         
33268         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33269         
33270         this.name = hiddenInput.name;
33271         
33272         if(cfg.cn.length > 0) {
33273             cfg.cn.push(hiddenInput);
33274         }
33275         
33276         return cfg;
33277     },
33278
33279     // private
33280     initEvents : function()
33281     {   
33282         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33283         
33284         var allowed = "0123456789";
33285         
33286         if(this.allowDecimals){
33287             allowed += this.decimalSeparator;
33288         }
33289         
33290         if(this.allowNegative){
33291             allowed += "-";
33292         }
33293         
33294         if(this.thousandsDelimiter) {
33295             allowed += ",";
33296         }
33297         
33298         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33299         
33300         var keyPress = function(e){
33301             
33302             var k = e.getKey();
33303             
33304             var c = e.getCharCode();
33305             
33306             if(
33307                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33308                     allowed.indexOf(String.fromCharCode(c)) === -1
33309             ){
33310                 e.stopEvent();
33311                 return;
33312             }
33313             
33314             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33315                 return;
33316             }
33317             
33318             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33319                 e.stopEvent();
33320             }
33321         };
33322         
33323         this.el.on("keypress", keyPress, this);
33324     },
33325     
33326     validateValue : function(value)
33327     {
33328         
33329         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33330             return false;
33331         }
33332         
33333         var num = this.parseValue(value);
33334         
33335         if(isNaN(num)){
33336             this.markInvalid(String.format(this.nanText, value));
33337             return false;
33338         }
33339         
33340         if(num < this.minValue){
33341             this.markInvalid(String.format(this.minText, this.minValue));
33342             return false;
33343         }
33344         
33345         if(num > this.maxValue){
33346             this.markInvalid(String.format(this.maxText, this.maxValue));
33347             return false;
33348         }
33349         
33350         return true;
33351     },
33352
33353     getValue : function()
33354     {
33355         var v = this.hiddenEl().getValue();
33356         
33357         return this.fixPrecision(this.parseValue(v));
33358     },
33359
33360     parseValue : function(value)
33361     {
33362         if(this.thousandsDelimiter) {
33363             value += "";
33364             r = new RegExp(",", "g");
33365             value = value.replace(r, "");
33366         }
33367         
33368         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33369         return isNaN(value) ? '' : value;
33370     },
33371
33372     fixPrecision : function(value)
33373     {
33374         if(this.thousandsDelimiter) {
33375             value += "";
33376             r = new RegExp(",", "g");
33377             value = value.replace(r, "");
33378         }
33379         
33380         var nan = isNaN(value);
33381         
33382         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33383             return nan ? '' : value;
33384         }
33385         return parseFloat(value).toFixed(this.decimalPrecision);
33386     },
33387
33388     setValue : function(v)
33389     {
33390         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33391         
33392         this.value = v;
33393         
33394         if(this.rendered){
33395             
33396             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33397             
33398             this.inputEl().dom.value = (v == '') ? '' :
33399                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33400             
33401             if(!this.allowZero && v === '0') {
33402                 this.hiddenEl().dom.value = '';
33403                 this.inputEl().dom.value = '';
33404             }
33405             
33406             this.validate();
33407         }
33408     },
33409
33410     decimalPrecisionFcn : function(v)
33411     {
33412         return Math.floor(v);
33413     },
33414
33415     beforeBlur : function()
33416     {
33417         if(!this.castInt){
33418             return;
33419         }
33420         
33421         var v = this.parseValue(this.getRawValue());
33422         
33423         Roo.log("Number Field Before Blur");
33424         Roo.log(v);
33425         
33426         if(v || v === 0){
33427             this.setValue(v);
33428         }
33429     },
33430     
33431     hiddenEl : function()
33432     {
33433         return this.el.select('input.hidden-number-input',true).first();
33434     }
33435     
33436 });
33437
33438  
33439
33440 /*
33441 * Licence: LGPL
33442 */
33443
33444 /**
33445  * @class Roo.bootstrap.DocumentSlider
33446  * @extends Roo.bootstrap.Component
33447  * Bootstrap DocumentSlider class
33448  * 
33449  * @constructor
33450  * Create a new DocumentViewer
33451  * @param {Object} config The config object
33452  */
33453
33454 Roo.bootstrap.DocumentSlider = function(config){
33455     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33456     
33457     this.files = [];
33458     
33459     this.addEvents({
33460         /**
33461          * @event initial
33462          * Fire after initEvent
33463          * @param {Roo.bootstrap.DocumentSlider} this
33464          */
33465         "initial" : true,
33466         /**
33467          * @event update
33468          * Fire after update
33469          * @param {Roo.bootstrap.DocumentSlider} this
33470          */
33471         "update" : true,
33472         /**
33473          * @event click
33474          * Fire after click
33475          * @param {Roo.bootstrap.DocumentSlider} this
33476          */
33477         "click" : true
33478     });
33479 };
33480
33481 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33482     
33483     files : false,
33484     
33485     indicator : 0,
33486     
33487     getAutoCreate : function()
33488     {
33489         var cfg = {
33490             tag : 'div',
33491             cls : 'roo-document-slider',
33492             cn : [
33493                 {
33494                     tag : 'div',
33495                     cls : 'roo-document-slider-header',
33496                     cn : [
33497                         {
33498                             tag : 'div',
33499                             cls : 'roo-document-slider-header-title'
33500                         }
33501                     ]
33502                 },
33503                 {
33504                     tag : 'div',
33505                     cls : 'roo-document-slider-body',
33506                     cn : [
33507                         {
33508                             tag : 'div',
33509                             cls : 'roo-document-slider-prev',
33510                             cn : [
33511                                 {
33512                                     tag : 'i',
33513                                     cls : 'fa fa-chevron-left'
33514                                 }
33515                             ]
33516                         },
33517                         {
33518                             tag : 'div',
33519                             cls : 'roo-document-slider-thumb',
33520                             cn : [
33521                                 {
33522                                     tag : 'img',
33523                                     cls : 'roo-document-slider-image'
33524                                 }
33525                             ]
33526                         },
33527                         {
33528                             tag : 'div',
33529                             cls : 'roo-document-slider-next',
33530                             cn : [
33531                                 {
33532                                     tag : 'i',
33533                                     cls : 'fa fa-chevron-right'
33534                                 }
33535                             ]
33536                         }
33537                     ]
33538                 }
33539             ]
33540         };
33541         
33542         return cfg;
33543     },
33544     
33545     initEvents : function()
33546     {
33547         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33548         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33549         
33550         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33551         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33552         
33553         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33554         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33555         
33556         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33557         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33558         
33559         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33560         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33561         
33562         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33563         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33564         
33565         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33566         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33567         
33568         this.thumbEl.on('click', this.onClick, this);
33569         
33570         this.prevIndicator.on('click', this.prev, this);
33571         
33572         this.nextIndicator.on('click', this.next, this);
33573         
33574     },
33575     
33576     initial : function()
33577     {
33578         if(this.files.length){
33579             this.indicator = 1;
33580             this.update()
33581         }
33582         
33583         this.fireEvent('initial', this);
33584     },
33585     
33586     update : function()
33587     {
33588         this.imageEl.attr('src', this.files[this.indicator - 1]);
33589         
33590         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33591         
33592         this.prevIndicator.show();
33593         
33594         if(this.indicator == 1){
33595             this.prevIndicator.hide();
33596         }
33597         
33598         this.nextIndicator.show();
33599         
33600         if(this.indicator == this.files.length){
33601             this.nextIndicator.hide();
33602         }
33603         
33604         this.thumbEl.scrollTo('top');
33605         
33606         this.fireEvent('update', this);
33607     },
33608     
33609     onClick : function(e)
33610     {
33611         e.preventDefault();
33612         
33613         this.fireEvent('click', this);
33614     },
33615     
33616     prev : function(e)
33617     {
33618         e.preventDefault();
33619         
33620         this.indicator = Math.max(1, this.indicator - 1);
33621         
33622         this.update();
33623     },
33624     
33625     next : function(e)
33626     {
33627         e.preventDefault();
33628         
33629         this.indicator = Math.min(this.files.length, this.indicator + 1);
33630         
33631         this.update();
33632     }
33633 });
33634 /*
33635  * - LGPL
33636  *
33637  * RadioSet
33638  *
33639  *
33640  */
33641
33642 /**
33643  * @class Roo.bootstrap.RadioSet
33644  * @extends Roo.bootstrap.Input
33645  * Bootstrap RadioSet class
33646  * @cfg {String} indicatorpos (left|right) default left
33647  * @cfg {Boolean} inline (true|false) inline the element (default true)
33648  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33649  * @constructor
33650  * Create a new RadioSet
33651  * @param {Object} config The config object
33652  */
33653
33654 Roo.bootstrap.RadioSet = function(config){
33655     
33656     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33657     
33658     this.radioes = [];
33659     
33660     Roo.bootstrap.RadioSet.register(this);
33661     
33662     this.addEvents({
33663         /**
33664         * @event check
33665         * Fires when the element is checked or unchecked.
33666         * @param {Roo.bootstrap.RadioSet} this This radio
33667         * @param {Roo.bootstrap.Radio} item The checked item
33668         */
33669        check : true,
33670        /**
33671         * @event click
33672         * Fires when the element is click.
33673         * @param {Roo.bootstrap.RadioSet} this This radio set
33674         * @param {Roo.bootstrap.Radio} item The checked item
33675         * @param {Roo.EventObject} e The event object
33676         */
33677        click : true
33678     });
33679     
33680 };
33681
33682 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33683
33684     radioes : false,
33685     
33686     inline : true,
33687     
33688     weight : '',
33689     
33690     indicatorpos : 'left',
33691     
33692     getAutoCreate : function()
33693     {
33694         var label = {
33695             tag : 'label',
33696             cls : 'roo-radio-set-label',
33697             cn : [
33698                 {
33699                     tag : 'span',
33700                     html : this.fieldLabel
33701                 }
33702             ]
33703         };
33704         
33705         if(this.indicatorpos == 'left'){
33706             label.cn.unshift({
33707                 tag : 'i',
33708                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33709                 tooltip : 'This field is required'
33710             });
33711         } else {
33712             label.cn.push({
33713                 tag : 'i',
33714                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33715                 tooltip : 'This field is required'
33716             });
33717         }
33718         
33719         var items = {
33720             tag : 'div',
33721             cls : 'roo-radio-set-items'
33722         };
33723         
33724         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33725         
33726         if (align === 'left' && this.fieldLabel.length) {
33727             
33728             items = {
33729                 cls : "roo-radio-set-right", 
33730                 cn: [
33731                     items
33732                 ]
33733             };
33734             
33735             if(this.labelWidth > 12){
33736                 label.style = "width: " + this.labelWidth + 'px';
33737             }
33738             
33739             if(this.labelWidth < 13 && this.labelmd == 0){
33740                 this.labelmd = this.labelWidth;
33741             }
33742             
33743             if(this.labellg > 0){
33744                 label.cls += ' col-lg-' + this.labellg;
33745                 items.cls += ' col-lg-' + (12 - this.labellg);
33746             }
33747             
33748             if(this.labelmd > 0){
33749                 label.cls += ' col-md-' + this.labelmd;
33750                 items.cls += ' col-md-' + (12 - this.labelmd);
33751             }
33752             
33753             if(this.labelsm > 0){
33754                 label.cls += ' col-sm-' + this.labelsm;
33755                 items.cls += ' col-sm-' + (12 - this.labelsm);
33756             }
33757             
33758             if(this.labelxs > 0){
33759                 label.cls += ' col-xs-' + this.labelxs;
33760                 items.cls += ' col-xs-' + (12 - this.labelxs);
33761             }
33762         }
33763         
33764         var cfg = {
33765             tag : 'div',
33766             cls : 'roo-radio-set',
33767             cn : [
33768                 {
33769                     tag : 'input',
33770                     cls : 'roo-radio-set-input',
33771                     type : 'hidden',
33772                     name : this.name,
33773                     value : this.value ? this.value :  ''
33774                 },
33775                 label,
33776                 items
33777             ]
33778         };
33779         
33780         if(this.weight.length){
33781             cfg.cls += ' roo-radio-' + this.weight;
33782         }
33783         
33784         if(this.inline) {
33785             cfg.cls += ' roo-radio-set-inline';
33786         }
33787         
33788         var settings=this;
33789         ['xs','sm','md','lg'].map(function(size){
33790             if (settings[size]) {
33791                 cfg.cls += ' col-' + size + '-' + settings[size];
33792             }
33793         });
33794         
33795         return cfg;
33796         
33797     },
33798
33799     initEvents : function()
33800     {
33801         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33802         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33803         
33804         if(!this.fieldLabel.length){
33805             this.labelEl.hide();
33806         }
33807         
33808         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33809         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33810         
33811         this.indicator = this.indicatorEl();
33812         
33813         if(this.indicator){
33814             this.indicator.addClass('invisible');
33815         }
33816         
33817         this.originalValue = this.getValue();
33818         
33819     },
33820     
33821     inputEl: function ()
33822     {
33823         return this.el.select('.roo-radio-set-input', true).first();
33824     },
33825     
33826     getChildContainer : function()
33827     {
33828         return this.itemsEl;
33829     },
33830     
33831     register : function(item)
33832     {
33833         this.radioes.push(item);
33834         
33835     },
33836     
33837     validate : function()
33838     {   
33839         if(this.getVisibilityEl().hasClass('hidden')){
33840             return true;
33841         }
33842         
33843         var valid = false;
33844         
33845         Roo.each(this.radioes, function(i){
33846             if(!i.checked){
33847                 return;
33848             }
33849             
33850             valid = true;
33851             return false;
33852         });
33853         
33854         if(this.allowBlank) {
33855             return true;
33856         }
33857         
33858         if(this.disabled || valid){
33859             this.markValid();
33860             return true;
33861         }
33862         
33863         this.markInvalid();
33864         return false;
33865         
33866     },
33867     
33868     markValid : function()
33869     {
33870         if(this.labelEl.isVisible(true)){
33871             this.indicatorEl().removeClass('visible');
33872             this.indicatorEl().addClass('invisible');
33873         }
33874         
33875         this.el.removeClass([this.invalidClass, this.validClass]);
33876         this.el.addClass(this.validClass);
33877         
33878         this.fireEvent('valid', this);
33879     },
33880     
33881     markInvalid : function(msg)
33882     {
33883         if(this.allowBlank || this.disabled){
33884             return;
33885         }
33886         
33887         if(this.labelEl.isVisible(true)){
33888             this.indicatorEl().removeClass('invisible');
33889             this.indicatorEl().addClass('visible');
33890         }
33891         
33892         this.el.removeClass([this.invalidClass, this.validClass]);
33893         this.el.addClass(this.invalidClass);
33894         
33895         this.fireEvent('invalid', this, msg);
33896         
33897     },
33898     
33899     setValue : function(v, suppressEvent)
33900     {   
33901         if(this.value === v){
33902             return;
33903         }
33904         
33905         this.value = v;
33906         
33907         if(this.rendered){
33908             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33909         }
33910         
33911         Roo.each(this.radioes, function(i){
33912             i.checked = false;
33913             i.el.removeClass('checked');
33914         });
33915         
33916         Roo.each(this.radioes, function(i){
33917             
33918             if(i.value === v || i.value.toString() === v.toString()){
33919                 i.checked = true;
33920                 i.el.addClass('checked');
33921                 
33922                 if(suppressEvent !== true){
33923                     this.fireEvent('check', this, i);
33924                 }
33925                 
33926                 return false;
33927             }
33928             
33929         }, this);
33930         
33931         this.validate();
33932     },
33933     
33934     clearInvalid : function(){
33935         
33936         if(!this.el || this.preventMark){
33937             return;
33938         }
33939         
33940         this.el.removeClass([this.invalidClass]);
33941         
33942         this.fireEvent('valid', this);
33943     }
33944     
33945 });
33946
33947 Roo.apply(Roo.bootstrap.RadioSet, {
33948     
33949     groups: {},
33950     
33951     register : function(set)
33952     {
33953         this.groups[set.name] = set;
33954     },
33955     
33956     get: function(name) 
33957     {
33958         if (typeof(this.groups[name]) == 'undefined') {
33959             return false;
33960         }
33961         
33962         return this.groups[name] ;
33963     }
33964     
33965 });
33966 /*
33967  * Based on:
33968  * Ext JS Library 1.1.1
33969  * Copyright(c) 2006-2007, Ext JS, LLC.
33970  *
33971  * Originally Released Under LGPL - original licence link has changed is not relivant.
33972  *
33973  * Fork - LGPL
33974  * <script type="text/javascript">
33975  */
33976
33977
33978 /**
33979  * @class Roo.bootstrap.SplitBar
33980  * @extends Roo.util.Observable
33981  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33982  * <br><br>
33983  * Usage:
33984  * <pre><code>
33985 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33986                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33987 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33988 split.minSize = 100;
33989 split.maxSize = 600;
33990 split.animate = true;
33991 split.on('moved', splitterMoved);
33992 </code></pre>
33993  * @constructor
33994  * Create a new SplitBar
33995  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33996  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33997  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33998  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33999                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34000                         position of the SplitBar).
34001  */
34002 Roo.bootstrap.SplitBar = function(cfg){
34003     
34004     /** @private */
34005     
34006     //{
34007     //  dragElement : elm
34008     //  resizingElement: el,
34009         // optional..
34010     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34011     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34012         // existingProxy ???
34013     //}
34014     
34015     this.el = Roo.get(cfg.dragElement, true);
34016     this.el.dom.unselectable = "on";
34017     /** @private */
34018     this.resizingEl = Roo.get(cfg.resizingElement, true);
34019
34020     /**
34021      * @private
34022      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34023      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34024      * @type Number
34025      */
34026     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34027     
34028     /**
34029      * The minimum size of the resizing element. (Defaults to 0)
34030      * @type Number
34031      */
34032     this.minSize = 0;
34033     
34034     /**
34035      * The maximum size of the resizing element. (Defaults to 2000)
34036      * @type Number
34037      */
34038     this.maxSize = 2000;
34039     
34040     /**
34041      * Whether to animate the transition to the new size
34042      * @type Boolean
34043      */
34044     this.animate = false;
34045     
34046     /**
34047      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34048      * @type Boolean
34049      */
34050     this.useShim = false;
34051     
34052     /** @private */
34053     this.shim = null;
34054     
34055     if(!cfg.existingProxy){
34056         /** @private */
34057         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34058     }else{
34059         this.proxy = Roo.get(cfg.existingProxy).dom;
34060     }
34061     /** @private */
34062     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34063     
34064     /** @private */
34065     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34066     
34067     /** @private */
34068     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34069     
34070     /** @private */
34071     this.dragSpecs = {};
34072     
34073     /**
34074      * @private The adapter to use to positon and resize elements
34075      */
34076     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34077     this.adapter.init(this);
34078     
34079     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34080         /** @private */
34081         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34082         this.el.addClass("roo-splitbar-h");
34083     }else{
34084         /** @private */
34085         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34086         this.el.addClass("roo-splitbar-v");
34087     }
34088     
34089     this.addEvents({
34090         /**
34091          * @event resize
34092          * Fires when the splitter is moved (alias for {@link #event-moved})
34093          * @param {Roo.bootstrap.SplitBar} this
34094          * @param {Number} newSize the new width or height
34095          */
34096         "resize" : true,
34097         /**
34098          * @event moved
34099          * Fires when the splitter is moved
34100          * @param {Roo.bootstrap.SplitBar} this
34101          * @param {Number} newSize the new width or height
34102          */
34103         "moved" : true,
34104         /**
34105          * @event beforeresize
34106          * Fires before the splitter is dragged
34107          * @param {Roo.bootstrap.SplitBar} this
34108          */
34109         "beforeresize" : true,
34110
34111         "beforeapply" : true
34112     });
34113
34114     Roo.util.Observable.call(this);
34115 };
34116
34117 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34118     onStartProxyDrag : function(x, y){
34119         this.fireEvent("beforeresize", this);
34120         if(!this.overlay){
34121             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34122             o.unselectable();
34123             o.enableDisplayMode("block");
34124             // all splitbars share the same overlay
34125             Roo.bootstrap.SplitBar.prototype.overlay = o;
34126         }
34127         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34128         this.overlay.show();
34129         Roo.get(this.proxy).setDisplayed("block");
34130         var size = this.adapter.getElementSize(this);
34131         this.activeMinSize = this.getMinimumSize();;
34132         this.activeMaxSize = this.getMaximumSize();;
34133         var c1 = size - this.activeMinSize;
34134         var c2 = Math.max(this.activeMaxSize - size, 0);
34135         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34136             this.dd.resetConstraints();
34137             this.dd.setXConstraint(
34138                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34139                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34140             );
34141             this.dd.setYConstraint(0, 0);
34142         }else{
34143             this.dd.resetConstraints();
34144             this.dd.setXConstraint(0, 0);
34145             this.dd.setYConstraint(
34146                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34147                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34148             );
34149          }
34150         this.dragSpecs.startSize = size;
34151         this.dragSpecs.startPoint = [x, y];
34152         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34153     },
34154     
34155     /** 
34156      * @private Called after the drag operation by the DDProxy
34157      */
34158     onEndProxyDrag : function(e){
34159         Roo.get(this.proxy).setDisplayed(false);
34160         var endPoint = Roo.lib.Event.getXY(e);
34161         if(this.overlay){
34162             this.overlay.hide();
34163         }
34164         var newSize;
34165         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34166             newSize = this.dragSpecs.startSize + 
34167                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34168                     endPoint[0] - this.dragSpecs.startPoint[0] :
34169                     this.dragSpecs.startPoint[0] - endPoint[0]
34170                 );
34171         }else{
34172             newSize = this.dragSpecs.startSize + 
34173                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34174                     endPoint[1] - this.dragSpecs.startPoint[1] :
34175                     this.dragSpecs.startPoint[1] - endPoint[1]
34176                 );
34177         }
34178         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34179         if(newSize != this.dragSpecs.startSize){
34180             if(this.fireEvent('beforeapply', this, newSize) !== false){
34181                 this.adapter.setElementSize(this, newSize);
34182                 this.fireEvent("moved", this, newSize);
34183                 this.fireEvent("resize", this, newSize);
34184             }
34185         }
34186     },
34187     
34188     /**
34189      * Get the adapter this SplitBar uses
34190      * @return The adapter object
34191      */
34192     getAdapter : function(){
34193         return this.adapter;
34194     },
34195     
34196     /**
34197      * Set the adapter this SplitBar uses
34198      * @param {Object} adapter A SplitBar adapter object
34199      */
34200     setAdapter : function(adapter){
34201         this.adapter = adapter;
34202         this.adapter.init(this);
34203     },
34204     
34205     /**
34206      * Gets the minimum size for the resizing element
34207      * @return {Number} The minimum size
34208      */
34209     getMinimumSize : function(){
34210         return this.minSize;
34211     },
34212     
34213     /**
34214      * Sets the minimum size for the resizing element
34215      * @param {Number} minSize The minimum size
34216      */
34217     setMinimumSize : function(minSize){
34218         this.minSize = minSize;
34219     },
34220     
34221     /**
34222      * Gets the maximum size for the resizing element
34223      * @return {Number} The maximum size
34224      */
34225     getMaximumSize : function(){
34226         return this.maxSize;
34227     },
34228     
34229     /**
34230      * Sets the maximum size for the resizing element
34231      * @param {Number} maxSize The maximum size
34232      */
34233     setMaximumSize : function(maxSize){
34234         this.maxSize = maxSize;
34235     },
34236     
34237     /**
34238      * Sets the initialize size for the resizing element
34239      * @param {Number} size The initial size
34240      */
34241     setCurrentSize : function(size){
34242         var oldAnimate = this.animate;
34243         this.animate = false;
34244         this.adapter.setElementSize(this, size);
34245         this.animate = oldAnimate;
34246     },
34247     
34248     /**
34249      * Destroy this splitbar. 
34250      * @param {Boolean} removeEl True to remove the element
34251      */
34252     destroy : function(removeEl){
34253         if(this.shim){
34254             this.shim.remove();
34255         }
34256         this.dd.unreg();
34257         this.proxy.parentNode.removeChild(this.proxy);
34258         if(removeEl){
34259             this.el.remove();
34260         }
34261     }
34262 });
34263
34264 /**
34265  * @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.
34266  */
34267 Roo.bootstrap.SplitBar.createProxy = function(dir){
34268     var proxy = new Roo.Element(document.createElement("div"));
34269     proxy.unselectable();
34270     var cls = 'roo-splitbar-proxy';
34271     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34272     document.body.appendChild(proxy.dom);
34273     return proxy.dom;
34274 };
34275
34276 /** 
34277  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34278  * Default Adapter. It assumes the splitter and resizing element are not positioned
34279  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34280  */
34281 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34282 };
34283
34284 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34285     // do nothing for now
34286     init : function(s){
34287     
34288     },
34289     /**
34290      * Called before drag operations to get the current size of the resizing element. 
34291      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34292      */
34293      getElementSize : function(s){
34294         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34295             return s.resizingEl.getWidth();
34296         }else{
34297             return s.resizingEl.getHeight();
34298         }
34299     },
34300     
34301     /**
34302      * Called after drag operations to set the size of the resizing element.
34303      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34304      * @param {Number} newSize The new size to set
34305      * @param {Function} onComplete A function to be invoked when resizing is complete
34306      */
34307     setElementSize : function(s, newSize, onComplete){
34308         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34309             if(!s.animate){
34310                 s.resizingEl.setWidth(newSize);
34311                 if(onComplete){
34312                     onComplete(s, newSize);
34313                 }
34314             }else{
34315                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34316             }
34317         }else{
34318             
34319             if(!s.animate){
34320                 s.resizingEl.setHeight(newSize);
34321                 if(onComplete){
34322                     onComplete(s, newSize);
34323                 }
34324             }else{
34325                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34326             }
34327         }
34328     }
34329 };
34330
34331 /** 
34332  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34333  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34334  * Adapter that  moves the splitter element to align with the resized sizing element. 
34335  * Used with an absolute positioned SplitBar.
34336  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34337  * document.body, make sure you assign an id to the body element.
34338  */
34339 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34340     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34341     this.container = Roo.get(container);
34342 };
34343
34344 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34345     init : function(s){
34346         this.basic.init(s);
34347     },
34348     
34349     getElementSize : function(s){
34350         return this.basic.getElementSize(s);
34351     },
34352     
34353     setElementSize : function(s, newSize, onComplete){
34354         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34355     },
34356     
34357     moveSplitter : function(s){
34358         var yes = Roo.bootstrap.SplitBar;
34359         switch(s.placement){
34360             case yes.LEFT:
34361                 s.el.setX(s.resizingEl.getRight());
34362                 break;
34363             case yes.RIGHT:
34364                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34365                 break;
34366             case yes.TOP:
34367                 s.el.setY(s.resizingEl.getBottom());
34368                 break;
34369             case yes.BOTTOM:
34370                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34371                 break;
34372         }
34373     }
34374 };
34375
34376 /**
34377  * Orientation constant - Create a vertical SplitBar
34378  * @static
34379  * @type Number
34380  */
34381 Roo.bootstrap.SplitBar.VERTICAL = 1;
34382
34383 /**
34384  * Orientation constant - Create a horizontal SplitBar
34385  * @static
34386  * @type Number
34387  */
34388 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34389
34390 /**
34391  * Placement constant - The resizing element is to the left of the splitter element
34392  * @static
34393  * @type Number
34394  */
34395 Roo.bootstrap.SplitBar.LEFT = 1;
34396
34397 /**
34398  * Placement constant - The resizing element is to the right of the splitter element
34399  * @static
34400  * @type Number
34401  */
34402 Roo.bootstrap.SplitBar.RIGHT = 2;
34403
34404 /**
34405  * Placement constant - The resizing element is positioned above the splitter element
34406  * @static
34407  * @type Number
34408  */
34409 Roo.bootstrap.SplitBar.TOP = 3;
34410
34411 /**
34412  * Placement constant - The resizing element is positioned under splitter element
34413  * @static
34414  * @type Number
34415  */
34416 Roo.bootstrap.SplitBar.BOTTOM = 4;
34417 Roo.namespace("Roo.bootstrap.layout");/*
34418  * Based on:
34419  * Ext JS Library 1.1.1
34420  * Copyright(c) 2006-2007, Ext JS, LLC.
34421  *
34422  * Originally Released Under LGPL - original licence link has changed is not relivant.
34423  *
34424  * Fork - LGPL
34425  * <script type="text/javascript">
34426  */
34427
34428 /**
34429  * @class Roo.bootstrap.layout.Manager
34430  * @extends Roo.bootstrap.Component
34431  * Base class for layout managers.
34432  */
34433 Roo.bootstrap.layout.Manager = function(config)
34434 {
34435     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34436
34437
34438
34439
34440
34441     /** false to disable window resize monitoring @type Boolean */
34442     this.monitorWindowResize = true;
34443     this.regions = {};
34444     this.addEvents({
34445         /**
34446          * @event layout
34447          * Fires when a layout is performed.
34448          * @param {Roo.LayoutManager} this
34449          */
34450         "layout" : true,
34451         /**
34452          * @event regionresized
34453          * Fires when the user resizes a region.
34454          * @param {Roo.LayoutRegion} region The resized region
34455          * @param {Number} newSize The new size (width for east/west, height for north/south)
34456          */
34457         "regionresized" : true,
34458         /**
34459          * @event regioncollapsed
34460          * Fires when a region is collapsed.
34461          * @param {Roo.LayoutRegion} region The collapsed region
34462          */
34463         "regioncollapsed" : true,
34464         /**
34465          * @event regionexpanded
34466          * Fires when a region is expanded.
34467          * @param {Roo.LayoutRegion} region The expanded region
34468          */
34469         "regionexpanded" : true
34470     });
34471     this.updating = false;
34472
34473     if (config.el) {
34474         this.el = Roo.get(config.el);
34475         this.initEvents();
34476     }
34477
34478 };
34479
34480 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34481
34482
34483     regions : null,
34484
34485     monitorWindowResize : true,
34486
34487
34488     updating : false,
34489
34490
34491     onRender : function(ct, position)
34492     {
34493         if(!this.el){
34494             this.el = Roo.get(ct);
34495             this.initEvents();
34496         }
34497         //this.fireEvent('render',this);
34498     },
34499
34500
34501     initEvents: function()
34502     {
34503
34504
34505         // ie scrollbar fix
34506         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34507             document.body.scroll = "no";
34508         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34509             this.el.position('relative');
34510         }
34511         this.id = this.el.id;
34512         this.el.addClass("roo-layout-container");
34513         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34514         if(this.el.dom != document.body ) {
34515             this.el.on('resize', this.layout,this);
34516             this.el.on('show', this.layout,this);
34517         }
34518
34519     },
34520
34521     /**
34522      * Returns true if this layout is currently being updated
34523      * @return {Boolean}
34524      */
34525     isUpdating : function(){
34526         return this.updating;
34527     },
34528
34529     /**
34530      * Suspend the LayoutManager from doing auto-layouts while
34531      * making multiple add or remove calls
34532      */
34533     beginUpdate : function(){
34534         this.updating = true;
34535     },
34536
34537     /**
34538      * Restore auto-layouts and optionally disable the manager from performing a layout
34539      * @param {Boolean} noLayout true to disable a layout update
34540      */
34541     endUpdate : function(noLayout){
34542         this.updating = false;
34543         if(!noLayout){
34544             this.layout();
34545         }
34546     },
34547
34548     layout: function(){
34549         // abstract...
34550     },
34551
34552     onRegionResized : function(region, newSize){
34553         this.fireEvent("regionresized", region, newSize);
34554         this.layout();
34555     },
34556
34557     onRegionCollapsed : function(region){
34558         this.fireEvent("regioncollapsed", region);
34559     },
34560
34561     onRegionExpanded : function(region){
34562         this.fireEvent("regionexpanded", region);
34563     },
34564
34565     /**
34566      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34567      * performs box-model adjustments.
34568      * @return {Object} The size as an object {width: (the width), height: (the height)}
34569      */
34570     getViewSize : function()
34571     {
34572         var size;
34573         if(this.el.dom != document.body){
34574             size = this.el.getSize();
34575         }else{
34576             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34577         }
34578         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34579         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34580         return size;
34581     },
34582
34583     /**
34584      * Returns the Element this layout is bound to.
34585      * @return {Roo.Element}
34586      */
34587     getEl : function(){
34588         return this.el;
34589     },
34590
34591     /**
34592      * Returns the specified region.
34593      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34594      * @return {Roo.LayoutRegion}
34595      */
34596     getRegion : function(target){
34597         return this.regions[target.toLowerCase()];
34598     },
34599
34600     onWindowResize : function(){
34601         if(this.monitorWindowResize){
34602             this.layout();
34603         }
34604     }
34605 });
34606 /*
34607  * Based on:
34608  * Ext JS Library 1.1.1
34609  * Copyright(c) 2006-2007, Ext JS, LLC.
34610  *
34611  * Originally Released Under LGPL - original licence link has changed is not relivant.
34612  *
34613  * Fork - LGPL
34614  * <script type="text/javascript">
34615  */
34616 /**
34617  * @class Roo.bootstrap.layout.Border
34618  * @extends Roo.bootstrap.layout.Manager
34619  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34620  * please see: examples/bootstrap/nested.html<br><br>
34621  
34622 <b>The container the layout is rendered into can be either the body element or any other element.
34623 If it is not the body element, the container needs to either be an absolute positioned element,
34624 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34625 the container size if it is not the body element.</b>
34626
34627 * @constructor
34628 * Create a new Border
34629 * @param {Object} config Configuration options
34630  */
34631 Roo.bootstrap.layout.Border = function(config){
34632     config = config || {};
34633     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34634     
34635     
34636     
34637     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34638         if(config[region]){
34639             config[region].region = region;
34640             this.addRegion(config[region]);
34641         }
34642     },this);
34643     
34644 };
34645
34646 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34647
34648 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34649     /**
34650      * Creates and adds a new region if it doesn't already exist.
34651      * @param {String} target The target region key (north, south, east, west or center).
34652      * @param {Object} config The regions config object
34653      * @return {BorderLayoutRegion} The new region
34654      */
34655     addRegion : function(config)
34656     {
34657         if(!this.regions[config.region]){
34658             var r = this.factory(config);
34659             this.bindRegion(r);
34660         }
34661         return this.regions[config.region];
34662     },
34663
34664     // private (kinda)
34665     bindRegion : function(r){
34666         this.regions[r.config.region] = r;
34667         
34668         r.on("visibilitychange",    this.layout, this);
34669         r.on("paneladded",          this.layout, this);
34670         r.on("panelremoved",        this.layout, this);
34671         r.on("invalidated",         this.layout, this);
34672         r.on("resized",             this.onRegionResized, this);
34673         r.on("collapsed",           this.onRegionCollapsed, this);
34674         r.on("expanded",            this.onRegionExpanded, this);
34675     },
34676
34677     /**
34678      * Performs a layout update.
34679      */
34680     layout : function()
34681     {
34682         if(this.updating) {
34683             return;
34684         }
34685         
34686         // render all the rebions if they have not been done alreayd?
34687         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34688             if(this.regions[region] && !this.regions[region].bodyEl){
34689                 this.regions[region].onRender(this.el)
34690             }
34691         },this);
34692         
34693         var size = this.getViewSize();
34694         var w = size.width;
34695         var h = size.height;
34696         var centerW = w;
34697         var centerH = h;
34698         var centerY = 0;
34699         var centerX = 0;
34700         //var x = 0, y = 0;
34701
34702         var rs = this.regions;
34703         var north = rs["north"];
34704         var south = rs["south"]; 
34705         var west = rs["west"];
34706         var east = rs["east"];
34707         var center = rs["center"];
34708         //if(this.hideOnLayout){ // not supported anymore
34709             //c.el.setStyle("display", "none");
34710         //}
34711         if(north && north.isVisible()){
34712             var b = north.getBox();
34713             var m = north.getMargins();
34714             b.width = w - (m.left+m.right);
34715             b.x = m.left;
34716             b.y = m.top;
34717             centerY = b.height + b.y + m.bottom;
34718             centerH -= centerY;
34719             north.updateBox(this.safeBox(b));
34720         }
34721         if(south && south.isVisible()){
34722             var b = south.getBox();
34723             var m = south.getMargins();
34724             b.width = w - (m.left+m.right);
34725             b.x = m.left;
34726             var totalHeight = (b.height + m.top + m.bottom);
34727             b.y = h - totalHeight + m.top;
34728             centerH -= totalHeight;
34729             south.updateBox(this.safeBox(b));
34730         }
34731         if(west && west.isVisible()){
34732             var b = west.getBox();
34733             var m = west.getMargins();
34734             b.height = centerH - (m.top+m.bottom);
34735             b.x = m.left;
34736             b.y = centerY + m.top;
34737             var totalWidth = (b.width + m.left + m.right);
34738             centerX += totalWidth;
34739             centerW -= totalWidth;
34740             west.updateBox(this.safeBox(b));
34741         }
34742         if(east && east.isVisible()){
34743             var b = east.getBox();
34744             var m = east.getMargins();
34745             b.height = centerH - (m.top+m.bottom);
34746             var totalWidth = (b.width + m.left + m.right);
34747             b.x = w - totalWidth + m.left;
34748             b.y = centerY + m.top;
34749             centerW -= totalWidth;
34750             east.updateBox(this.safeBox(b));
34751         }
34752         if(center){
34753             var m = center.getMargins();
34754             var centerBox = {
34755                 x: centerX + m.left,
34756                 y: centerY + m.top,
34757                 width: centerW - (m.left+m.right),
34758                 height: centerH - (m.top+m.bottom)
34759             };
34760             //if(this.hideOnLayout){
34761                 //center.el.setStyle("display", "block");
34762             //}
34763             center.updateBox(this.safeBox(centerBox));
34764         }
34765         this.el.repaint();
34766         this.fireEvent("layout", this);
34767     },
34768
34769     // private
34770     safeBox : function(box){
34771         box.width = Math.max(0, box.width);
34772         box.height = Math.max(0, box.height);
34773         return box;
34774     },
34775
34776     /**
34777      * Adds a ContentPanel (or subclass) to this layout.
34778      * @param {String} target The target region key (north, south, east, west or center).
34779      * @param {Roo.ContentPanel} panel The panel to add
34780      * @return {Roo.ContentPanel} The added panel
34781      */
34782     add : function(target, panel){
34783          
34784         target = target.toLowerCase();
34785         return this.regions[target].add(panel);
34786     },
34787
34788     /**
34789      * Remove a ContentPanel (or subclass) to this layout.
34790      * @param {String} target The target region key (north, south, east, west or center).
34791      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34792      * @return {Roo.ContentPanel} The removed panel
34793      */
34794     remove : function(target, panel){
34795         target = target.toLowerCase();
34796         return this.regions[target].remove(panel);
34797     },
34798
34799     /**
34800      * Searches all regions for a panel with the specified id
34801      * @param {String} panelId
34802      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34803      */
34804     findPanel : function(panelId){
34805         var rs = this.regions;
34806         for(var target in rs){
34807             if(typeof rs[target] != "function"){
34808                 var p = rs[target].getPanel(panelId);
34809                 if(p){
34810                     return p;
34811                 }
34812             }
34813         }
34814         return null;
34815     },
34816
34817     /**
34818      * Searches all regions for a panel with the specified id and activates (shows) it.
34819      * @param {String/ContentPanel} panelId The panels id or the panel itself
34820      * @return {Roo.ContentPanel} The shown panel or null
34821      */
34822     showPanel : function(panelId) {
34823       var rs = this.regions;
34824       for(var target in rs){
34825          var r = rs[target];
34826          if(typeof r != "function"){
34827             if(r.hasPanel(panelId)){
34828                return r.showPanel(panelId);
34829             }
34830          }
34831       }
34832       return null;
34833    },
34834
34835    /**
34836      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34837      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34838      */
34839    /*
34840     restoreState : function(provider){
34841         if(!provider){
34842             provider = Roo.state.Manager;
34843         }
34844         var sm = new Roo.LayoutStateManager();
34845         sm.init(this, provider);
34846     },
34847 */
34848  
34849  
34850     /**
34851      * Adds a xtype elements to the layout.
34852      * <pre><code>
34853
34854 layout.addxtype({
34855        xtype : 'ContentPanel',
34856        region: 'west',
34857        items: [ .... ]
34858    }
34859 );
34860
34861 layout.addxtype({
34862         xtype : 'NestedLayoutPanel',
34863         region: 'west',
34864         layout: {
34865            center: { },
34866            west: { }   
34867         },
34868         items : [ ... list of content panels or nested layout panels.. ]
34869    }
34870 );
34871 </code></pre>
34872      * @param {Object} cfg Xtype definition of item to add.
34873      */
34874     addxtype : function(cfg)
34875     {
34876         // basically accepts a pannel...
34877         // can accept a layout region..!?!?
34878         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34879         
34880         
34881         // theory?  children can only be panels??
34882         
34883         //if (!cfg.xtype.match(/Panel$/)) {
34884         //    return false;
34885         //}
34886         var ret = false;
34887         
34888         if (typeof(cfg.region) == 'undefined') {
34889             Roo.log("Failed to add Panel, region was not set");
34890             Roo.log(cfg);
34891             return false;
34892         }
34893         var region = cfg.region;
34894         delete cfg.region;
34895         
34896           
34897         var xitems = [];
34898         if (cfg.items) {
34899             xitems = cfg.items;
34900             delete cfg.items;
34901         }
34902         var nb = false;
34903         
34904         switch(cfg.xtype) 
34905         {
34906             case 'Content':  // ContentPanel (el, cfg)
34907             case 'Scroll':  // ContentPanel (el, cfg)
34908             case 'View': 
34909                 cfg.autoCreate = true;
34910                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34911                 //} else {
34912                 //    var el = this.el.createChild();
34913                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34914                 //}
34915                 
34916                 this.add(region, ret);
34917                 break;
34918             
34919             /*
34920             case 'TreePanel': // our new panel!
34921                 cfg.el = this.el.createChild();
34922                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34923                 this.add(region, ret);
34924                 break;
34925             */
34926             
34927             case 'Nest': 
34928                 // create a new Layout (which is  a Border Layout...
34929                 
34930                 var clayout = cfg.layout;
34931                 clayout.el  = this.el.createChild();
34932                 clayout.items   = clayout.items  || [];
34933                 
34934                 delete cfg.layout;
34935                 
34936                 // replace this exitems with the clayout ones..
34937                 xitems = clayout.items;
34938                  
34939                 // force background off if it's in center...
34940                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34941                     cfg.background = false;
34942                 }
34943                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34944                 
34945                 
34946                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34947                 //console.log('adding nested layout panel '  + cfg.toSource());
34948                 this.add(region, ret);
34949                 nb = {}; /// find first...
34950                 break;
34951             
34952             case 'Grid':
34953                 
34954                 // needs grid and region
34955                 
34956                 //var el = this.getRegion(region).el.createChild();
34957                 /*
34958                  *var el = this.el.createChild();
34959                 // create the grid first...
34960                 cfg.grid.container = el;
34961                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34962                 */
34963                 
34964                 if (region == 'center' && this.active ) {
34965                     cfg.background = false;
34966                 }
34967                 
34968                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34969                 
34970                 this.add(region, ret);
34971                 /*
34972                 if (cfg.background) {
34973                     // render grid on panel activation (if panel background)
34974                     ret.on('activate', function(gp) {
34975                         if (!gp.grid.rendered) {
34976                     //        gp.grid.render(el);
34977                         }
34978                     });
34979                 } else {
34980                   //  cfg.grid.render(el);
34981                 }
34982                 */
34983                 break;
34984            
34985            
34986             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34987                 // it was the old xcomponent building that caused this before.
34988                 // espeically if border is the top element in the tree.
34989                 ret = this;
34990                 break; 
34991                 
34992                     
34993                 
34994                 
34995                 
34996             default:
34997                 /*
34998                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34999                     
35000                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35001                     this.add(region, ret);
35002                 } else {
35003                 */
35004                     Roo.log(cfg);
35005                     throw "Can not add '" + cfg.xtype + "' to Border";
35006                     return null;
35007              
35008                                 
35009              
35010         }
35011         this.beginUpdate();
35012         // add children..
35013         var region = '';
35014         var abn = {};
35015         Roo.each(xitems, function(i)  {
35016             region = nb && i.region ? i.region : false;
35017             
35018             var add = ret.addxtype(i);
35019            
35020             if (region) {
35021                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35022                 if (!i.background) {
35023                     abn[region] = nb[region] ;
35024                 }
35025             }
35026             
35027         });
35028         this.endUpdate();
35029
35030         // make the last non-background panel active..
35031         //if (nb) { Roo.log(abn); }
35032         if (nb) {
35033             
35034             for(var r in abn) {
35035                 region = this.getRegion(r);
35036                 if (region) {
35037                     // tried using nb[r], but it does not work..
35038                      
35039                     region.showPanel(abn[r]);
35040                    
35041                 }
35042             }
35043         }
35044         return ret;
35045         
35046     },
35047     
35048     
35049 // private
35050     factory : function(cfg)
35051     {
35052         
35053         var validRegions = Roo.bootstrap.layout.Border.regions;
35054
35055         var target = cfg.region;
35056         cfg.mgr = this;
35057         
35058         var r = Roo.bootstrap.layout;
35059         Roo.log(target);
35060         switch(target){
35061             case "north":
35062                 return new r.North(cfg);
35063             case "south":
35064                 return new r.South(cfg);
35065             case "east":
35066                 return new r.East(cfg);
35067             case "west":
35068                 return new r.West(cfg);
35069             case "center":
35070                 return new r.Center(cfg);
35071         }
35072         throw 'Layout region "'+target+'" not supported.';
35073     }
35074     
35075     
35076 });
35077  /*
35078  * Based on:
35079  * Ext JS Library 1.1.1
35080  * Copyright(c) 2006-2007, Ext JS, LLC.
35081  *
35082  * Originally Released Under LGPL - original licence link has changed is not relivant.
35083  *
35084  * Fork - LGPL
35085  * <script type="text/javascript">
35086  */
35087  
35088 /**
35089  * @class Roo.bootstrap.layout.Basic
35090  * @extends Roo.util.Observable
35091  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35092  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35093  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35094  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35095  * @cfg {string}   region  the region that it inhabits..
35096  * @cfg {bool}   skipConfig skip config?
35097  * 
35098
35099  */
35100 Roo.bootstrap.layout.Basic = function(config){
35101     
35102     this.mgr = config.mgr;
35103     
35104     this.position = config.region;
35105     
35106     var skipConfig = config.skipConfig;
35107     
35108     this.events = {
35109         /**
35110          * @scope Roo.BasicLayoutRegion
35111          */
35112         
35113         /**
35114          * @event beforeremove
35115          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35116          * @param {Roo.LayoutRegion} this
35117          * @param {Roo.ContentPanel} panel The panel
35118          * @param {Object} e The cancel event object
35119          */
35120         "beforeremove" : true,
35121         /**
35122          * @event invalidated
35123          * Fires when the layout for this region is changed.
35124          * @param {Roo.LayoutRegion} this
35125          */
35126         "invalidated" : true,
35127         /**
35128          * @event visibilitychange
35129          * Fires when this region is shown or hidden 
35130          * @param {Roo.LayoutRegion} this
35131          * @param {Boolean} visibility true or false
35132          */
35133         "visibilitychange" : true,
35134         /**
35135          * @event paneladded
35136          * Fires when a panel is added. 
35137          * @param {Roo.LayoutRegion} this
35138          * @param {Roo.ContentPanel} panel The panel
35139          */
35140         "paneladded" : true,
35141         /**
35142          * @event panelremoved
35143          * Fires when a panel is removed. 
35144          * @param {Roo.LayoutRegion} this
35145          * @param {Roo.ContentPanel} panel The panel
35146          */
35147         "panelremoved" : true,
35148         /**
35149          * @event beforecollapse
35150          * Fires when this region before collapse.
35151          * @param {Roo.LayoutRegion} this
35152          */
35153         "beforecollapse" : true,
35154         /**
35155          * @event collapsed
35156          * Fires when this region is collapsed.
35157          * @param {Roo.LayoutRegion} this
35158          */
35159         "collapsed" : true,
35160         /**
35161          * @event expanded
35162          * Fires when this region is expanded.
35163          * @param {Roo.LayoutRegion} this
35164          */
35165         "expanded" : true,
35166         /**
35167          * @event slideshow
35168          * Fires when this region is slid into view.
35169          * @param {Roo.LayoutRegion} this
35170          */
35171         "slideshow" : true,
35172         /**
35173          * @event slidehide
35174          * Fires when this region slides out of view. 
35175          * @param {Roo.LayoutRegion} this
35176          */
35177         "slidehide" : true,
35178         /**
35179          * @event panelactivated
35180          * Fires when a panel is activated. 
35181          * @param {Roo.LayoutRegion} this
35182          * @param {Roo.ContentPanel} panel The activated panel
35183          */
35184         "panelactivated" : true,
35185         /**
35186          * @event resized
35187          * Fires when the user resizes this region. 
35188          * @param {Roo.LayoutRegion} this
35189          * @param {Number} newSize The new size (width for east/west, height for north/south)
35190          */
35191         "resized" : true
35192     };
35193     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35194     this.panels = new Roo.util.MixedCollection();
35195     this.panels.getKey = this.getPanelId.createDelegate(this);
35196     this.box = null;
35197     this.activePanel = null;
35198     // ensure listeners are added...
35199     
35200     if (config.listeners || config.events) {
35201         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35202             listeners : config.listeners || {},
35203             events : config.events || {}
35204         });
35205     }
35206     
35207     if(skipConfig !== true){
35208         this.applyConfig(config);
35209     }
35210 };
35211
35212 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35213 {
35214     getPanelId : function(p){
35215         return p.getId();
35216     },
35217     
35218     applyConfig : function(config){
35219         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35220         this.config = config;
35221         
35222     },
35223     
35224     /**
35225      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35226      * the width, for horizontal (north, south) the height.
35227      * @param {Number} newSize The new width or height
35228      */
35229     resizeTo : function(newSize){
35230         var el = this.el ? this.el :
35231                  (this.activePanel ? this.activePanel.getEl() : null);
35232         if(el){
35233             switch(this.position){
35234                 case "east":
35235                 case "west":
35236                     el.setWidth(newSize);
35237                     this.fireEvent("resized", this, newSize);
35238                 break;
35239                 case "north":
35240                 case "south":
35241                     el.setHeight(newSize);
35242                     this.fireEvent("resized", this, newSize);
35243                 break;                
35244             }
35245         }
35246     },
35247     
35248     getBox : function(){
35249         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35250     },
35251     
35252     getMargins : function(){
35253         return this.margins;
35254     },
35255     
35256     updateBox : function(box){
35257         this.box = box;
35258         var el = this.activePanel.getEl();
35259         el.dom.style.left = box.x + "px";
35260         el.dom.style.top = box.y + "px";
35261         this.activePanel.setSize(box.width, box.height);
35262     },
35263     
35264     /**
35265      * Returns the container element for this region.
35266      * @return {Roo.Element}
35267      */
35268     getEl : function(){
35269         return this.activePanel;
35270     },
35271     
35272     /**
35273      * Returns true if this region is currently visible.
35274      * @return {Boolean}
35275      */
35276     isVisible : function(){
35277         return this.activePanel ? true : false;
35278     },
35279     
35280     setActivePanel : function(panel){
35281         panel = this.getPanel(panel);
35282         if(this.activePanel && this.activePanel != panel){
35283             this.activePanel.setActiveState(false);
35284             this.activePanel.getEl().setLeftTop(-10000,-10000);
35285         }
35286         this.activePanel = panel;
35287         panel.setActiveState(true);
35288         if(this.box){
35289             panel.setSize(this.box.width, this.box.height);
35290         }
35291         this.fireEvent("panelactivated", this, panel);
35292         this.fireEvent("invalidated");
35293     },
35294     
35295     /**
35296      * Show the specified panel.
35297      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35298      * @return {Roo.ContentPanel} The shown panel or null
35299      */
35300     showPanel : function(panel){
35301         panel = this.getPanel(panel);
35302         if(panel){
35303             this.setActivePanel(panel);
35304         }
35305         return panel;
35306     },
35307     
35308     /**
35309      * Get the active panel for this region.
35310      * @return {Roo.ContentPanel} The active panel or null
35311      */
35312     getActivePanel : function(){
35313         return this.activePanel;
35314     },
35315     
35316     /**
35317      * Add the passed ContentPanel(s)
35318      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35319      * @return {Roo.ContentPanel} The panel added (if only one was added)
35320      */
35321     add : function(panel){
35322         if(arguments.length > 1){
35323             for(var i = 0, len = arguments.length; i < len; i++) {
35324                 this.add(arguments[i]);
35325             }
35326             return null;
35327         }
35328         if(this.hasPanel(panel)){
35329             this.showPanel(panel);
35330             return panel;
35331         }
35332         var el = panel.getEl();
35333         if(el.dom.parentNode != this.mgr.el.dom){
35334             this.mgr.el.dom.appendChild(el.dom);
35335         }
35336         if(panel.setRegion){
35337             panel.setRegion(this);
35338         }
35339         this.panels.add(panel);
35340         el.setStyle("position", "absolute");
35341         if(!panel.background){
35342             this.setActivePanel(panel);
35343             if(this.config.initialSize && this.panels.getCount()==1){
35344                 this.resizeTo(this.config.initialSize);
35345             }
35346         }
35347         this.fireEvent("paneladded", this, panel);
35348         return panel;
35349     },
35350     
35351     /**
35352      * Returns true if the panel is in this region.
35353      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35354      * @return {Boolean}
35355      */
35356     hasPanel : function(panel){
35357         if(typeof panel == "object"){ // must be panel obj
35358             panel = panel.getId();
35359         }
35360         return this.getPanel(panel) ? true : false;
35361     },
35362     
35363     /**
35364      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35365      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35366      * @param {Boolean} preservePanel Overrides the config preservePanel option
35367      * @return {Roo.ContentPanel} The panel that was removed
35368      */
35369     remove : function(panel, preservePanel){
35370         panel = this.getPanel(panel);
35371         if(!panel){
35372             return null;
35373         }
35374         var e = {};
35375         this.fireEvent("beforeremove", this, panel, e);
35376         if(e.cancel === true){
35377             return null;
35378         }
35379         var panelId = panel.getId();
35380         this.panels.removeKey(panelId);
35381         return panel;
35382     },
35383     
35384     /**
35385      * Returns the panel specified or null if it's not in this region.
35386      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35387      * @return {Roo.ContentPanel}
35388      */
35389     getPanel : function(id){
35390         if(typeof id == "object"){ // must be panel obj
35391             return id;
35392         }
35393         return this.panels.get(id);
35394     },
35395     
35396     /**
35397      * Returns this regions position (north/south/east/west/center).
35398      * @return {String} 
35399      */
35400     getPosition: function(){
35401         return this.position;    
35402     }
35403 });/*
35404  * Based on:
35405  * Ext JS Library 1.1.1
35406  * Copyright(c) 2006-2007, Ext JS, LLC.
35407  *
35408  * Originally Released Under LGPL - original licence link has changed is not relivant.
35409  *
35410  * Fork - LGPL
35411  * <script type="text/javascript">
35412  */
35413  
35414 /**
35415  * @class Roo.bootstrap.layout.Region
35416  * @extends Roo.bootstrap.layout.Basic
35417  * This class represents a region in a layout manager.
35418  
35419  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35420  * @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})
35421  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35422  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35423  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35424  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35425  * @cfg {String}    title           The title for the region (overrides panel titles)
35426  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35427  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35428  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35429  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35430  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35431  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35432  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35433  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35434  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35435  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35436
35437  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35438  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35439  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35440  * @cfg {Number}    width           For East/West panels
35441  * @cfg {Number}    height          For North/South panels
35442  * @cfg {Boolean}   split           To show the splitter
35443  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35444  * 
35445  * @cfg {string}   cls             Extra CSS classes to add to region
35446  * 
35447  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35448  * @cfg {string}   region  the region that it inhabits..
35449  *
35450
35451  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35452  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35453
35454  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35455  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35456  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35457  */
35458 Roo.bootstrap.layout.Region = function(config)
35459 {
35460     this.applyConfig(config);
35461
35462     var mgr = config.mgr;
35463     var pos = config.region;
35464     config.skipConfig = true;
35465     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35466     
35467     if (mgr.el) {
35468         this.onRender(mgr.el);   
35469     }
35470      
35471     this.visible = true;
35472     this.collapsed = false;
35473     this.unrendered_panels = [];
35474 };
35475
35476 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35477
35478     position: '', // set by wrapper (eg. north/south etc..)
35479     unrendered_panels : null,  // unrendered panels.
35480     createBody : function(){
35481         /** This region's body element 
35482         * @type Roo.Element */
35483         this.bodyEl = this.el.createChild({
35484                 tag: "div",
35485                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35486         });
35487     },
35488
35489     onRender: function(ctr, pos)
35490     {
35491         var dh = Roo.DomHelper;
35492         /** This region's container element 
35493         * @type Roo.Element */
35494         this.el = dh.append(ctr.dom, {
35495                 tag: "div",
35496                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35497             }, true);
35498         /** This region's title element 
35499         * @type Roo.Element */
35500     
35501         this.titleEl = dh.append(this.el.dom,
35502             {
35503                     tag: "div",
35504                     unselectable: "on",
35505                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35506                     children:[
35507                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35508                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35509                     ]}, true);
35510         
35511         this.titleEl.enableDisplayMode();
35512         /** This region's title text element 
35513         * @type HTMLElement */
35514         this.titleTextEl = this.titleEl.dom.firstChild;
35515         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35516         /*
35517         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35518         this.closeBtn.enableDisplayMode();
35519         this.closeBtn.on("click", this.closeClicked, this);
35520         this.closeBtn.hide();
35521     */
35522         this.createBody(this.config);
35523         if(this.config.hideWhenEmpty){
35524             this.hide();
35525             this.on("paneladded", this.validateVisibility, this);
35526             this.on("panelremoved", this.validateVisibility, this);
35527         }
35528         if(this.autoScroll){
35529             this.bodyEl.setStyle("overflow", "auto");
35530         }else{
35531             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35532         }
35533         //if(c.titlebar !== false){
35534             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35535                 this.titleEl.hide();
35536             }else{
35537                 this.titleEl.show();
35538                 if(this.config.title){
35539                     this.titleTextEl.innerHTML = this.config.title;
35540                 }
35541             }
35542         //}
35543         if(this.config.collapsed){
35544             this.collapse(true);
35545         }
35546         if(this.config.hidden){
35547             this.hide();
35548         }
35549         
35550         if (this.unrendered_panels && this.unrendered_panels.length) {
35551             for (var i =0;i< this.unrendered_panels.length; i++) {
35552                 this.add(this.unrendered_panels[i]);
35553             }
35554             this.unrendered_panels = null;
35555             
35556         }
35557         
35558     },
35559     
35560     applyConfig : function(c)
35561     {
35562         /*
35563          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35564             var dh = Roo.DomHelper;
35565             if(c.titlebar !== false){
35566                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35567                 this.collapseBtn.on("click", this.collapse, this);
35568                 this.collapseBtn.enableDisplayMode();
35569                 /*
35570                 if(c.showPin === true || this.showPin){
35571                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35572                     this.stickBtn.enableDisplayMode();
35573                     this.stickBtn.on("click", this.expand, this);
35574                     this.stickBtn.hide();
35575                 }
35576                 
35577             }
35578             */
35579             /** This region's collapsed element
35580             * @type Roo.Element */
35581             /*
35582              *
35583             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35584                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35585             ]}, true);
35586             
35587             if(c.floatable !== false){
35588                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35589                this.collapsedEl.on("click", this.collapseClick, this);
35590             }
35591
35592             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35593                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35594                    id: "message", unselectable: "on", style:{"float":"left"}});
35595                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35596              }
35597             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35598             this.expandBtn.on("click", this.expand, this);
35599             
35600         }
35601         
35602         if(this.collapseBtn){
35603             this.collapseBtn.setVisible(c.collapsible == true);
35604         }
35605         
35606         this.cmargins = c.cmargins || this.cmargins ||
35607                          (this.position == "west" || this.position == "east" ?
35608                              {top: 0, left: 2, right:2, bottom: 0} :
35609                              {top: 2, left: 0, right:0, bottom: 2});
35610         */
35611         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35612         
35613         
35614         this.bottomTabs = c.tabPosition != "top";
35615         
35616         this.autoScroll = c.autoScroll || false;
35617         
35618         
35619        
35620         
35621         this.duration = c.duration || .30;
35622         this.slideDuration = c.slideDuration || .45;
35623         this.config = c;
35624        
35625     },
35626     /**
35627      * Returns true if this region is currently visible.
35628      * @return {Boolean}
35629      */
35630     isVisible : function(){
35631         return this.visible;
35632     },
35633
35634     /**
35635      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35636      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35637      */
35638     //setCollapsedTitle : function(title){
35639     //    title = title || "&#160;";
35640      //   if(this.collapsedTitleTextEl){
35641       //      this.collapsedTitleTextEl.innerHTML = title;
35642        // }
35643     //},
35644
35645     getBox : function(){
35646         var b;
35647       //  if(!this.collapsed){
35648             b = this.el.getBox(false, true);
35649        // }else{
35650           //  b = this.collapsedEl.getBox(false, true);
35651         //}
35652         return b;
35653     },
35654
35655     getMargins : function(){
35656         return this.margins;
35657         //return this.collapsed ? this.cmargins : this.margins;
35658     },
35659 /*
35660     highlight : function(){
35661         this.el.addClass("x-layout-panel-dragover");
35662     },
35663
35664     unhighlight : function(){
35665         this.el.removeClass("x-layout-panel-dragover");
35666     },
35667 */
35668     updateBox : function(box)
35669     {
35670         if (!this.bodyEl) {
35671             return; // not rendered yet..
35672         }
35673         
35674         this.box = box;
35675         if(!this.collapsed){
35676             this.el.dom.style.left = box.x + "px";
35677             this.el.dom.style.top = box.y + "px";
35678             this.updateBody(box.width, box.height);
35679         }else{
35680             this.collapsedEl.dom.style.left = box.x + "px";
35681             this.collapsedEl.dom.style.top = box.y + "px";
35682             this.collapsedEl.setSize(box.width, box.height);
35683         }
35684         if(this.tabs){
35685             this.tabs.autoSizeTabs();
35686         }
35687     },
35688
35689     updateBody : function(w, h)
35690     {
35691         if(w !== null){
35692             this.el.setWidth(w);
35693             w -= this.el.getBorderWidth("rl");
35694             if(this.config.adjustments){
35695                 w += this.config.adjustments[0];
35696             }
35697         }
35698         if(h !== null && h > 0){
35699             this.el.setHeight(h);
35700             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35701             h -= this.el.getBorderWidth("tb");
35702             if(this.config.adjustments){
35703                 h += this.config.adjustments[1];
35704             }
35705             this.bodyEl.setHeight(h);
35706             if(this.tabs){
35707                 h = this.tabs.syncHeight(h);
35708             }
35709         }
35710         if(this.panelSize){
35711             w = w !== null ? w : this.panelSize.width;
35712             h = h !== null ? h : this.panelSize.height;
35713         }
35714         if(this.activePanel){
35715             var el = this.activePanel.getEl();
35716             w = w !== null ? w : el.getWidth();
35717             h = h !== null ? h : el.getHeight();
35718             this.panelSize = {width: w, height: h};
35719             this.activePanel.setSize(w, h);
35720         }
35721         if(Roo.isIE && this.tabs){
35722             this.tabs.el.repaint();
35723         }
35724     },
35725
35726     /**
35727      * Returns the container element for this region.
35728      * @return {Roo.Element}
35729      */
35730     getEl : function(){
35731         return this.el;
35732     },
35733
35734     /**
35735      * Hides this region.
35736      */
35737     hide : function(){
35738         //if(!this.collapsed){
35739             this.el.dom.style.left = "-2000px";
35740             this.el.hide();
35741         //}else{
35742          //   this.collapsedEl.dom.style.left = "-2000px";
35743          //   this.collapsedEl.hide();
35744        // }
35745         this.visible = false;
35746         this.fireEvent("visibilitychange", this, false);
35747     },
35748
35749     /**
35750      * Shows this region if it was previously hidden.
35751      */
35752     show : function(){
35753         //if(!this.collapsed){
35754             this.el.show();
35755         //}else{
35756         //    this.collapsedEl.show();
35757        // }
35758         this.visible = true;
35759         this.fireEvent("visibilitychange", this, true);
35760     },
35761 /*
35762     closeClicked : function(){
35763         if(this.activePanel){
35764             this.remove(this.activePanel);
35765         }
35766     },
35767
35768     collapseClick : function(e){
35769         if(this.isSlid){
35770            e.stopPropagation();
35771            this.slideIn();
35772         }else{
35773            e.stopPropagation();
35774            this.slideOut();
35775         }
35776     },
35777 */
35778     /**
35779      * Collapses this region.
35780      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35781      */
35782     /*
35783     collapse : function(skipAnim, skipCheck = false){
35784         if(this.collapsed) {
35785             return;
35786         }
35787         
35788         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35789             
35790             this.collapsed = true;
35791             if(this.split){
35792                 this.split.el.hide();
35793             }
35794             if(this.config.animate && skipAnim !== true){
35795                 this.fireEvent("invalidated", this);
35796                 this.animateCollapse();
35797             }else{
35798                 this.el.setLocation(-20000,-20000);
35799                 this.el.hide();
35800                 this.collapsedEl.show();
35801                 this.fireEvent("collapsed", this);
35802                 this.fireEvent("invalidated", this);
35803             }
35804         }
35805         
35806     },
35807 */
35808     animateCollapse : function(){
35809         // overridden
35810     },
35811
35812     /**
35813      * Expands this region if it was previously collapsed.
35814      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35815      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35816      */
35817     /*
35818     expand : function(e, skipAnim){
35819         if(e) {
35820             e.stopPropagation();
35821         }
35822         if(!this.collapsed || this.el.hasActiveFx()) {
35823             return;
35824         }
35825         if(this.isSlid){
35826             this.afterSlideIn();
35827             skipAnim = true;
35828         }
35829         this.collapsed = false;
35830         if(this.config.animate && skipAnim !== true){
35831             this.animateExpand();
35832         }else{
35833             this.el.show();
35834             if(this.split){
35835                 this.split.el.show();
35836             }
35837             this.collapsedEl.setLocation(-2000,-2000);
35838             this.collapsedEl.hide();
35839             this.fireEvent("invalidated", this);
35840             this.fireEvent("expanded", this);
35841         }
35842     },
35843 */
35844     animateExpand : function(){
35845         // overridden
35846     },
35847
35848     initTabs : function()
35849     {
35850         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35851         
35852         var ts = new Roo.bootstrap.panel.Tabs({
35853                 el: this.bodyEl.dom,
35854                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35855                 disableTooltips: this.config.disableTabTips,
35856                 toolbar : this.config.toolbar
35857             });
35858         
35859         if(this.config.hideTabs){
35860             ts.stripWrap.setDisplayed(false);
35861         }
35862         this.tabs = ts;
35863         ts.resizeTabs = this.config.resizeTabs === true;
35864         ts.minTabWidth = this.config.minTabWidth || 40;
35865         ts.maxTabWidth = this.config.maxTabWidth || 250;
35866         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35867         ts.monitorResize = false;
35868         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35869         ts.bodyEl.addClass('roo-layout-tabs-body');
35870         this.panels.each(this.initPanelAsTab, this);
35871     },
35872
35873     initPanelAsTab : function(panel){
35874         var ti = this.tabs.addTab(
35875             panel.getEl().id,
35876             panel.getTitle(),
35877             null,
35878             this.config.closeOnTab && panel.isClosable(),
35879             panel.tpl
35880         );
35881         if(panel.tabTip !== undefined){
35882             ti.setTooltip(panel.tabTip);
35883         }
35884         ti.on("activate", function(){
35885               this.setActivePanel(panel);
35886         }, this);
35887         
35888         if(this.config.closeOnTab){
35889             ti.on("beforeclose", function(t, e){
35890                 e.cancel = true;
35891                 this.remove(panel);
35892             }, this);
35893         }
35894         
35895         panel.tabItem = ti;
35896         
35897         return ti;
35898     },
35899
35900     updatePanelTitle : function(panel, title)
35901     {
35902         if(this.activePanel == panel){
35903             this.updateTitle(title);
35904         }
35905         if(this.tabs){
35906             var ti = this.tabs.getTab(panel.getEl().id);
35907             ti.setText(title);
35908             if(panel.tabTip !== undefined){
35909                 ti.setTooltip(panel.tabTip);
35910             }
35911         }
35912     },
35913
35914     updateTitle : function(title){
35915         if(this.titleTextEl && !this.config.title){
35916             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35917         }
35918     },
35919
35920     setActivePanel : function(panel)
35921     {
35922         panel = this.getPanel(panel);
35923         if(this.activePanel && this.activePanel != panel){
35924             if(this.activePanel.setActiveState(false) === false){
35925                 return;
35926             }
35927         }
35928         this.activePanel = panel;
35929         panel.setActiveState(true);
35930         if(this.panelSize){
35931             panel.setSize(this.panelSize.width, this.panelSize.height);
35932         }
35933         if(this.closeBtn){
35934             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35935         }
35936         this.updateTitle(panel.getTitle());
35937         if(this.tabs){
35938             this.fireEvent("invalidated", this);
35939         }
35940         this.fireEvent("panelactivated", this, panel);
35941     },
35942
35943     /**
35944      * Shows the specified panel.
35945      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35946      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35947      */
35948     showPanel : function(panel)
35949     {
35950         panel = this.getPanel(panel);
35951         if(panel){
35952             if(this.tabs){
35953                 var tab = this.tabs.getTab(panel.getEl().id);
35954                 if(tab.isHidden()){
35955                     this.tabs.unhideTab(tab.id);
35956                 }
35957                 tab.activate();
35958             }else{
35959                 this.setActivePanel(panel);
35960             }
35961         }
35962         return panel;
35963     },
35964
35965     /**
35966      * Get the active panel for this region.
35967      * @return {Roo.ContentPanel} The active panel or null
35968      */
35969     getActivePanel : function(){
35970         return this.activePanel;
35971     },
35972
35973     validateVisibility : function(){
35974         if(this.panels.getCount() < 1){
35975             this.updateTitle("&#160;");
35976             this.closeBtn.hide();
35977             this.hide();
35978         }else{
35979             if(!this.isVisible()){
35980                 this.show();
35981             }
35982         }
35983     },
35984
35985     /**
35986      * Adds the passed ContentPanel(s) to this region.
35987      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35988      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35989      */
35990     add : function(panel)
35991     {
35992         if(arguments.length > 1){
35993             for(var i = 0, len = arguments.length; i < len; i++) {
35994                 this.add(arguments[i]);
35995             }
35996             return null;
35997         }
35998         
35999         // if we have not been rendered yet, then we can not really do much of this..
36000         if (!this.bodyEl) {
36001             this.unrendered_panels.push(panel);
36002             return panel;
36003         }
36004         
36005         
36006         
36007         
36008         if(this.hasPanel(panel)){
36009             this.showPanel(panel);
36010             return panel;
36011         }
36012         panel.setRegion(this);
36013         this.panels.add(panel);
36014        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36015             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36016             // and hide them... ???
36017             this.bodyEl.dom.appendChild(panel.getEl().dom);
36018             if(panel.background !== true){
36019                 this.setActivePanel(panel);
36020             }
36021             this.fireEvent("paneladded", this, panel);
36022             return panel;
36023         }
36024         */
36025         if(!this.tabs){
36026             this.initTabs();
36027         }else{
36028             this.initPanelAsTab(panel);
36029         }
36030         
36031         
36032         if(panel.background !== true){
36033             this.tabs.activate(panel.getEl().id);
36034         }
36035         this.fireEvent("paneladded", this, panel);
36036         return panel;
36037     },
36038
36039     /**
36040      * Hides the tab for the specified panel.
36041      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36042      */
36043     hidePanel : function(panel){
36044         if(this.tabs && (panel = this.getPanel(panel))){
36045             this.tabs.hideTab(panel.getEl().id);
36046         }
36047     },
36048
36049     /**
36050      * Unhides the tab for a previously hidden panel.
36051      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36052      */
36053     unhidePanel : function(panel){
36054         if(this.tabs && (panel = this.getPanel(panel))){
36055             this.tabs.unhideTab(panel.getEl().id);
36056         }
36057     },
36058
36059     clearPanels : function(){
36060         while(this.panels.getCount() > 0){
36061              this.remove(this.panels.first());
36062         }
36063     },
36064
36065     /**
36066      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36067      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36068      * @param {Boolean} preservePanel Overrides the config preservePanel option
36069      * @return {Roo.ContentPanel} The panel that was removed
36070      */
36071     remove : function(panel, preservePanel)
36072     {
36073         panel = this.getPanel(panel);
36074         if(!panel){
36075             return null;
36076         }
36077         var e = {};
36078         this.fireEvent("beforeremove", this, panel, e);
36079         if(e.cancel === true){
36080             return null;
36081         }
36082         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36083         var panelId = panel.getId();
36084         this.panels.removeKey(panelId);
36085         if(preservePanel){
36086             document.body.appendChild(panel.getEl().dom);
36087         }
36088         if(this.tabs){
36089             this.tabs.removeTab(panel.getEl().id);
36090         }else if (!preservePanel){
36091             this.bodyEl.dom.removeChild(panel.getEl().dom);
36092         }
36093         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36094             var p = this.panels.first();
36095             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36096             tempEl.appendChild(p.getEl().dom);
36097             this.bodyEl.update("");
36098             this.bodyEl.dom.appendChild(p.getEl().dom);
36099             tempEl = null;
36100             this.updateTitle(p.getTitle());
36101             this.tabs = null;
36102             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36103             this.setActivePanel(p);
36104         }
36105         panel.setRegion(null);
36106         if(this.activePanel == panel){
36107             this.activePanel = null;
36108         }
36109         if(this.config.autoDestroy !== false && preservePanel !== true){
36110             try{panel.destroy();}catch(e){}
36111         }
36112         this.fireEvent("panelremoved", this, panel);
36113         return panel;
36114     },
36115
36116     /**
36117      * Returns the TabPanel component used by this region
36118      * @return {Roo.TabPanel}
36119      */
36120     getTabs : function(){
36121         return this.tabs;
36122     },
36123
36124     createTool : function(parentEl, className){
36125         var btn = Roo.DomHelper.append(parentEl, {
36126             tag: "div",
36127             cls: "x-layout-tools-button",
36128             children: [ {
36129                 tag: "div",
36130                 cls: "roo-layout-tools-button-inner " + className,
36131                 html: "&#160;"
36132             }]
36133         }, true);
36134         btn.addClassOnOver("roo-layout-tools-button-over");
36135         return btn;
36136     }
36137 });/*
36138  * Based on:
36139  * Ext JS Library 1.1.1
36140  * Copyright(c) 2006-2007, Ext JS, LLC.
36141  *
36142  * Originally Released Under LGPL - original licence link has changed is not relivant.
36143  *
36144  * Fork - LGPL
36145  * <script type="text/javascript">
36146  */
36147  
36148
36149
36150 /**
36151  * @class Roo.SplitLayoutRegion
36152  * @extends Roo.LayoutRegion
36153  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36154  */
36155 Roo.bootstrap.layout.Split = function(config){
36156     this.cursor = config.cursor;
36157     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36158 };
36159
36160 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36161 {
36162     splitTip : "Drag to resize.",
36163     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36164     useSplitTips : false,
36165
36166     applyConfig : function(config){
36167         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36168     },
36169     
36170     onRender : function(ctr,pos) {
36171         
36172         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36173         if(!this.config.split){
36174             return;
36175         }
36176         if(!this.split){
36177             
36178             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36179                             tag: "div",
36180                             id: this.el.id + "-split",
36181                             cls: "roo-layout-split roo-layout-split-"+this.position,
36182                             html: "&#160;"
36183             });
36184             /** The SplitBar for this region 
36185             * @type Roo.SplitBar */
36186             // does not exist yet...
36187             Roo.log([this.position, this.orientation]);
36188             
36189             this.split = new Roo.bootstrap.SplitBar({
36190                 dragElement : splitEl,
36191                 resizingElement: this.el,
36192                 orientation : this.orientation
36193             });
36194             
36195             this.split.on("moved", this.onSplitMove, this);
36196             this.split.useShim = this.config.useShim === true;
36197             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36198             if(this.useSplitTips){
36199                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36200             }
36201             //if(config.collapsible){
36202             //    this.split.el.on("dblclick", this.collapse,  this);
36203             //}
36204         }
36205         if(typeof this.config.minSize != "undefined"){
36206             this.split.minSize = this.config.minSize;
36207         }
36208         if(typeof this.config.maxSize != "undefined"){
36209             this.split.maxSize = this.config.maxSize;
36210         }
36211         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36212             this.hideSplitter();
36213         }
36214         
36215     },
36216
36217     getHMaxSize : function(){
36218          var cmax = this.config.maxSize || 10000;
36219          var center = this.mgr.getRegion("center");
36220          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36221     },
36222
36223     getVMaxSize : function(){
36224          var cmax = this.config.maxSize || 10000;
36225          var center = this.mgr.getRegion("center");
36226          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36227     },
36228
36229     onSplitMove : function(split, newSize){
36230         this.fireEvent("resized", this, newSize);
36231     },
36232     
36233     /** 
36234      * Returns the {@link Roo.SplitBar} for this region.
36235      * @return {Roo.SplitBar}
36236      */
36237     getSplitBar : function(){
36238         return this.split;
36239     },
36240     
36241     hide : function(){
36242         this.hideSplitter();
36243         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36244     },
36245
36246     hideSplitter : function(){
36247         if(this.split){
36248             this.split.el.setLocation(-2000,-2000);
36249             this.split.el.hide();
36250         }
36251     },
36252
36253     show : function(){
36254         if(this.split){
36255             this.split.el.show();
36256         }
36257         Roo.bootstrap.layout.Split.superclass.show.call(this);
36258     },
36259     
36260     beforeSlide: function(){
36261         if(Roo.isGecko){// firefox overflow auto bug workaround
36262             this.bodyEl.clip();
36263             if(this.tabs) {
36264                 this.tabs.bodyEl.clip();
36265             }
36266             if(this.activePanel){
36267                 this.activePanel.getEl().clip();
36268                 
36269                 if(this.activePanel.beforeSlide){
36270                     this.activePanel.beforeSlide();
36271                 }
36272             }
36273         }
36274     },
36275     
36276     afterSlide : function(){
36277         if(Roo.isGecko){// firefox overflow auto bug workaround
36278             this.bodyEl.unclip();
36279             if(this.tabs) {
36280                 this.tabs.bodyEl.unclip();
36281             }
36282             if(this.activePanel){
36283                 this.activePanel.getEl().unclip();
36284                 if(this.activePanel.afterSlide){
36285                     this.activePanel.afterSlide();
36286                 }
36287             }
36288         }
36289     },
36290
36291     initAutoHide : function(){
36292         if(this.autoHide !== false){
36293             if(!this.autoHideHd){
36294                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36295                 this.autoHideHd = {
36296                     "mouseout": function(e){
36297                         if(!e.within(this.el, true)){
36298                             st.delay(500);
36299                         }
36300                     },
36301                     "mouseover" : function(e){
36302                         st.cancel();
36303                     },
36304                     scope : this
36305                 };
36306             }
36307             this.el.on(this.autoHideHd);
36308         }
36309     },
36310
36311     clearAutoHide : function(){
36312         if(this.autoHide !== false){
36313             this.el.un("mouseout", this.autoHideHd.mouseout);
36314             this.el.un("mouseover", this.autoHideHd.mouseover);
36315         }
36316     },
36317
36318     clearMonitor : function(){
36319         Roo.get(document).un("click", this.slideInIf, this);
36320     },
36321
36322     // these names are backwards but not changed for compat
36323     slideOut : function(){
36324         if(this.isSlid || this.el.hasActiveFx()){
36325             return;
36326         }
36327         this.isSlid = true;
36328         if(this.collapseBtn){
36329             this.collapseBtn.hide();
36330         }
36331         this.closeBtnState = this.closeBtn.getStyle('display');
36332         this.closeBtn.hide();
36333         if(this.stickBtn){
36334             this.stickBtn.show();
36335         }
36336         this.el.show();
36337         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36338         this.beforeSlide();
36339         this.el.setStyle("z-index", 10001);
36340         this.el.slideIn(this.getSlideAnchor(), {
36341             callback: function(){
36342                 this.afterSlide();
36343                 this.initAutoHide();
36344                 Roo.get(document).on("click", this.slideInIf, this);
36345                 this.fireEvent("slideshow", this);
36346             },
36347             scope: this,
36348             block: true
36349         });
36350     },
36351
36352     afterSlideIn : function(){
36353         this.clearAutoHide();
36354         this.isSlid = false;
36355         this.clearMonitor();
36356         this.el.setStyle("z-index", "");
36357         if(this.collapseBtn){
36358             this.collapseBtn.show();
36359         }
36360         this.closeBtn.setStyle('display', this.closeBtnState);
36361         if(this.stickBtn){
36362             this.stickBtn.hide();
36363         }
36364         this.fireEvent("slidehide", this);
36365     },
36366
36367     slideIn : function(cb){
36368         if(!this.isSlid || this.el.hasActiveFx()){
36369             Roo.callback(cb);
36370             return;
36371         }
36372         this.isSlid = false;
36373         this.beforeSlide();
36374         this.el.slideOut(this.getSlideAnchor(), {
36375             callback: function(){
36376                 this.el.setLeftTop(-10000, -10000);
36377                 this.afterSlide();
36378                 this.afterSlideIn();
36379                 Roo.callback(cb);
36380             },
36381             scope: this,
36382             block: true
36383         });
36384     },
36385     
36386     slideInIf : function(e){
36387         if(!e.within(this.el)){
36388             this.slideIn();
36389         }
36390     },
36391
36392     animateCollapse : function(){
36393         this.beforeSlide();
36394         this.el.setStyle("z-index", 20000);
36395         var anchor = this.getSlideAnchor();
36396         this.el.slideOut(anchor, {
36397             callback : function(){
36398                 this.el.setStyle("z-index", "");
36399                 this.collapsedEl.slideIn(anchor, {duration:.3});
36400                 this.afterSlide();
36401                 this.el.setLocation(-10000,-10000);
36402                 this.el.hide();
36403                 this.fireEvent("collapsed", this);
36404             },
36405             scope: this,
36406             block: true
36407         });
36408     },
36409
36410     animateExpand : function(){
36411         this.beforeSlide();
36412         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36413         this.el.setStyle("z-index", 20000);
36414         this.collapsedEl.hide({
36415             duration:.1
36416         });
36417         this.el.slideIn(this.getSlideAnchor(), {
36418             callback : function(){
36419                 this.el.setStyle("z-index", "");
36420                 this.afterSlide();
36421                 if(this.split){
36422                     this.split.el.show();
36423                 }
36424                 this.fireEvent("invalidated", this);
36425                 this.fireEvent("expanded", this);
36426             },
36427             scope: this,
36428             block: true
36429         });
36430     },
36431
36432     anchors : {
36433         "west" : "left",
36434         "east" : "right",
36435         "north" : "top",
36436         "south" : "bottom"
36437     },
36438
36439     sanchors : {
36440         "west" : "l",
36441         "east" : "r",
36442         "north" : "t",
36443         "south" : "b"
36444     },
36445
36446     canchors : {
36447         "west" : "tl-tr",
36448         "east" : "tr-tl",
36449         "north" : "tl-bl",
36450         "south" : "bl-tl"
36451     },
36452
36453     getAnchor : function(){
36454         return this.anchors[this.position];
36455     },
36456
36457     getCollapseAnchor : function(){
36458         return this.canchors[this.position];
36459     },
36460
36461     getSlideAnchor : function(){
36462         return this.sanchors[this.position];
36463     },
36464
36465     getAlignAdj : function(){
36466         var cm = this.cmargins;
36467         switch(this.position){
36468             case "west":
36469                 return [0, 0];
36470             break;
36471             case "east":
36472                 return [0, 0];
36473             break;
36474             case "north":
36475                 return [0, 0];
36476             break;
36477             case "south":
36478                 return [0, 0];
36479             break;
36480         }
36481     },
36482
36483     getExpandAdj : function(){
36484         var c = this.collapsedEl, cm = this.cmargins;
36485         switch(this.position){
36486             case "west":
36487                 return [-(cm.right+c.getWidth()+cm.left), 0];
36488             break;
36489             case "east":
36490                 return [cm.right+c.getWidth()+cm.left, 0];
36491             break;
36492             case "north":
36493                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36494             break;
36495             case "south":
36496                 return [0, cm.top+cm.bottom+c.getHeight()];
36497             break;
36498         }
36499     }
36500 });/*
36501  * Based on:
36502  * Ext JS Library 1.1.1
36503  * Copyright(c) 2006-2007, Ext JS, LLC.
36504  *
36505  * Originally Released Under LGPL - original licence link has changed is not relivant.
36506  *
36507  * Fork - LGPL
36508  * <script type="text/javascript">
36509  */
36510 /*
36511  * These classes are private internal classes
36512  */
36513 Roo.bootstrap.layout.Center = function(config){
36514     config.region = "center";
36515     Roo.bootstrap.layout.Region.call(this, config);
36516     this.visible = true;
36517     this.minWidth = config.minWidth || 20;
36518     this.minHeight = config.minHeight || 20;
36519 };
36520
36521 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36522     hide : function(){
36523         // center panel can't be hidden
36524     },
36525     
36526     show : function(){
36527         // center panel can't be hidden
36528     },
36529     
36530     getMinWidth: function(){
36531         return this.minWidth;
36532     },
36533     
36534     getMinHeight: function(){
36535         return this.minHeight;
36536     }
36537 });
36538
36539
36540
36541
36542  
36543
36544
36545
36546
36547
36548 Roo.bootstrap.layout.North = function(config)
36549 {
36550     config.region = 'north';
36551     config.cursor = 'n-resize';
36552     
36553     Roo.bootstrap.layout.Split.call(this, config);
36554     
36555     
36556     if(this.split){
36557         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36558         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36559         this.split.el.addClass("roo-layout-split-v");
36560     }
36561     var size = config.initialSize || config.height;
36562     if(typeof size != "undefined"){
36563         this.el.setHeight(size);
36564     }
36565 };
36566 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36567 {
36568     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36569     
36570     
36571     
36572     getBox : function(){
36573         if(this.collapsed){
36574             return this.collapsedEl.getBox();
36575         }
36576         var box = this.el.getBox();
36577         if(this.split){
36578             box.height += this.split.el.getHeight();
36579         }
36580         return box;
36581     },
36582     
36583     updateBox : function(box){
36584         if(this.split && !this.collapsed){
36585             box.height -= this.split.el.getHeight();
36586             this.split.el.setLeft(box.x);
36587             this.split.el.setTop(box.y+box.height);
36588             this.split.el.setWidth(box.width);
36589         }
36590         if(this.collapsed){
36591             this.updateBody(box.width, null);
36592         }
36593         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36594     }
36595 });
36596
36597
36598
36599
36600
36601 Roo.bootstrap.layout.South = function(config){
36602     config.region = 'south';
36603     config.cursor = 's-resize';
36604     Roo.bootstrap.layout.Split.call(this, config);
36605     if(this.split){
36606         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36607         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36608         this.split.el.addClass("roo-layout-split-v");
36609     }
36610     var size = config.initialSize || config.height;
36611     if(typeof size != "undefined"){
36612         this.el.setHeight(size);
36613     }
36614 };
36615
36616 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36617     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36618     getBox : function(){
36619         if(this.collapsed){
36620             return this.collapsedEl.getBox();
36621         }
36622         var box = this.el.getBox();
36623         if(this.split){
36624             var sh = this.split.el.getHeight();
36625             box.height += sh;
36626             box.y -= sh;
36627         }
36628         return box;
36629     },
36630     
36631     updateBox : function(box){
36632         if(this.split && !this.collapsed){
36633             var sh = this.split.el.getHeight();
36634             box.height -= sh;
36635             box.y += sh;
36636             this.split.el.setLeft(box.x);
36637             this.split.el.setTop(box.y-sh);
36638             this.split.el.setWidth(box.width);
36639         }
36640         if(this.collapsed){
36641             this.updateBody(box.width, null);
36642         }
36643         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36644     }
36645 });
36646
36647 Roo.bootstrap.layout.East = function(config){
36648     config.region = "east";
36649     config.cursor = "e-resize";
36650     Roo.bootstrap.layout.Split.call(this, config);
36651     if(this.split){
36652         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36653         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36654         this.split.el.addClass("roo-layout-split-h");
36655     }
36656     var size = config.initialSize || config.width;
36657     if(typeof size != "undefined"){
36658         this.el.setWidth(size);
36659     }
36660 };
36661 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36662     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36663     getBox : function(){
36664         if(this.collapsed){
36665             return this.collapsedEl.getBox();
36666         }
36667         var box = this.el.getBox();
36668         if(this.split){
36669             var sw = this.split.el.getWidth();
36670             box.width += sw;
36671             box.x -= sw;
36672         }
36673         return box;
36674     },
36675
36676     updateBox : function(box){
36677         if(this.split && !this.collapsed){
36678             var sw = this.split.el.getWidth();
36679             box.width -= sw;
36680             this.split.el.setLeft(box.x);
36681             this.split.el.setTop(box.y);
36682             this.split.el.setHeight(box.height);
36683             box.x += sw;
36684         }
36685         if(this.collapsed){
36686             this.updateBody(null, box.height);
36687         }
36688         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36689     }
36690 });
36691
36692 Roo.bootstrap.layout.West = function(config){
36693     config.region = "west";
36694     config.cursor = "w-resize";
36695     
36696     Roo.bootstrap.layout.Split.call(this, config);
36697     if(this.split){
36698         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36699         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36700         this.split.el.addClass("roo-layout-split-h");
36701     }
36702     
36703 };
36704 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36705     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36706     
36707     onRender: function(ctr, pos)
36708     {
36709         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36710         var size = this.config.initialSize || this.config.width;
36711         if(typeof size != "undefined"){
36712             this.el.setWidth(size);
36713         }
36714     },
36715     
36716     getBox : function(){
36717         if(this.collapsed){
36718             return this.collapsedEl.getBox();
36719         }
36720         var box = this.el.getBox();
36721         if(this.split){
36722             box.width += this.split.el.getWidth();
36723         }
36724         return box;
36725     },
36726     
36727     updateBox : function(box){
36728         if(this.split && !this.collapsed){
36729             var sw = this.split.el.getWidth();
36730             box.width -= sw;
36731             this.split.el.setLeft(box.x+box.width);
36732             this.split.el.setTop(box.y);
36733             this.split.el.setHeight(box.height);
36734         }
36735         if(this.collapsed){
36736             this.updateBody(null, box.height);
36737         }
36738         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36739     }
36740 });
36741 Roo.namespace("Roo.bootstrap.panel");/*
36742  * Based on:
36743  * Ext JS Library 1.1.1
36744  * Copyright(c) 2006-2007, Ext JS, LLC.
36745  *
36746  * Originally Released Under LGPL - original licence link has changed is not relivant.
36747  *
36748  * Fork - LGPL
36749  * <script type="text/javascript">
36750  */
36751 /**
36752  * @class Roo.ContentPanel
36753  * @extends Roo.util.Observable
36754  * A basic ContentPanel element.
36755  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36756  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36757  * @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
36758  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36759  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36760  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36761  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36762  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36763  * @cfg {String} title          The title for this panel
36764  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36765  * @cfg {String} url            Calls {@link #setUrl} with this value
36766  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36767  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36768  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36769  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36770  * @cfg {Boolean} badges render the badges
36771
36772  * @constructor
36773  * Create a new ContentPanel.
36774  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36775  * @param {String/Object} config A string to set only the title or a config object
36776  * @param {String} content (optional) Set the HTML content for this panel
36777  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36778  */
36779 Roo.bootstrap.panel.Content = function( config){
36780     
36781     this.tpl = config.tpl || false;
36782     
36783     var el = config.el;
36784     var content = config.content;
36785
36786     if(config.autoCreate){ // xtype is available if this is called from factory
36787         el = Roo.id();
36788     }
36789     this.el = Roo.get(el);
36790     if(!this.el && config && config.autoCreate){
36791         if(typeof config.autoCreate == "object"){
36792             if(!config.autoCreate.id){
36793                 config.autoCreate.id = config.id||el;
36794             }
36795             this.el = Roo.DomHelper.append(document.body,
36796                         config.autoCreate, true);
36797         }else{
36798             var elcfg =  {   tag: "div",
36799                             cls: "roo-layout-inactive-content",
36800                             id: config.id||el
36801                             };
36802             if (config.html) {
36803                 elcfg.html = config.html;
36804                 
36805             }
36806                         
36807             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36808         }
36809     } 
36810     this.closable = false;
36811     this.loaded = false;
36812     this.active = false;
36813    
36814       
36815     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36816         
36817         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36818         
36819         this.wrapEl = this.el; //this.el.wrap();
36820         var ti = [];
36821         if (config.toolbar.items) {
36822             ti = config.toolbar.items ;
36823             delete config.toolbar.items ;
36824         }
36825         
36826         var nitems = [];
36827         this.toolbar.render(this.wrapEl, 'before');
36828         for(var i =0;i < ti.length;i++) {
36829           //  Roo.log(['add child', items[i]]);
36830             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36831         }
36832         this.toolbar.items = nitems;
36833         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36834         delete config.toolbar;
36835         
36836     }
36837     /*
36838     // xtype created footer. - not sure if will work as we normally have to render first..
36839     if (this.footer && !this.footer.el && this.footer.xtype) {
36840         if (!this.wrapEl) {
36841             this.wrapEl = this.el.wrap();
36842         }
36843     
36844         this.footer.container = this.wrapEl.createChild();
36845          
36846         this.footer = Roo.factory(this.footer, Roo);
36847         
36848     }
36849     */
36850     
36851      if(typeof config == "string"){
36852         this.title = config;
36853     }else{
36854         Roo.apply(this, config);
36855     }
36856     
36857     if(this.resizeEl){
36858         this.resizeEl = Roo.get(this.resizeEl, true);
36859     }else{
36860         this.resizeEl = this.el;
36861     }
36862     // handle view.xtype
36863     
36864  
36865     
36866     
36867     this.addEvents({
36868         /**
36869          * @event activate
36870          * Fires when this panel is activated. 
36871          * @param {Roo.ContentPanel} this
36872          */
36873         "activate" : true,
36874         /**
36875          * @event deactivate
36876          * Fires when this panel is activated. 
36877          * @param {Roo.ContentPanel} this
36878          */
36879         "deactivate" : true,
36880
36881         /**
36882          * @event resize
36883          * Fires when this panel is resized if fitToFrame is true.
36884          * @param {Roo.ContentPanel} this
36885          * @param {Number} width The width after any component adjustments
36886          * @param {Number} height The height after any component adjustments
36887          */
36888         "resize" : true,
36889         
36890          /**
36891          * @event render
36892          * Fires when this tab is created
36893          * @param {Roo.ContentPanel} this
36894          */
36895         "render" : true
36896         
36897         
36898         
36899     });
36900     
36901
36902     
36903     
36904     if(this.autoScroll){
36905         this.resizeEl.setStyle("overflow", "auto");
36906     } else {
36907         // fix randome scrolling
36908         //this.el.on('scroll', function() {
36909         //    Roo.log('fix random scolling');
36910         //    this.scrollTo('top',0); 
36911         //});
36912     }
36913     content = content || this.content;
36914     if(content){
36915         this.setContent(content);
36916     }
36917     if(config && config.url){
36918         this.setUrl(this.url, this.params, this.loadOnce);
36919     }
36920     
36921     
36922     
36923     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36924     
36925     if (this.view && typeof(this.view.xtype) != 'undefined') {
36926         this.view.el = this.el.appendChild(document.createElement("div"));
36927         this.view = Roo.factory(this.view); 
36928         this.view.render  &&  this.view.render(false, '');  
36929     }
36930     
36931     
36932     this.fireEvent('render', this);
36933 };
36934
36935 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36936     
36937     tabTip : '',
36938     
36939     setRegion : function(region){
36940         this.region = region;
36941         this.setActiveClass(region && !this.background);
36942     },
36943     
36944     
36945     setActiveClass: function(state)
36946     {
36947         if(state){
36948            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36949            this.el.setStyle('position','relative');
36950         }else{
36951            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36952            this.el.setStyle('position', 'absolute');
36953         } 
36954     },
36955     
36956     /**
36957      * Returns the toolbar for this Panel if one was configured. 
36958      * @return {Roo.Toolbar} 
36959      */
36960     getToolbar : function(){
36961         return this.toolbar;
36962     },
36963     
36964     setActiveState : function(active)
36965     {
36966         this.active = active;
36967         this.setActiveClass(active);
36968         if(!active){
36969             if(this.fireEvent("deactivate", this) === false){
36970                 return false;
36971             }
36972             return true;
36973         }
36974         this.fireEvent("activate", this);
36975         return true;
36976     },
36977     /**
36978      * Updates this panel's element
36979      * @param {String} content The new content
36980      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36981     */
36982     setContent : function(content, loadScripts){
36983         this.el.update(content, loadScripts);
36984     },
36985
36986     ignoreResize : function(w, h){
36987         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36988             return true;
36989         }else{
36990             this.lastSize = {width: w, height: h};
36991             return false;
36992         }
36993     },
36994     /**
36995      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36996      * @return {Roo.UpdateManager} The UpdateManager
36997      */
36998     getUpdateManager : function(){
36999         return this.el.getUpdateManager();
37000     },
37001      /**
37002      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37003      * @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:
37004 <pre><code>
37005 panel.load({
37006     url: "your-url.php",
37007     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37008     callback: yourFunction,
37009     scope: yourObject, //(optional scope)
37010     discardUrl: false,
37011     nocache: false,
37012     text: "Loading...",
37013     timeout: 30,
37014     scripts: false
37015 });
37016 </code></pre>
37017      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37018      * 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.
37019      * @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}
37020      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37021      * @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.
37022      * @return {Roo.ContentPanel} this
37023      */
37024     load : function(){
37025         var um = this.el.getUpdateManager();
37026         um.update.apply(um, arguments);
37027         return this;
37028     },
37029
37030
37031     /**
37032      * 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.
37033      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37034      * @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)
37035      * @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)
37036      * @return {Roo.UpdateManager} The UpdateManager
37037      */
37038     setUrl : function(url, params, loadOnce){
37039         if(this.refreshDelegate){
37040             this.removeListener("activate", this.refreshDelegate);
37041         }
37042         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37043         this.on("activate", this.refreshDelegate);
37044         return this.el.getUpdateManager();
37045     },
37046     
37047     _handleRefresh : function(url, params, loadOnce){
37048         if(!loadOnce || !this.loaded){
37049             var updater = this.el.getUpdateManager();
37050             updater.update(url, params, this._setLoaded.createDelegate(this));
37051         }
37052     },
37053     
37054     _setLoaded : function(){
37055         this.loaded = true;
37056     }, 
37057     
37058     /**
37059      * Returns this panel's id
37060      * @return {String} 
37061      */
37062     getId : function(){
37063         return this.el.id;
37064     },
37065     
37066     /** 
37067      * Returns this panel's element - used by regiosn to add.
37068      * @return {Roo.Element} 
37069      */
37070     getEl : function(){
37071         return this.wrapEl || this.el;
37072     },
37073     
37074    
37075     
37076     adjustForComponents : function(width, height)
37077     {
37078         //Roo.log('adjustForComponents ');
37079         if(this.resizeEl != this.el){
37080             width -= this.el.getFrameWidth('lr');
37081             height -= this.el.getFrameWidth('tb');
37082         }
37083         if(this.toolbar){
37084             var te = this.toolbar.getEl();
37085             te.setWidth(width);
37086             height -= te.getHeight();
37087         }
37088         if(this.footer){
37089             var te = this.footer.getEl();
37090             te.setWidth(width);
37091             height -= te.getHeight();
37092         }
37093         
37094         
37095         if(this.adjustments){
37096             width += this.adjustments[0];
37097             height += this.adjustments[1];
37098         }
37099         return {"width": width, "height": height};
37100     },
37101     
37102     setSize : function(width, height){
37103         if(this.fitToFrame && !this.ignoreResize(width, height)){
37104             if(this.fitContainer && this.resizeEl != this.el){
37105                 this.el.setSize(width, height);
37106             }
37107             var size = this.adjustForComponents(width, height);
37108             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37109             this.fireEvent('resize', this, size.width, size.height);
37110         }
37111     },
37112     
37113     /**
37114      * Returns this panel's title
37115      * @return {String} 
37116      */
37117     getTitle : function(){
37118         
37119         if (typeof(this.title) != 'object') {
37120             return this.title;
37121         }
37122         
37123         var t = '';
37124         for (var k in this.title) {
37125             if (!this.title.hasOwnProperty(k)) {
37126                 continue;
37127             }
37128             
37129             if (k.indexOf('-') >= 0) {
37130                 var s = k.split('-');
37131                 for (var i = 0; i<s.length; i++) {
37132                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37133                 }
37134             } else {
37135                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37136             }
37137         }
37138         return t;
37139     },
37140     
37141     /**
37142      * Set this panel's title
37143      * @param {String} title
37144      */
37145     setTitle : function(title){
37146         this.title = title;
37147         if(this.region){
37148             this.region.updatePanelTitle(this, title);
37149         }
37150     },
37151     
37152     /**
37153      * Returns true is this panel was configured to be closable
37154      * @return {Boolean} 
37155      */
37156     isClosable : function(){
37157         return this.closable;
37158     },
37159     
37160     beforeSlide : function(){
37161         this.el.clip();
37162         this.resizeEl.clip();
37163     },
37164     
37165     afterSlide : function(){
37166         this.el.unclip();
37167         this.resizeEl.unclip();
37168     },
37169     
37170     /**
37171      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37172      *   Will fail silently if the {@link #setUrl} method has not been called.
37173      *   This does not activate the panel, just updates its content.
37174      */
37175     refresh : function(){
37176         if(this.refreshDelegate){
37177            this.loaded = false;
37178            this.refreshDelegate();
37179         }
37180     },
37181     
37182     /**
37183      * Destroys this panel
37184      */
37185     destroy : function(){
37186         this.el.removeAllListeners();
37187         var tempEl = document.createElement("span");
37188         tempEl.appendChild(this.el.dom);
37189         tempEl.innerHTML = "";
37190         this.el.remove();
37191         this.el = null;
37192     },
37193     
37194     /**
37195      * form - if the content panel contains a form - this is a reference to it.
37196      * @type {Roo.form.Form}
37197      */
37198     form : false,
37199     /**
37200      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37201      *    This contains a reference to it.
37202      * @type {Roo.View}
37203      */
37204     view : false,
37205     
37206       /**
37207      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37208      * <pre><code>
37209
37210 layout.addxtype({
37211        xtype : 'Form',
37212        items: [ .... ]
37213    }
37214 );
37215
37216 </code></pre>
37217      * @param {Object} cfg Xtype definition of item to add.
37218      */
37219     
37220     
37221     getChildContainer: function () {
37222         return this.getEl();
37223     }
37224     
37225     
37226     /*
37227         var  ret = new Roo.factory(cfg);
37228         return ret;
37229         
37230         
37231         // add form..
37232         if (cfg.xtype.match(/^Form$/)) {
37233             
37234             var el;
37235             //if (this.footer) {
37236             //    el = this.footer.container.insertSibling(false, 'before');
37237             //} else {
37238                 el = this.el.createChild();
37239             //}
37240
37241             this.form = new  Roo.form.Form(cfg);
37242             
37243             
37244             if ( this.form.allItems.length) {
37245                 this.form.render(el.dom);
37246             }
37247             return this.form;
37248         }
37249         // should only have one of theses..
37250         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37251             // views.. should not be just added - used named prop 'view''
37252             
37253             cfg.el = this.el.appendChild(document.createElement("div"));
37254             // factory?
37255             
37256             var ret = new Roo.factory(cfg);
37257              
37258              ret.render && ret.render(false, ''); // render blank..
37259             this.view = ret;
37260             return ret;
37261         }
37262         return false;
37263     }
37264     \*/
37265 });
37266  
37267 /**
37268  * @class Roo.bootstrap.panel.Grid
37269  * @extends Roo.bootstrap.panel.Content
37270  * @constructor
37271  * Create a new GridPanel.
37272  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37273  * @param {Object} config A the config object
37274   
37275  */
37276
37277
37278
37279 Roo.bootstrap.panel.Grid = function(config)
37280 {
37281     
37282       
37283     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37284         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37285
37286     config.el = this.wrapper;
37287     //this.el = this.wrapper;
37288     
37289       if (config.container) {
37290         // ctor'ed from a Border/panel.grid
37291         
37292         
37293         this.wrapper.setStyle("overflow", "hidden");
37294         this.wrapper.addClass('roo-grid-container');
37295
37296     }
37297     
37298     
37299     if(config.toolbar){
37300         var tool_el = this.wrapper.createChild();    
37301         this.toolbar = Roo.factory(config.toolbar);
37302         var ti = [];
37303         if (config.toolbar.items) {
37304             ti = config.toolbar.items ;
37305             delete config.toolbar.items ;
37306         }
37307         
37308         var nitems = [];
37309         this.toolbar.render(tool_el);
37310         for(var i =0;i < ti.length;i++) {
37311           //  Roo.log(['add child', items[i]]);
37312             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37313         }
37314         this.toolbar.items = nitems;
37315         
37316         delete config.toolbar;
37317     }
37318     
37319     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37320     config.grid.scrollBody = true;;
37321     config.grid.monitorWindowResize = false; // turn off autosizing
37322     config.grid.autoHeight = false;
37323     config.grid.autoWidth = false;
37324     
37325     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37326     
37327     if (config.background) {
37328         // render grid on panel activation (if panel background)
37329         this.on('activate', function(gp) {
37330             if (!gp.grid.rendered) {
37331                 gp.grid.render(this.wrapper);
37332                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37333             }
37334         });
37335             
37336     } else {
37337         this.grid.render(this.wrapper);
37338         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37339
37340     }
37341     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37342     // ??? needed ??? config.el = this.wrapper;
37343     
37344     
37345     
37346   
37347     // xtype created footer. - not sure if will work as we normally have to render first..
37348     if (this.footer && !this.footer.el && this.footer.xtype) {
37349         
37350         var ctr = this.grid.getView().getFooterPanel(true);
37351         this.footer.dataSource = this.grid.dataSource;
37352         this.footer = Roo.factory(this.footer, Roo);
37353         this.footer.render(ctr);
37354         
37355     }
37356     
37357     
37358     
37359     
37360      
37361 };
37362
37363 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37364     getId : function(){
37365         return this.grid.id;
37366     },
37367     
37368     /**
37369      * Returns the grid for this panel
37370      * @return {Roo.bootstrap.Table} 
37371      */
37372     getGrid : function(){
37373         return this.grid;    
37374     },
37375     
37376     setSize : function(width, height){
37377         if(!this.ignoreResize(width, height)){
37378             var grid = this.grid;
37379             var size = this.adjustForComponents(width, height);
37380             var gridel = grid.getGridEl();
37381             gridel.setSize(size.width, size.height);
37382             /*
37383             var thd = grid.getGridEl().select('thead',true).first();
37384             var tbd = grid.getGridEl().select('tbody', true).first();
37385             if (tbd) {
37386                 tbd.setSize(width, height - thd.getHeight());
37387             }
37388             */
37389             grid.autoSize();
37390         }
37391     },
37392      
37393     
37394     
37395     beforeSlide : function(){
37396         this.grid.getView().scroller.clip();
37397     },
37398     
37399     afterSlide : function(){
37400         this.grid.getView().scroller.unclip();
37401     },
37402     
37403     destroy : function(){
37404         this.grid.destroy();
37405         delete this.grid;
37406         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37407     }
37408 });
37409
37410 /**
37411  * @class Roo.bootstrap.panel.Nest
37412  * @extends Roo.bootstrap.panel.Content
37413  * @constructor
37414  * Create a new Panel, that can contain a layout.Border.
37415  * 
37416  * 
37417  * @param {Roo.BorderLayout} layout The layout for this panel
37418  * @param {String/Object} config A string to set only the title or a config object
37419  */
37420 Roo.bootstrap.panel.Nest = function(config)
37421 {
37422     // construct with only one argument..
37423     /* FIXME - implement nicer consturctors
37424     if (layout.layout) {
37425         config = layout;
37426         layout = config.layout;
37427         delete config.layout;
37428     }
37429     if (layout.xtype && !layout.getEl) {
37430         // then layout needs constructing..
37431         layout = Roo.factory(layout, Roo);
37432     }
37433     */
37434     
37435     config.el =  config.layout.getEl();
37436     
37437     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37438     
37439     config.layout.monitorWindowResize = false; // turn off autosizing
37440     this.layout = config.layout;
37441     this.layout.getEl().addClass("roo-layout-nested-layout");
37442     
37443     
37444     
37445     
37446 };
37447
37448 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37449
37450     setSize : function(width, height){
37451         if(!this.ignoreResize(width, height)){
37452             var size = this.adjustForComponents(width, height);
37453             var el = this.layout.getEl();
37454             if (size.height < 1) {
37455                 el.setWidth(size.width);   
37456             } else {
37457                 el.setSize(size.width, size.height);
37458             }
37459             var touch = el.dom.offsetWidth;
37460             this.layout.layout();
37461             // ie requires a double layout on the first pass
37462             if(Roo.isIE && !this.initialized){
37463                 this.initialized = true;
37464                 this.layout.layout();
37465             }
37466         }
37467     },
37468     
37469     // activate all subpanels if not currently active..
37470     
37471     setActiveState : function(active){
37472         this.active = active;
37473         this.setActiveClass(active);
37474         
37475         if(!active){
37476             this.fireEvent("deactivate", this);
37477             return;
37478         }
37479         
37480         this.fireEvent("activate", this);
37481         // not sure if this should happen before or after..
37482         if (!this.layout) {
37483             return; // should not happen..
37484         }
37485         var reg = false;
37486         for (var r in this.layout.regions) {
37487             reg = this.layout.getRegion(r);
37488             if (reg.getActivePanel()) {
37489                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37490                 reg.setActivePanel(reg.getActivePanel());
37491                 continue;
37492             }
37493             if (!reg.panels.length) {
37494                 continue;
37495             }
37496             reg.showPanel(reg.getPanel(0));
37497         }
37498         
37499         
37500         
37501         
37502     },
37503     
37504     /**
37505      * Returns the nested BorderLayout for this panel
37506      * @return {Roo.BorderLayout} 
37507      */
37508     getLayout : function(){
37509         return this.layout;
37510     },
37511     
37512      /**
37513      * Adds a xtype elements to the layout of the nested panel
37514      * <pre><code>
37515
37516 panel.addxtype({
37517        xtype : 'ContentPanel',
37518        region: 'west',
37519        items: [ .... ]
37520    }
37521 );
37522
37523 panel.addxtype({
37524         xtype : 'NestedLayoutPanel',
37525         region: 'west',
37526         layout: {
37527            center: { },
37528            west: { }   
37529         },
37530         items : [ ... list of content panels or nested layout panels.. ]
37531    }
37532 );
37533 </code></pre>
37534      * @param {Object} cfg Xtype definition of item to add.
37535      */
37536     addxtype : function(cfg) {
37537         return this.layout.addxtype(cfg);
37538     
37539     }
37540 });        /*
37541  * Based on:
37542  * Ext JS Library 1.1.1
37543  * Copyright(c) 2006-2007, Ext JS, LLC.
37544  *
37545  * Originally Released Under LGPL - original licence link has changed is not relivant.
37546  *
37547  * Fork - LGPL
37548  * <script type="text/javascript">
37549  */
37550 /**
37551  * @class Roo.TabPanel
37552  * @extends Roo.util.Observable
37553  * A lightweight tab container.
37554  * <br><br>
37555  * Usage:
37556  * <pre><code>
37557 // basic tabs 1, built from existing content
37558 var tabs = new Roo.TabPanel("tabs1");
37559 tabs.addTab("script", "View Script");
37560 tabs.addTab("markup", "View Markup");
37561 tabs.activate("script");
37562
37563 // more advanced tabs, built from javascript
37564 var jtabs = new Roo.TabPanel("jtabs");
37565 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37566
37567 // set up the UpdateManager
37568 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37569 var updater = tab2.getUpdateManager();
37570 updater.setDefaultUrl("ajax1.htm");
37571 tab2.on('activate', updater.refresh, updater, true);
37572
37573 // Use setUrl for Ajax loading
37574 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37575 tab3.setUrl("ajax2.htm", null, true);
37576
37577 // Disabled tab
37578 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37579 tab4.disable();
37580
37581 jtabs.activate("jtabs-1");
37582  * </code></pre>
37583  * @constructor
37584  * Create a new TabPanel.
37585  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37586  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37587  */
37588 Roo.bootstrap.panel.Tabs = function(config){
37589     /**
37590     * The container element for this TabPanel.
37591     * @type Roo.Element
37592     */
37593     this.el = Roo.get(config.el);
37594     delete config.el;
37595     if(config){
37596         if(typeof config == "boolean"){
37597             this.tabPosition = config ? "bottom" : "top";
37598         }else{
37599             Roo.apply(this, config);
37600         }
37601     }
37602     
37603     if(this.tabPosition == "bottom"){
37604         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37605         this.el.addClass("roo-tabs-bottom");
37606     }
37607     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37608     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37609     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37610     if(Roo.isIE){
37611         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37612     }
37613     if(this.tabPosition != "bottom"){
37614         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37615          * @type Roo.Element
37616          */
37617         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37618         this.el.addClass("roo-tabs-top");
37619     }
37620     this.items = [];
37621
37622     this.bodyEl.setStyle("position", "relative");
37623
37624     this.active = null;
37625     this.activateDelegate = this.activate.createDelegate(this);
37626
37627     this.addEvents({
37628         /**
37629          * @event tabchange
37630          * Fires when the active tab changes
37631          * @param {Roo.TabPanel} this
37632          * @param {Roo.TabPanelItem} activePanel The new active tab
37633          */
37634         "tabchange": true,
37635         /**
37636          * @event beforetabchange
37637          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37638          * @param {Roo.TabPanel} this
37639          * @param {Object} e Set cancel to true on this object to cancel the tab change
37640          * @param {Roo.TabPanelItem} tab The tab being changed to
37641          */
37642         "beforetabchange" : true
37643     });
37644
37645     Roo.EventManager.onWindowResize(this.onResize, this);
37646     this.cpad = this.el.getPadding("lr");
37647     this.hiddenCount = 0;
37648
37649
37650     // toolbar on the tabbar support...
37651     if (this.toolbar) {
37652         alert("no toolbar support yet");
37653         this.toolbar  = false;
37654         /*
37655         var tcfg = this.toolbar;
37656         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37657         this.toolbar = new Roo.Toolbar(tcfg);
37658         if (Roo.isSafari) {
37659             var tbl = tcfg.container.child('table', true);
37660             tbl.setAttribute('width', '100%');
37661         }
37662         */
37663         
37664     }
37665    
37666
37667
37668     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37669 };
37670
37671 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37672     /*
37673      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37674      */
37675     tabPosition : "top",
37676     /*
37677      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37678      */
37679     currentTabWidth : 0,
37680     /*
37681      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37682      */
37683     minTabWidth : 40,
37684     /*
37685      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37686      */
37687     maxTabWidth : 250,
37688     /*
37689      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37690      */
37691     preferredTabWidth : 175,
37692     /*
37693      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37694      */
37695     resizeTabs : false,
37696     /*
37697      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37698      */
37699     monitorResize : true,
37700     /*
37701      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37702      */
37703     toolbar : false,
37704
37705     /**
37706      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37707      * @param {String} id The id of the div to use <b>or create</b>
37708      * @param {String} text The text for the tab
37709      * @param {String} content (optional) Content to put in the TabPanelItem body
37710      * @param {Boolean} closable (optional) True to create a close icon on the tab
37711      * @return {Roo.TabPanelItem} The created TabPanelItem
37712      */
37713     addTab : function(id, text, content, closable, tpl)
37714     {
37715         var item = new Roo.bootstrap.panel.TabItem({
37716             panel: this,
37717             id : id,
37718             text : text,
37719             closable : closable,
37720             tpl : tpl
37721         });
37722         this.addTabItem(item);
37723         if(content){
37724             item.setContent(content);
37725         }
37726         return item;
37727     },
37728
37729     /**
37730      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37731      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37732      * @return {Roo.TabPanelItem}
37733      */
37734     getTab : function(id){
37735         return this.items[id];
37736     },
37737
37738     /**
37739      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37740      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37741      */
37742     hideTab : function(id){
37743         var t = this.items[id];
37744         if(!t.isHidden()){
37745            t.setHidden(true);
37746            this.hiddenCount++;
37747            this.autoSizeTabs();
37748         }
37749     },
37750
37751     /**
37752      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37753      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37754      */
37755     unhideTab : function(id){
37756         var t = this.items[id];
37757         if(t.isHidden()){
37758            t.setHidden(false);
37759            this.hiddenCount--;
37760            this.autoSizeTabs();
37761         }
37762     },
37763
37764     /**
37765      * Adds an existing {@link Roo.TabPanelItem}.
37766      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37767      */
37768     addTabItem : function(item){
37769         this.items[item.id] = item;
37770         this.items.push(item);
37771       //  if(this.resizeTabs){
37772     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37773   //         this.autoSizeTabs();
37774 //        }else{
37775 //            item.autoSize();
37776        // }
37777     },
37778
37779     /**
37780      * Removes a {@link Roo.TabPanelItem}.
37781      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37782      */
37783     removeTab : function(id){
37784         var items = this.items;
37785         var tab = items[id];
37786         if(!tab) { return; }
37787         var index = items.indexOf(tab);
37788         if(this.active == tab && items.length > 1){
37789             var newTab = this.getNextAvailable(index);
37790             if(newTab) {
37791                 newTab.activate();
37792             }
37793         }
37794         this.stripEl.dom.removeChild(tab.pnode.dom);
37795         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37796             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37797         }
37798         items.splice(index, 1);
37799         delete this.items[tab.id];
37800         tab.fireEvent("close", tab);
37801         tab.purgeListeners();
37802         this.autoSizeTabs();
37803     },
37804
37805     getNextAvailable : function(start){
37806         var items = this.items;
37807         var index = start;
37808         // look for a next tab that will slide over to
37809         // replace the one being removed
37810         while(index < items.length){
37811             var item = items[++index];
37812             if(item && !item.isHidden()){
37813                 return item;
37814             }
37815         }
37816         // if one isn't found select the previous tab (on the left)
37817         index = start;
37818         while(index >= 0){
37819             var item = items[--index];
37820             if(item && !item.isHidden()){
37821                 return item;
37822             }
37823         }
37824         return null;
37825     },
37826
37827     /**
37828      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37829      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37830      */
37831     disableTab : function(id){
37832         var tab = this.items[id];
37833         if(tab && this.active != tab){
37834             tab.disable();
37835         }
37836     },
37837
37838     /**
37839      * Enables a {@link Roo.TabPanelItem} that is disabled.
37840      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37841      */
37842     enableTab : function(id){
37843         var tab = this.items[id];
37844         tab.enable();
37845     },
37846
37847     /**
37848      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37849      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37850      * @return {Roo.TabPanelItem} The TabPanelItem.
37851      */
37852     activate : function(id){
37853         var tab = this.items[id];
37854         if(!tab){
37855             return null;
37856         }
37857         if(tab == this.active || tab.disabled){
37858             return tab;
37859         }
37860         var e = {};
37861         this.fireEvent("beforetabchange", this, e, tab);
37862         if(e.cancel !== true && !tab.disabled){
37863             if(this.active){
37864                 this.active.hide();
37865             }
37866             this.active = this.items[id];
37867             this.active.show();
37868             this.fireEvent("tabchange", this, this.active);
37869         }
37870         return tab;
37871     },
37872
37873     /**
37874      * Gets the active {@link Roo.TabPanelItem}.
37875      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37876      */
37877     getActiveTab : function(){
37878         return this.active;
37879     },
37880
37881     /**
37882      * Updates the tab body element to fit the height of the container element
37883      * for overflow scrolling
37884      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37885      */
37886     syncHeight : function(targetHeight){
37887         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37888         var bm = this.bodyEl.getMargins();
37889         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37890         this.bodyEl.setHeight(newHeight);
37891         return newHeight;
37892     },
37893
37894     onResize : function(){
37895         if(this.monitorResize){
37896             this.autoSizeTabs();
37897         }
37898     },
37899
37900     /**
37901      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37902      */
37903     beginUpdate : function(){
37904         this.updating = true;
37905     },
37906
37907     /**
37908      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37909      */
37910     endUpdate : function(){
37911         this.updating = false;
37912         this.autoSizeTabs();
37913     },
37914
37915     /**
37916      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37917      */
37918     autoSizeTabs : function(){
37919         var count = this.items.length;
37920         var vcount = count - this.hiddenCount;
37921         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37922             return;
37923         }
37924         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37925         var availWidth = Math.floor(w / vcount);
37926         var b = this.stripBody;
37927         if(b.getWidth() > w){
37928             var tabs = this.items;
37929             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37930             if(availWidth < this.minTabWidth){
37931                 /*if(!this.sleft){    // incomplete scrolling code
37932                     this.createScrollButtons();
37933                 }
37934                 this.showScroll();
37935                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37936             }
37937         }else{
37938             if(this.currentTabWidth < this.preferredTabWidth){
37939                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37940             }
37941         }
37942     },
37943
37944     /**
37945      * Returns the number of tabs in this TabPanel.
37946      * @return {Number}
37947      */
37948      getCount : function(){
37949          return this.items.length;
37950      },
37951
37952     /**
37953      * Resizes all the tabs to the passed width
37954      * @param {Number} The new width
37955      */
37956     setTabWidth : function(width){
37957         this.currentTabWidth = width;
37958         for(var i = 0, len = this.items.length; i < len; i++) {
37959                 if(!this.items[i].isHidden()) {
37960                 this.items[i].setWidth(width);
37961             }
37962         }
37963     },
37964
37965     /**
37966      * Destroys this TabPanel
37967      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37968      */
37969     destroy : function(removeEl){
37970         Roo.EventManager.removeResizeListener(this.onResize, this);
37971         for(var i = 0, len = this.items.length; i < len; i++){
37972             this.items[i].purgeListeners();
37973         }
37974         if(removeEl === true){
37975             this.el.update("");
37976             this.el.remove();
37977         }
37978     },
37979     
37980     createStrip : function(container)
37981     {
37982         var strip = document.createElement("nav");
37983         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37984         container.appendChild(strip);
37985         return strip;
37986     },
37987     
37988     createStripList : function(strip)
37989     {
37990         // div wrapper for retard IE
37991         // returns the "tr" element.
37992         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37993         //'<div class="x-tabs-strip-wrap">'+
37994           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37995           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37996         return strip.firstChild; //.firstChild.firstChild.firstChild;
37997     },
37998     createBody : function(container)
37999     {
38000         var body = document.createElement("div");
38001         Roo.id(body, "tab-body");
38002         //Roo.fly(body).addClass("x-tabs-body");
38003         Roo.fly(body).addClass("tab-content");
38004         container.appendChild(body);
38005         return body;
38006     },
38007     createItemBody :function(bodyEl, id){
38008         var body = Roo.getDom(id);
38009         if(!body){
38010             body = document.createElement("div");
38011             body.id = id;
38012         }
38013         //Roo.fly(body).addClass("x-tabs-item-body");
38014         Roo.fly(body).addClass("tab-pane");
38015          bodyEl.insertBefore(body, bodyEl.firstChild);
38016         return body;
38017     },
38018     /** @private */
38019     createStripElements :  function(stripEl, text, closable, tpl)
38020     {
38021         var td = document.createElement("li"); // was td..
38022         
38023         
38024         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38025         
38026         
38027         stripEl.appendChild(td);
38028         /*if(closable){
38029             td.className = "x-tabs-closable";
38030             if(!this.closeTpl){
38031                 this.closeTpl = new Roo.Template(
38032                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38033                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38034                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38035                 );
38036             }
38037             var el = this.closeTpl.overwrite(td, {"text": text});
38038             var close = el.getElementsByTagName("div")[0];
38039             var inner = el.getElementsByTagName("em")[0];
38040             return {"el": el, "close": close, "inner": inner};
38041         } else {
38042         */
38043         // not sure what this is..
38044 //            if(!this.tabTpl){
38045                 //this.tabTpl = new Roo.Template(
38046                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38047                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38048                 //);
38049 //                this.tabTpl = new Roo.Template(
38050 //                   '<a href="#">' +
38051 //                   '<span unselectable="on"' +
38052 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38053 //                            ' >{text}</span></a>'
38054 //                );
38055 //                
38056 //            }
38057
38058
38059             var template = tpl || this.tabTpl || false;
38060             
38061             if(!template){
38062                 
38063                 template = new Roo.Template(
38064                    '<a href="#">' +
38065                    '<span unselectable="on"' +
38066                             (this.disableTooltips ? '' : ' title="{text}"') +
38067                             ' >{text}</span></a>'
38068                 );
38069             }
38070             
38071             switch (typeof(template)) {
38072                 case 'object' :
38073                     break;
38074                 case 'string' :
38075                     template = new Roo.Template(template);
38076                     break;
38077                 default :
38078                     break;
38079             }
38080             
38081             var el = template.overwrite(td, {"text": text});
38082             
38083             var inner = el.getElementsByTagName("span")[0];
38084             
38085             return {"el": el, "inner": inner};
38086             
38087     }
38088         
38089     
38090 });
38091
38092 /**
38093  * @class Roo.TabPanelItem
38094  * @extends Roo.util.Observable
38095  * Represents an individual item (tab plus body) in a TabPanel.
38096  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38097  * @param {String} id The id of this TabPanelItem
38098  * @param {String} text The text for the tab of this TabPanelItem
38099  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38100  */
38101 Roo.bootstrap.panel.TabItem = function(config){
38102     /**
38103      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38104      * @type Roo.TabPanel
38105      */
38106     this.tabPanel = config.panel;
38107     /**
38108      * The id for this TabPanelItem
38109      * @type String
38110      */
38111     this.id = config.id;
38112     /** @private */
38113     this.disabled = false;
38114     /** @private */
38115     this.text = config.text;
38116     /** @private */
38117     this.loaded = false;
38118     this.closable = config.closable;
38119
38120     /**
38121      * The body element for this TabPanelItem.
38122      * @type Roo.Element
38123      */
38124     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38125     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38126     this.bodyEl.setStyle("display", "block");
38127     this.bodyEl.setStyle("zoom", "1");
38128     //this.hideAction();
38129
38130     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38131     /** @private */
38132     this.el = Roo.get(els.el);
38133     this.inner = Roo.get(els.inner, true);
38134     this.textEl = Roo.get(this.el.dom.firstChild, true);
38135     this.pnode = Roo.get(els.el.parentNode, true);
38136 //    this.el.on("mousedown", this.onTabMouseDown, this);
38137     this.el.on("click", this.onTabClick, this);
38138     /** @private */
38139     if(config.closable){
38140         var c = Roo.get(els.close, true);
38141         c.dom.title = this.closeText;
38142         c.addClassOnOver("close-over");
38143         c.on("click", this.closeClick, this);
38144      }
38145
38146     this.addEvents({
38147          /**
38148          * @event activate
38149          * Fires when this tab becomes the active tab.
38150          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38151          * @param {Roo.TabPanelItem} this
38152          */
38153         "activate": true,
38154         /**
38155          * @event beforeclose
38156          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38157          * @param {Roo.TabPanelItem} this
38158          * @param {Object} e Set cancel to true on this object to cancel the close.
38159          */
38160         "beforeclose": true,
38161         /**
38162          * @event close
38163          * Fires when this tab is closed.
38164          * @param {Roo.TabPanelItem} this
38165          */
38166          "close": true,
38167         /**
38168          * @event deactivate
38169          * Fires when this tab is no longer the active tab.
38170          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38171          * @param {Roo.TabPanelItem} this
38172          */
38173          "deactivate" : true
38174     });
38175     this.hidden = false;
38176
38177     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38178 };
38179
38180 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38181            {
38182     purgeListeners : function(){
38183        Roo.util.Observable.prototype.purgeListeners.call(this);
38184        this.el.removeAllListeners();
38185     },
38186     /**
38187      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38188      */
38189     show : function(){
38190         this.pnode.addClass("active");
38191         this.showAction();
38192         if(Roo.isOpera){
38193             this.tabPanel.stripWrap.repaint();
38194         }
38195         this.fireEvent("activate", this.tabPanel, this);
38196     },
38197
38198     /**
38199      * Returns true if this tab is the active tab.
38200      * @return {Boolean}
38201      */
38202     isActive : function(){
38203         return this.tabPanel.getActiveTab() == this;
38204     },
38205
38206     /**
38207      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38208      */
38209     hide : function(){
38210         this.pnode.removeClass("active");
38211         this.hideAction();
38212         this.fireEvent("deactivate", this.tabPanel, this);
38213     },
38214
38215     hideAction : function(){
38216         this.bodyEl.hide();
38217         this.bodyEl.setStyle("position", "absolute");
38218         this.bodyEl.setLeft("-20000px");
38219         this.bodyEl.setTop("-20000px");
38220     },
38221
38222     showAction : function(){
38223         this.bodyEl.setStyle("position", "relative");
38224         this.bodyEl.setTop("");
38225         this.bodyEl.setLeft("");
38226         this.bodyEl.show();
38227     },
38228
38229     /**
38230      * Set the tooltip for the tab.
38231      * @param {String} tooltip The tab's tooltip
38232      */
38233     setTooltip : function(text){
38234         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38235             this.textEl.dom.qtip = text;
38236             this.textEl.dom.removeAttribute('title');
38237         }else{
38238             this.textEl.dom.title = text;
38239         }
38240     },
38241
38242     onTabClick : function(e){
38243         e.preventDefault();
38244         this.tabPanel.activate(this.id);
38245     },
38246
38247     onTabMouseDown : function(e){
38248         e.preventDefault();
38249         this.tabPanel.activate(this.id);
38250     },
38251 /*
38252     getWidth : function(){
38253         return this.inner.getWidth();
38254     },
38255
38256     setWidth : function(width){
38257         var iwidth = width - this.pnode.getPadding("lr");
38258         this.inner.setWidth(iwidth);
38259         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38260         this.pnode.setWidth(width);
38261     },
38262 */
38263     /**
38264      * Show or hide the tab
38265      * @param {Boolean} hidden True to hide or false to show.
38266      */
38267     setHidden : function(hidden){
38268         this.hidden = hidden;
38269         this.pnode.setStyle("display", hidden ? "none" : "");
38270     },
38271
38272     /**
38273      * Returns true if this tab is "hidden"
38274      * @return {Boolean}
38275      */
38276     isHidden : function(){
38277         return this.hidden;
38278     },
38279
38280     /**
38281      * Returns the text for this tab
38282      * @return {String}
38283      */
38284     getText : function(){
38285         return this.text;
38286     },
38287     /*
38288     autoSize : function(){
38289         //this.el.beginMeasure();
38290         this.textEl.setWidth(1);
38291         /*
38292          *  #2804 [new] Tabs in Roojs
38293          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38294          */
38295         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38296         //this.el.endMeasure();
38297     //},
38298
38299     /**
38300      * Sets the text for the tab (Note: this also sets the tooltip text)
38301      * @param {String} text The tab's text and tooltip
38302      */
38303     setText : function(text){
38304         this.text = text;
38305         this.textEl.update(text);
38306         this.setTooltip(text);
38307         //if(!this.tabPanel.resizeTabs){
38308         //    this.autoSize();
38309         //}
38310     },
38311     /**
38312      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38313      */
38314     activate : function(){
38315         this.tabPanel.activate(this.id);
38316     },
38317
38318     /**
38319      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38320      */
38321     disable : function(){
38322         if(this.tabPanel.active != this){
38323             this.disabled = true;
38324             this.pnode.addClass("disabled");
38325         }
38326     },
38327
38328     /**
38329      * Enables this TabPanelItem if it was previously disabled.
38330      */
38331     enable : function(){
38332         this.disabled = false;
38333         this.pnode.removeClass("disabled");
38334     },
38335
38336     /**
38337      * Sets the content for this TabPanelItem.
38338      * @param {String} content The content
38339      * @param {Boolean} loadScripts true to look for and load scripts
38340      */
38341     setContent : function(content, loadScripts){
38342         this.bodyEl.update(content, loadScripts);
38343     },
38344
38345     /**
38346      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38347      * @return {Roo.UpdateManager} The UpdateManager
38348      */
38349     getUpdateManager : function(){
38350         return this.bodyEl.getUpdateManager();
38351     },
38352
38353     /**
38354      * Set a URL to be used to load the content for this TabPanelItem.
38355      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38356      * @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)
38357      * @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)
38358      * @return {Roo.UpdateManager} The UpdateManager
38359      */
38360     setUrl : function(url, params, loadOnce){
38361         if(this.refreshDelegate){
38362             this.un('activate', this.refreshDelegate);
38363         }
38364         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38365         this.on("activate", this.refreshDelegate);
38366         return this.bodyEl.getUpdateManager();
38367     },
38368
38369     /** @private */
38370     _handleRefresh : function(url, params, loadOnce){
38371         if(!loadOnce || !this.loaded){
38372             var updater = this.bodyEl.getUpdateManager();
38373             updater.update(url, params, this._setLoaded.createDelegate(this));
38374         }
38375     },
38376
38377     /**
38378      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38379      *   Will fail silently if the setUrl method has not been called.
38380      *   This does not activate the panel, just updates its content.
38381      */
38382     refresh : function(){
38383         if(this.refreshDelegate){
38384            this.loaded = false;
38385            this.refreshDelegate();
38386         }
38387     },
38388
38389     /** @private */
38390     _setLoaded : function(){
38391         this.loaded = true;
38392     },
38393
38394     /** @private */
38395     closeClick : function(e){
38396         var o = {};
38397         e.stopEvent();
38398         this.fireEvent("beforeclose", this, o);
38399         if(o.cancel !== true){
38400             this.tabPanel.removeTab(this.id);
38401         }
38402     },
38403     /**
38404      * The text displayed in the tooltip for the close icon.
38405      * @type String
38406      */
38407     closeText : "Close this tab"
38408 });
38409 /**
38410 *    This script refer to:
38411 *    Title: International Telephone Input
38412 *    Author: Jack O'Connor
38413 *    Code version:  v12.1.12
38414 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38415 **/
38416
38417 Roo.bootstrap.PhoneInputData = function() {
38418     var d = [
38419       [
38420         "Afghanistan (‫افغانستان‬‎)",
38421         "af",
38422         "93"
38423       ],
38424       [
38425         "Albania (Shqipëri)",
38426         "al",
38427         "355"
38428       ],
38429       [
38430         "Algeria (‫الجزائر‬‎)",
38431         "dz",
38432         "213"
38433       ],
38434       [
38435         "American Samoa",
38436         "as",
38437         "1684"
38438       ],
38439       [
38440         "Andorra",
38441         "ad",
38442         "376"
38443       ],
38444       [
38445         "Angola",
38446         "ao",
38447         "244"
38448       ],
38449       [
38450         "Anguilla",
38451         "ai",
38452         "1264"
38453       ],
38454       [
38455         "Antigua and Barbuda",
38456         "ag",
38457         "1268"
38458       ],
38459       [
38460         "Argentina",
38461         "ar",
38462         "54"
38463       ],
38464       [
38465         "Armenia (Հայաստան)",
38466         "am",
38467         "374"
38468       ],
38469       [
38470         "Aruba",
38471         "aw",
38472         "297"
38473       ],
38474       [
38475         "Australia",
38476         "au",
38477         "61",
38478         0
38479       ],
38480       [
38481         "Austria (Österreich)",
38482         "at",
38483         "43"
38484       ],
38485       [
38486         "Azerbaijan (Azərbaycan)",
38487         "az",
38488         "994"
38489       ],
38490       [
38491         "Bahamas",
38492         "bs",
38493         "1242"
38494       ],
38495       [
38496         "Bahrain (‫البحرين‬‎)",
38497         "bh",
38498         "973"
38499       ],
38500       [
38501         "Bangladesh (বাংলাদেশ)",
38502         "bd",
38503         "880"
38504       ],
38505       [
38506         "Barbados",
38507         "bb",
38508         "1246"
38509       ],
38510       [
38511         "Belarus (Беларусь)",
38512         "by",
38513         "375"
38514       ],
38515       [
38516         "Belgium (België)",
38517         "be",
38518         "32"
38519       ],
38520       [
38521         "Belize",
38522         "bz",
38523         "501"
38524       ],
38525       [
38526         "Benin (Bénin)",
38527         "bj",
38528         "229"
38529       ],
38530       [
38531         "Bermuda",
38532         "bm",
38533         "1441"
38534       ],
38535       [
38536         "Bhutan (འབྲུག)",
38537         "bt",
38538         "975"
38539       ],
38540       [
38541         "Bolivia",
38542         "bo",
38543         "591"
38544       ],
38545       [
38546         "Bosnia and Herzegovina (Босна и Херцеговина)",
38547         "ba",
38548         "387"
38549       ],
38550       [
38551         "Botswana",
38552         "bw",
38553         "267"
38554       ],
38555       [
38556         "Brazil (Brasil)",
38557         "br",
38558         "55"
38559       ],
38560       [
38561         "British Indian Ocean Territory",
38562         "io",
38563         "246"
38564       ],
38565       [
38566         "British Virgin Islands",
38567         "vg",
38568         "1284"
38569       ],
38570       [
38571         "Brunei",
38572         "bn",
38573         "673"
38574       ],
38575       [
38576         "Bulgaria (България)",
38577         "bg",
38578         "359"
38579       ],
38580       [
38581         "Burkina Faso",
38582         "bf",
38583         "226"
38584       ],
38585       [
38586         "Burundi (Uburundi)",
38587         "bi",
38588         "257"
38589       ],
38590       [
38591         "Cambodia (កម្ពុជា)",
38592         "kh",
38593         "855"
38594       ],
38595       [
38596         "Cameroon (Cameroun)",
38597         "cm",
38598         "237"
38599       ],
38600       [
38601         "Canada",
38602         "ca",
38603         "1",
38604         1,
38605         ["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"]
38606       ],
38607       [
38608         "Cape Verde (Kabu Verdi)",
38609         "cv",
38610         "238"
38611       ],
38612       [
38613         "Caribbean Netherlands",
38614         "bq",
38615         "599",
38616         1
38617       ],
38618       [
38619         "Cayman Islands",
38620         "ky",
38621         "1345"
38622       ],
38623       [
38624         "Central African Republic (République centrafricaine)",
38625         "cf",
38626         "236"
38627       ],
38628       [
38629         "Chad (Tchad)",
38630         "td",
38631         "235"
38632       ],
38633       [
38634         "Chile",
38635         "cl",
38636         "56"
38637       ],
38638       [
38639         "China (中国)",
38640         "cn",
38641         "86"
38642       ],
38643       [
38644         "Christmas Island",
38645         "cx",
38646         "61",
38647         2
38648       ],
38649       [
38650         "Cocos (Keeling) Islands",
38651         "cc",
38652         "61",
38653         1
38654       ],
38655       [
38656         "Colombia",
38657         "co",
38658         "57"
38659       ],
38660       [
38661         "Comoros (‫جزر القمر‬‎)",
38662         "km",
38663         "269"
38664       ],
38665       [
38666         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38667         "cd",
38668         "243"
38669       ],
38670       [
38671         "Congo (Republic) (Congo-Brazzaville)",
38672         "cg",
38673         "242"
38674       ],
38675       [
38676         "Cook Islands",
38677         "ck",
38678         "682"
38679       ],
38680       [
38681         "Costa Rica",
38682         "cr",
38683         "506"
38684       ],
38685       [
38686         "Côte d’Ivoire",
38687         "ci",
38688         "225"
38689       ],
38690       [
38691         "Croatia (Hrvatska)",
38692         "hr",
38693         "385"
38694       ],
38695       [
38696         "Cuba",
38697         "cu",
38698         "53"
38699       ],
38700       [
38701         "Curaçao",
38702         "cw",
38703         "599",
38704         0
38705       ],
38706       [
38707         "Cyprus (Κύπρος)",
38708         "cy",
38709         "357"
38710       ],
38711       [
38712         "Czech Republic (Česká republika)",
38713         "cz",
38714         "420"
38715       ],
38716       [
38717         "Denmark (Danmark)",
38718         "dk",
38719         "45"
38720       ],
38721       [
38722         "Djibouti",
38723         "dj",
38724         "253"
38725       ],
38726       [
38727         "Dominica",
38728         "dm",
38729         "1767"
38730       ],
38731       [
38732         "Dominican Republic (República Dominicana)",
38733         "do",
38734         "1",
38735         2,
38736         ["809", "829", "849"]
38737       ],
38738       [
38739         "Ecuador",
38740         "ec",
38741         "593"
38742       ],
38743       [
38744         "Egypt (‫مصر‬‎)",
38745         "eg",
38746         "20"
38747       ],
38748       [
38749         "El Salvador",
38750         "sv",
38751         "503"
38752       ],
38753       [
38754         "Equatorial Guinea (Guinea Ecuatorial)",
38755         "gq",
38756         "240"
38757       ],
38758       [
38759         "Eritrea",
38760         "er",
38761         "291"
38762       ],
38763       [
38764         "Estonia (Eesti)",
38765         "ee",
38766         "372"
38767       ],
38768       [
38769         "Ethiopia",
38770         "et",
38771         "251"
38772       ],
38773       [
38774         "Falkland Islands (Islas Malvinas)",
38775         "fk",
38776         "500"
38777       ],
38778       [
38779         "Faroe Islands (Føroyar)",
38780         "fo",
38781         "298"
38782       ],
38783       [
38784         "Fiji",
38785         "fj",
38786         "679"
38787       ],
38788       [
38789         "Finland (Suomi)",
38790         "fi",
38791         "358",
38792         0
38793       ],
38794       [
38795         "France",
38796         "fr",
38797         "33"
38798       ],
38799       [
38800         "French Guiana (Guyane française)",
38801         "gf",
38802         "594"
38803       ],
38804       [
38805         "French Polynesia (Polynésie française)",
38806         "pf",
38807         "689"
38808       ],
38809       [
38810         "Gabon",
38811         "ga",
38812         "241"
38813       ],
38814       [
38815         "Gambia",
38816         "gm",
38817         "220"
38818       ],
38819       [
38820         "Georgia (საქართველო)",
38821         "ge",
38822         "995"
38823       ],
38824       [
38825         "Germany (Deutschland)",
38826         "de",
38827         "49"
38828       ],
38829       [
38830         "Ghana (Gaana)",
38831         "gh",
38832         "233"
38833       ],
38834       [
38835         "Gibraltar",
38836         "gi",
38837         "350"
38838       ],
38839       [
38840         "Greece (Ελλάδα)",
38841         "gr",
38842         "30"
38843       ],
38844       [
38845         "Greenland (Kalaallit Nunaat)",
38846         "gl",
38847         "299"
38848       ],
38849       [
38850         "Grenada",
38851         "gd",
38852         "1473"
38853       ],
38854       [
38855         "Guadeloupe",
38856         "gp",
38857         "590",
38858         0
38859       ],
38860       [
38861         "Guam",
38862         "gu",
38863         "1671"
38864       ],
38865       [
38866         "Guatemala",
38867         "gt",
38868         "502"
38869       ],
38870       [
38871         "Guernsey",
38872         "gg",
38873         "44",
38874         1
38875       ],
38876       [
38877         "Guinea (Guinée)",
38878         "gn",
38879         "224"
38880       ],
38881       [
38882         "Guinea-Bissau (Guiné Bissau)",
38883         "gw",
38884         "245"
38885       ],
38886       [
38887         "Guyana",
38888         "gy",
38889         "592"
38890       ],
38891       [
38892         "Haiti",
38893         "ht",
38894         "509"
38895       ],
38896       [
38897         "Honduras",
38898         "hn",
38899         "504"
38900       ],
38901       [
38902         "Hong Kong (香港)",
38903         "hk",
38904         "852"
38905       ],
38906       [
38907         "Hungary (Magyarország)",
38908         "hu",
38909         "36"
38910       ],
38911       [
38912         "Iceland (Ísland)",
38913         "is",
38914         "354"
38915       ],
38916       [
38917         "India (भारत)",
38918         "in",
38919         "91"
38920       ],
38921       [
38922         "Indonesia",
38923         "id",
38924         "62"
38925       ],
38926       [
38927         "Iran (‫ایران‬‎)",
38928         "ir",
38929         "98"
38930       ],
38931       [
38932         "Iraq (‫العراق‬‎)",
38933         "iq",
38934         "964"
38935       ],
38936       [
38937         "Ireland",
38938         "ie",
38939         "353"
38940       ],
38941       [
38942         "Isle of Man",
38943         "im",
38944         "44",
38945         2
38946       ],
38947       [
38948         "Israel (‫ישראל‬‎)",
38949         "il",
38950         "972"
38951       ],
38952       [
38953         "Italy (Italia)",
38954         "it",
38955         "39",
38956         0
38957       ],
38958       [
38959         "Jamaica",
38960         "jm",
38961         "1876"
38962       ],
38963       [
38964         "Japan (日本)",
38965         "jp",
38966         "81"
38967       ],
38968       [
38969         "Jersey",
38970         "je",
38971         "44",
38972         3
38973       ],
38974       [
38975         "Jordan (‫الأردن‬‎)",
38976         "jo",
38977         "962"
38978       ],
38979       [
38980         "Kazakhstan (Казахстан)",
38981         "kz",
38982         "7",
38983         1
38984       ],
38985       [
38986         "Kenya",
38987         "ke",
38988         "254"
38989       ],
38990       [
38991         "Kiribati",
38992         "ki",
38993         "686"
38994       ],
38995       [
38996         "Kosovo",
38997         "xk",
38998         "383"
38999       ],
39000       [
39001         "Kuwait (‫الكويت‬‎)",
39002         "kw",
39003         "965"
39004       ],
39005       [
39006         "Kyrgyzstan (Кыргызстан)",
39007         "kg",
39008         "996"
39009       ],
39010       [
39011         "Laos (ລາວ)",
39012         "la",
39013         "856"
39014       ],
39015       [
39016         "Latvia (Latvija)",
39017         "lv",
39018         "371"
39019       ],
39020       [
39021         "Lebanon (‫لبنان‬‎)",
39022         "lb",
39023         "961"
39024       ],
39025       [
39026         "Lesotho",
39027         "ls",
39028         "266"
39029       ],
39030       [
39031         "Liberia",
39032         "lr",
39033         "231"
39034       ],
39035       [
39036         "Libya (‫ليبيا‬‎)",
39037         "ly",
39038         "218"
39039       ],
39040       [
39041         "Liechtenstein",
39042         "li",
39043         "423"
39044       ],
39045       [
39046         "Lithuania (Lietuva)",
39047         "lt",
39048         "370"
39049       ],
39050       [
39051         "Luxembourg",
39052         "lu",
39053         "352"
39054       ],
39055       [
39056         "Macau (澳門)",
39057         "mo",
39058         "853"
39059       ],
39060       [
39061         "Macedonia (FYROM) (Македонија)",
39062         "mk",
39063         "389"
39064       ],
39065       [
39066         "Madagascar (Madagasikara)",
39067         "mg",
39068         "261"
39069       ],
39070       [
39071         "Malawi",
39072         "mw",
39073         "265"
39074       ],
39075       [
39076         "Malaysia",
39077         "my",
39078         "60"
39079       ],
39080       [
39081         "Maldives",
39082         "mv",
39083         "960"
39084       ],
39085       [
39086         "Mali",
39087         "ml",
39088         "223"
39089       ],
39090       [
39091         "Malta",
39092         "mt",
39093         "356"
39094       ],
39095       [
39096         "Marshall Islands",
39097         "mh",
39098         "692"
39099       ],
39100       [
39101         "Martinique",
39102         "mq",
39103         "596"
39104       ],
39105       [
39106         "Mauritania (‫موريتانيا‬‎)",
39107         "mr",
39108         "222"
39109       ],
39110       [
39111         "Mauritius (Moris)",
39112         "mu",
39113         "230"
39114       ],
39115       [
39116         "Mayotte",
39117         "yt",
39118         "262",
39119         1
39120       ],
39121       [
39122         "Mexico (México)",
39123         "mx",
39124         "52"
39125       ],
39126       [
39127         "Micronesia",
39128         "fm",
39129         "691"
39130       ],
39131       [
39132         "Moldova (Republica Moldova)",
39133         "md",
39134         "373"
39135       ],
39136       [
39137         "Monaco",
39138         "mc",
39139         "377"
39140       ],
39141       [
39142         "Mongolia (Монгол)",
39143         "mn",
39144         "976"
39145       ],
39146       [
39147         "Montenegro (Crna Gora)",
39148         "me",
39149         "382"
39150       ],
39151       [
39152         "Montserrat",
39153         "ms",
39154         "1664"
39155       ],
39156       [
39157         "Morocco (‫المغرب‬‎)",
39158         "ma",
39159         "212",
39160         0
39161       ],
39162       [
39163         "Mozambique (Moçambique)",
39164         "mz",
39165         "258"
39166       ],
39167       [
39168         "Myanmar (Burma) (မြန်မာ)",
39169         "mm",
39170         "95"
39171       ],
39172       [
39173         "Namibia (Namibië)",
39174         "na",
39175         "264"
39176       ],
39177       [
39178         "Nauru",
39179         "nr",
39180         "674"
39181       ],
39182       [
39183         "Nepal (नेपाल)",
39184         "np",
39185         "977"
39186       ],
39187       [
39188         "Netherlands (Nederland)",
39189         "nl",
39190         "31"
39191       ],
39192       [
39193         "New Caledonia (Nouvelle-Calédonie)",
39194         "nc",
39195         "687"
39196       ],
39197       [
39198         "New Zealand",
39199         "nz",
39200         "64"
39201       ],
39202       [
39203         "Nicaragua",
39204         "ni",
39205         "505"
39206       ],
39207       [
39208         "Niger (Nijar)",
39209         "ne",
39210         "227"
39211       ],
39212       [
39213         "Nigeria",
39214         "ng",
39215         "234"
39216       ],
39217       [
39218         "Niue",
39219         "nu",
39220         "683"
39221       ],
39222       [
39223         "Norfolk Island",
39224         "nf",
39225         "672"
39226       ],
39227       [
39228         "North Korea (조선 민주주의 인민 공화국)",
39229         "kp",
39230         "850"
39231       ],
39232       [
39233         "Northern Mariana Islands",
39234         "mp",
39235         "1670"
39236       ],
39237       [
39238         "Norway (Norge)",
39239         "no",
39240         "47",
39241         0
39242       ],
39243       [
39244         "Oman (‫عُمان‬‎)",
39245         "om",
39246         "968"
39247       ],
39248       [
39249         "Pakistan (‫پاکستان‬‎)",
39250         "pk",
39251         "92"
39252       ],
39253       [
39254         "Palau",
39255         "pw",
39256         "680"
39257       ],
39258       [
39259         "Palestine (‫فلسطين‬‎)",
39260         "ps",
39261         "970"
39262       ],
39263       [
39264         "Panama (Panamá)",
39265         "pa",
39266         "507"
39267       ],
39268       [
39269         "Papua New Guinea",
39270         "pg",
39271         "675"
39272       ],
39273       [
39274         "Paraguay",
39275         "py",
39276         "595"
39277       ],
39278       [
39279         "Peru (Perú)",
39280         "pe",
39281         "51"
39282       ],
39283       [
39284         "Philippines",
39285         "ph",
39286         "63"
39287       ],
39288       [
39289         "Poland (Polska)",
39290         "pl",
39291         "48"
39292       ],
39293       [
39294         "Portugal",
39295         "pt",
39296         "351"
39297       ],
39298       [
39299         "Puerto Rico",
39300         "pr",
39301         "1",
39302         3,
39303         ["787", "939"]
39304       ],
39305       [
39306         "Qatar (‫قطر‬‎)",
39307         "qa",
39308         "974"
39309       ],
39310       [
39311         "Réunion (La Réunion)",
39312         "re",
39313         "262",
39314         0
39315       ],
39316       [
39317         "Romania (România)",
39318         "ro",
39319         "40"
39320       ],
39321       [
39322         "Russia (Россия)",
39323         "ru",
39324         "7",
39325         0
39326       ],
39327       [
39328         "Rwanda",
39329         "rw",
39330         "250"
39331       ],
39332       [
39333         "Saint Barthélemy",
39334         "bl",
39335         "590",
39336         1
39337       ],
39338       [
39339         "Saint Helena",
39340         "sh",
39341         "290"
39342       ],
39343       [
39344         "Saint Kitts and Nevis",
39345         "kn",
39346         "1869"
39347       ],
39348       [
39349         "Saint Lucia",
39350         "lc",
39351         "1758"
39352       ],
39353       [
39354         "Saint Martin (Saint-Martin (partie française))",
39355         "mf",
39356         "590",
39357         2
39358       ],
39359       [
39360         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39361         "pm",
39362         "508"
39363       ],
39364       [
39365         "Saint Vincent and the Grenadines",
39366         "vc",
39367         "1784"
39368       ],
39369       [
39370         "Samoa",
39371         "ws",
39372         "685"
39373       ],
39374       [
39375         "San Marino",
39376         "sm",
39377         "378"
39378       ],
39379       [
39380         "São Tomé and Príncipe (São Tomé e Príncipe)",
39381         "st",
39382         "239"
39383       ],
39384       [
39385         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39386         "sa",
39387         "966"
39388       ],
39389       [
39390         "Senegal (Sénégal)",
39391         "sn",
39392         "221"
39393       ],
39394       [
39395         "Serbia (Србија)",
39396         "rs",
39397         "381"
39398       ],
39399       [
39400         "Seychelles",
39401         "sc",
39402         "248"
39403       ],
39404       [
39405         "Sierra Leone",
39406         "sl",
39407         "232"
39408       ],
39409       [
39410         "Singapore",
39411         "sg",
39412         "65"
39413       ],
39414       [
39415         "Sint Maarten",
39416         "sx",
39417         "1721"
39418       ],
39419       [
39420         "Slovakia (Slovensko)",
39421         "sk",
39422         "421"
39423       ],
39424       [
39425         "Slovenia (Slovenija)",
39426         "si",
39427         "386"
39428       ],
39429       [
39430         "Solomon Islands",
39431         "sb",
39432         "677"
39433       ],
39434       [
39435         "Somalia (Soomaaliya)",
39436         "so",
39437         "252"
39438       ],
39439       [
39440         "South Africa",
39441         "za",
39442         "27"
39443       ],
39444       [
39445         "South Korea (대한민국)",
39446         "kr",
39447         "82"
39448       ],
39449       [
39450         "South Sudan (‫جنوب السودان‬‎)",
39451         "ss",
39452         "211"
39453       ],
39454       [
39455         "Spain (España)",
39456         "es",
39457         "34"
39458       ],
39459       [
39460         "Sri Lanka (ශ්‍රී ලංකාව)",
39461         "lk",
39462         "94"
39463       ],
39464       [
39465         "Sudan (‫السودان‬‎)",
39466         "sd",
39467         "249"
39468       ],
39469       [
39470         "Suriname",
39471         "sr",
39472         "597"
39473       ],
39474       [
39475         "Svalbard and Jan Mayen",
39476         "sj",
39477         "47",
39478         1
39479       ],
39480       [
39481         "Swaziland",
39482         "sz",
39483         "268"
39484       ],
39485       [
39486         "Sweden (Sverige)",
39487         "se",
39488         "46"
39489       ],
39490       [
39491         "Switzerland (Schweiz)",
39492         "ch",
39493         "41"
39494       ],
39495       [
39496         "Syria (‫سوريا‬‎)",
39497         "sy",
39498         "963"
39499       ],
39500       [
39501         "Taiwan (台灣)",
39502         "tw",
39503         "886"
39504       ],
39505       [
39506         "Tajikistan",
39507         "tj",
39508         "992"
39509       ],
39510       [
39511         "Tanzania",
39512         "tz",
39513         "255"
39514       ],
39515       [
39516         "Thailand (ไทย)",
39517         "th",
39518         "66"
39519       ],
39520       [
39521         "Timor-Leste",
39522         "tl",
39523         "670"
39524       ],
39525       [
39526         "Togo",
39527         "tg",
39528         "228"
39529       ],
39530       [
39531         "Tokelau",
39532         "tk",
39533         "690"
39534       ],
39535       [
39536         "Tonga",
39537         "to",
39538         "676"
39539       ],
39540       [
39541         "Trinidad and Tobago",
39542         "tt",
39543         "1868"
39544       ],
39545       [
39546         "Tunisia (‫تونس‬‎)",
39547         "tn",
39548         "216"
39549       ],
39550       [
39551         "Turkey (Türkiye)",
39552         "tr",
39553         "90"
39554       ],
39555       [
39556         "Turkmenistan",
39557         "tm",
39558         "993"
39559       ],
39560       [
39561         "Turks and Caicos Islands",
39562         "tc",
39563         "1649"
39564       ],
39565       [
39566         "Tuvalu",
39567         "tv",
39568         "688"
39569       ],
39570       [
39571         "U.S. Virgin Islands",
39572         "vi",
39573         "1340"
39574       ],
39575       [
39576         "Uganda",
39577         "ug",
39578         "256"
39579       ],
39580       [
39581         "Ukraine (Україна)",
39582         "ua",
39583         "380"
39584       ],
39585       [
39586         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39587         "ae",
39588         "971"
39589       ],
39590       [
39591         "United Kingdom",
39592         "gb",
39593         "44",
39594         0
39595       ],
39596       [
39597         "United States",
39598         "us",
39599         "1",
39600         0
39601       ],
39602       [
39603         "Uruguay",
39604         "uy",
39605         "598"
39606       ],
39607       [
39608         "Uzbekistan (Oʻzbekiston)",
39609         "uz",
39610         "998"
39611       ],
39612       [
39613         "Vanuatu",
39614         "vu",
39615         "678"
39616       ],
39617       [
39618         "Vatican City (Città del Vaticano)",
39619         "va",
39620         "39",
39621         1
39622       ],
39623       [
39624         "Venezuela",
39625         "ve",
39626         "58"
39627       ],
39628       [
39629         "Vietnam (Việt Nam)",
39630         "vn",
39631         "84"
39632       ],
39633       [
39634         "Wallis and Futuna (Wallis-et-Futuna)",
39635         "wf",
39636         "681"
39637       ],
39638       [
39639         "Western Sahara (‫الصحراء الغربية‬‎)",
39640         "eh",
39641         "212",
39642         1
39643       ],
39644       [
39645         "Yemen (‫اليمن‬‎)",
39646         "ye",
39647         "967"
39648       ],
39649       [
39650         "Zambia",
39651         "zm",
39652         "260"
39653       ],
39654       [
39655         "Zimbabwe",
39656         "zw",
39657         "263"
39658       ],
39659       [
39660         "Åland Islands",
39661         "ax",
39662         "358",
39663         1
39664       ]
39665   ];
39666   
39667   return d;
39668 }/**
39669 *    This script refer to:
39670 *    Title: International Telephone Input
39671 *    Author: Jack O'Connor
39672 *    Code version:  v12.1.12
39673 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39674 **/
39675
39676 /**
39677  * @class Roo.bootstrap.PhoneInput
39678  * @extends Roo.bootstrap.TriggerField
39679  * An input with International dial-code selection
39680  
39681  * @cfg {String} defaultDialCode default '+852'
39682  * @cfg {Array} preferedCountries default []
39683   
39684  * @constructor
39685  * Create a new PhoneInput.
39686  * @param {Object} config Configuration options
39687  */
39688
39689 Roo.bootstrap.PhoneInput = function(config) {
39690     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39691 };
39692
39693 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39694         
39695         listWidth: undefined,
39696         
39697         selectedClass: 'active',
39698         
39699         invalidClass : "has-warning",
39700         
39701         validClass: 'has-success',
39702         
39703         allowed: '0123456789',
39704         
39705         /**
39706          * @cfg {String} defaultDialCode The default dial code when initializing the input
39707          */
39708         defaultDialCode: '+852',
39709         
39710         /**
39711          * @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
39712          */
39713         preferedCountries: false,
39714         
39715         getAutoCreate : function()
39716         {
39717             var data = Roo.bootstrap.PhoneInputData();
39718             var align = this.labelAlign || this.parentLabelAlign();
39719             var id = Roo.id();
39720             
39721             this.allCountries = [];
39722             this.dialCodeMapping = [];
39723             
39724             for (var i = 0; i < data.length; i++) {
39725               var c = data[i];
39726               this.allCountries[i] = {
39727                 name: c[0],
39728                 iso2: c[1],
39729                 dialCode: c[2],
39730                 priority: c[3] || 0,
39731                 areaCodes: c[4] || null
39732               };
39733               this.dialCodeMapping[c[2]] = {
39734                   name: c[0],
39735                   iso2: c[1],
39736                   priority: c[3] || 0,
39737                   areaCodes: c[4] || null
39738               };
39739             }
39740             
39741             var cfg = {
39742                 cls: 'form-group',
39743                 cn: []
39744             };
39745             
39746             var input =  {
39747                 tag: 'input',
39748                 id : id,
39749                 cls : 'form-control tel-input',
39750                 autocomplete: 'new-password'
39751             };
39752             
39753             var hiddenInput = {
39754                 tag: 'input',
39755                 type: 'hidden',
39756                 cls: 'hidden-tel-input'
39757             };
39758             
39759             if (this.name) {
39760                 hiddenInput.name = this.name;
39761             }
39762             
39763             if (this.disabled) {
39764                 input.disabled = true;
39765             }
39766             
39767             var flag_container = {
39768                 tag: 'div',
39769                 cls: 'flag-box',
39770                 cn: [
39771                     {
39772                         tag: 'div',
39773                         cls: 'flag'
39774                     },
39775                     {
39776                         tag: 'div',
39777                         cls: 'caret'
39778                     }
39779                 ]
39780             };
39781             
39782             var box = {
39783                 tag: 'div',
39784                 cls: this.hasFeedback ? 'has-feedback' : '',
39785                 cn: [
39786                     hiddenInput,
39787                     input,
39788                     {
39789                         tag: 'input',
39790                         cls: 'dial-code-holder',
39791                         disabled: true
39792                     }
39793                 ]
39794             };
39795             
39796             var container = {
39797                 cls: 'roo-select2-container input-group',
39798                 cn: [
39799                     flag_container,
39800                     box
39801                 ]
39802             };
39803             
39804             if (this.fieldLabel.length) {
39805                 var indicator = {
39806                     tag: 'i',
39807                     tooltip: 'This field is required'
39808                 };
39809                 
39810                 var label = {
39811                     tag: 'label',
39812                     'for':  id,
39813                     cls: 'control-label',
39814                     cn: []
39815                 };
39816                 
39817                 var label_text = {
39818                     tag: 'span',
39819                     html: this.fieldLabel
39820                 };
39821                 
39822                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39823                 label.cn = [
39824                     indicator,
39825                     label_text
39826                 ];
39827                 
39828                 if(this.indicatorpos == 'right') {
39829                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39830                     label.cn = [
39831                         label_text,
39832                         indicator
39833                     ];
39834                 }
39835                 
39836                 if(align == 'left') {
39837                     container = {
39838                         tag: 'div',
39839                         cn: [
39840                             container
39841                         ]
39842                     };
39843                     
39844                     if(this.labelWidth > 12){
39845                         label.style = "width: " + this.labelWidth + 'px';
39846                     }
39847                     if(this.labelWidth < 13 && this.labelmd == 0){
39848                         this.labelmd = this.labelWidth;
39849                     }
39850                     if(this.labellg > 0){
39851                         label.cls += ' col-lg-' + this.labellg;
39852                         input.cls += ' col-lg-' + (12 - this.labellg);
39853                     }
39854                     if(this.labelmd > 0){
39855                         label.cls += ' col-md-' + this.labelmd;
39856                         container.cls += ' col-md-' + (12 - this.labelmd);
39857                     }
39858                     if(this.labelsm > 0){
39859                         label.cls += ' col-sm-' + this.labelsm;
39860                         container.cls += ' col-sm-' + (12 - this.labelsm);
39861                     }
39862                     if(this.labelxs > 0){
39863                         label.cls += ' col-xs-' + this.labelxs;
39864                         container.cls += ' col-xs-' + (12 - this.labelxs);
39865                     }
39866                 }
39867             }
39868             
39869             cfg.cn = [
39870                 label,
39871                 container
39872             ];
39873             
39874             var settings = this;
39875             
39876             ['xs','sm','md','lg'].map(function(size){
39877                 if (settings[size]) {
39878                     cfg.cls += ' col-' + size + '-' + settings[size];
39879                 }
39880             });
39881             
39882             this.store = new Roo.data.Store({
39883                 proxy : new Roo.data.MemoryProxy({}),
39884                 reader : new Roo.data.JsonReader({
39885                     fields : [
39886                         {
39887                             'name' : 'name',
39888                             'type' : 'string'
39889                         },
39890                         {
39891                             'name' : 'iso2',
39892                             'type' : 'string'
39893                         },
39894                         {
39895                             'name' : 'dialCode',
39896                             'type' : 'string'
39897                         },
39898                         {
39899                             'name' : 'priority',
39900                             'type' : 'string'
39901                         },
39902                         {
39903                             'name' : 'areaCodes',
39904                             'type' : 'string'
39905                         }
39906                     ]
39907                 })
39908             });
39909             
39910             if(!this.preferedCountries) {
39911                 this.preferedCountries = [
39912                     'hk',
39913                     'gb',
39914                     'us'
39915                 ];
39916             }
39917             
39918             var p = this.preferedCountries.reverse();
39919             
39920             if(p) {
39921                 for (var i = 0; i < p.length; i++) {
39922                     for (var j = 0; j < this.allCountries.length; j++) {
39923                         if(this.allCountries[j].iso2 == p[i]) {
39924                             var t = this.allCountries[j];
39925                             this.allCountries.splice(j,1);
39926                             this.allCountries.unshift(t);
39927                         }
39928                     } 
39929                 }
39930             }
39931             
39932             this.store.proxy.data = {
39933                 success: true,
39934                 data: this.allCountries
39935             };
39936             
39937             return cfg;
39938         },
39939         
39940         initEvents : function()
39941         {
39942             this.createList();
39943             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39944             
39945             this.indicator = this.indicatorEl();
39946             this.flag = this.flagEl();
39947             this.dialCodeHolder = this.dialCodeHolderEl();
39948             
39949             this.trigger = this.el.select('div.flag-box',true).first();
39950             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39951             
39952             var _this = this;
39953             
39954             (function(){
39955                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39956                 _this.list.setWidth(lw);
39957             }).defer(100);
39958             
39959             this.list.on('mouseover', this.onViewOver, this);
39960             this.list.on('mousemove', this.onViewMove, this);
39961             this.inputEl().on("keyup", this.onKeyUp, this);
39962             
39963             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39964
39965             this.view = new Roo.View(this.list, this.tpl, {
39966                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39967             });
39968             
39969             this.view.on('click', this.onViewClick, this);
39970             this.setValue(this.defaultDialCode);
39971         },
39972         
39973         onTriggerClick : function(e)
39974         {
39975             Roo.log('trigger click');
39976             if(this.disabled){
39977                 return;
39978             }
39979             
39980             if(this.isExpanded()){
39981                 this.collapse();
39982                 this.hasFocus = false;
39983             }else {
39984                 this.store.load({});
39985                 this.hasFocus = true;
39986                 this.expand();
39987             }
39988         },
39989         
39990         isExpanded : function()
39991         {
39992             return this.list.isVisible();
39993         },
39994         
39995         collapse : function()
39996         {
39997             if(!this.isExpanded()){
39998                 return;
39999             }
40000             this.list.hide();
40001             Roo.get(document).un('mousedown', this.collapseIf, this);
40002             Roo.get(document).un('mousewheel', this.collapseIf, this);
40003             this.fireEvent('collapse', this);
40004             this.validate();
40005         },
40006         
40007         expand : function()
40008         {
40009             Roo.log('expand');
40010
40011             if(this.isExpanded() || !this.hasFocus){
40012                 return;
40013             }
40014             
40015             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40016             this.list.setWidth(lw);
40017             
40018             this.list.show();
40019             this.restrictHeight();
40020             
40021             Roo.get(document).on('mousedown', this.collapseIf, this);
40022             Roo.get(document).on('mousewheel', this.collapseIf, this);
40023             
40024             this.fireEvent('expand', this);
40025         },
40026         
40027         restrictHeight : function()
40028         {
40029             this.list.alignTo(this.inputEl(), this.listAlign);
40030             this.list.alignTo(this.inputEl(), this.listAlign);
40031         },
40032         
40033         onViewOver : function(e, t)
40034         {
40035             if(this.inKeyMode){
40036                 return;
40037             }
40038             var item = this.view.findItemFromChild(t);
40039             
40040             if(item){
40041                 var index = this.view.indexOf(item);
40042                 this.select(index, false);
40043             }
40044         },
40045
40046         // private
40047         onViewClick : function(view, doFocus, el, e)
40048         {
40049             var index = this.view.getSelectedIndexes()[0];
40050             
40051             var r = this.store.getAt(index);
40052             
40053             if(r){
40054                 this.onSelect(r, index);
40055             }
40056             if(doFocus !== false && !this.blockFocus){
40057                 this.inputEl().focus();
40058             }
40059         },
40060         
40061         onViewMove : function(e, t)
40062         {
40063             this.inKeyMode = false;
40064         },
40065         
40066         select : function(index, scrollIntoView)
40067         {
40068             this.selectedIndex = index;
40069             this.view.select(index);
40070             if(scrollIntoView !== false){
40071                 var el = this.view.getNode(index);
40072                 if(el){
40073                     this.list.scrollChildIntoView(el, false);
40074                 }
40075             }
40076         },
40077         
40078         createList : function()
40079         {
40080             this.list = Roo.get(document.body).createChild({
40081                 tag: 'ul',
40082                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40083                 style: 'display:none'
40084             });
40085             
40086             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40087         },
40088         
40089         collapseIf : function(e)
40090         {
40091             var in_combo  = e.within(this.el);
40092             var in_list =  e.within(this.list);
40093             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40094             
40095             if (in_combo || in_list || is_list) {
40096                 return;
40097             }
40098             this.collapse();
40099         },
40100         
40101         onSelect : function(record, index)
40102         {
40103             if(this.fireEvent('beforeselect', this, record, index) !== false){
40104                 
40105                 this.setFlagClass(record.data.iso2);
40106                 this.setDialCode(record.data.dialCode);
40107                 this.hasFocus = false;
40108                 this.collapse();
40109                 this.fireEvent('select', this, record, index);
40110             }
40111         },
40112         
40113         flagEl : function()
40114         {
40115             var flag = this.el.select('div.flag',true).first();
40116             if(!flag){
40117                 return false;
40118             }
40119             return flag;
40120         },
40121         
40122         dialCodeHolderEl : function()
40123         {
40124             var d = this.el.select('input.dial-code-holder',true).first();
40125             if(!d){
40126                 return false;
40127             }
40128             return d;
40129         },
40130         
40131         setDialCode : function(v)
40132         {
40133             this.dialCodeHolder.dom.value = '+'+v;
40134         },
40135         
40136         setFlagClass : function(n)
40137         {
40138             this.flag.dom.className = 'flag '+n;
40139         },
40140         
40141         getValue : function()
40142         {
40143             var v = this.inputEl().getValue();
40144             if(this.dialCodeHolder) {
40145                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40146             }
40147             return v;
40148         },
40149         
40150         setValue : function(v)
40151         {
40152             var d = this.getDialCode(v);
40153             
40154             //invalid dial code
40155             if(v.length == 0 || !d || d.length == 0) {
40156                 if(this.rendered){
40157                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40158                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40159                 }
40160                 return;
40161             }
40162             
40163             //valid dial code
40164             this.setFlagClass(this.dialCodeMapping[d].iso2);
40165             this.setDialCode(d);
40166             this.inputEl().dom.value = v.replace('+'+d,'');
40167             this.hiddenEl().dom.value = this.getValue();
40168             
40169             this.validate();
40170         },
40171         
40172         getDialCode : function(v)
40173         {
40174             v = v ||  '';
40175             
40176             if (v.length == 0) {
40177                 return this.dialCodeHolder.dom.value;
40178             }
40179             
40180             var dialCode = "";
40181             if (v.charAt(0) != "+") {
40182                 return false;
40183             }
40184             var numericChars = "";
40185             for (var i = 1; i < v.length; i++) {
40186               var c = v.charAt(i);
40187               if (!isNaN(c)) {
40188                 numericChars += c;
40189                 if (this.dialCodeMapping[numericChars]) {
40190                   dialCode = v.substr(1, i);
40191                 }
40192                 if (numericChars.length == 4) {
40193                   break;
40194                 }
40195               }
40196             }
40197             return dialCode;
40198         },
40199         
40200         reset : function()
40201         {
40202             this.setValue(this.defaultDialCode);
40203             this.validate();
40204         },
40205         
40206         hiddenEl : function()
40207         {
40208             return this.el.select('input.hidden-tel-input',true).first();
40209         },
40210         
40211         onKeyUp : function(e){
40212             
40213             var k = e.getKey();
40214             var c = e.getCharCode();
40215             
40216             if(
40217                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40218                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40219             ){
40220                 e.stopEvent();
40221             }
40222             
40223             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40224             //     return;
40225             // }
40226             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40227                 e.stopEvent();
40228             }
40229             
40230             this.setValue(this.getValue());
40231         }
40232         
40233 });
40234 /**
40235  * @class Roo.bootstrap.MoneyField
40236  * @extends Roo.bootstrap.ComboBox
40237  * Bootstrap MoneyField class
40238  * 
40239  * @constructor
40240  * Create a new MoneyField.
40241  * @param {Object} config Configuration options
40242  */
40243
40244 Roo.bootstrap.MoneyField = function(config) {
40245     
40246     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40247     
40248 };
40249
40250 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40251     
40252     /**
40253      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40254      */
40255     allowDecimals : true,
40256     /**
40257      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40258      */
40259     decimalSeparator : ".",
40260     /**
40261      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40262      */
40263     decimalPrecision : 0,
40264     /**
40265      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40266      */
40267     allowNegative : true,
40268     /**
40269      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40270      */
40271     allowZero: true,
40272     /**
40273      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40274      */
40275     minValue : Number.NEGATIVE_INFINITY,
40276     /**
40277      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40278      */
40279     maxValue : Number.MAX_VALUE,
40280     /**
40281      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40282      */
40283     minText : "The minimum value for this field is {0}",
40284     /**
40285      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40286      */
40287     maxText : "The maximum value for this field is {0}",
40288     /**
40289      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40290      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40291      */
40292     nanText : "{0} is not a valid number",
40293     /**
40294      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40295      */
40296     castInt : true,
40297     /**
40298      * @cfg {String} defaults currency of the MoneyField
40299      * value should be in lkey
40300      */
40301     defaultCurrency : false,
40302     /**
40303      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40304      */
40305     thousandsDelimiter : false,
40306     
40307     
40308     inputlg : 9,
40309     inputmd : 9,
40310     inputsm : 9,
40311     inputxs : 6,
40312     
40313     store : false,
40314     
40315     getAutoCreate : function()
40316     {
40317         var align = this.labelAlign || this.parentLabelAlign();
40318         
40319         var id = Roo.id();
40320
40321         var cfg = {
40322             cls: 'form-group',
40323             cn: []
40324         };
40325
40326         var input =  {
40327             tag: 'input',
40328             id : id,
40329             cls : 'form-control roo-money-amount-input',
40330             autocomplete: 'new-password'
40331         };
40332         
40333         var hiddenInput = {
40334             tag: 'input',
40335             type: 'hidden',
40336             id: Roo.id(),
40337             cls: 'hidden-number-input'
40338         };
40339         
40340         if (this.name) {
40341             hiddenInput.name = this.name;
40342         }
40343
40344         if (this.disabled) {
40345             input.disabled = true;
40346         }
40347
40348         var clg = 12 - this.inputlg;
40349         var cmd = 12 - this.inputmd;
40350         var csm = 12 - this.inputsm;
40351         var cxs = 12 - this.inputxs;
40352         
40353         var container = {
40354             tag : 'div',
40355             cls : 'row roo-money-field',
40356             cn : [
40357                 {
40358                     tag : 'div',
40359                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40360                     cn : [
40361                         {
40362                             tag : 'div',
40363                             cls: 'roo-select2-container input-group',
40364                             cn: [
40365                                 {
40366                                     tag : 'input',
40367                                     cls : 'form-control roo-money-currency-input',
40368                                     autocomplete: 'new-password',
40369                                     readOnly : 1,
40370                                     name : this.currencyName
40371                                 },
40372                                 {
40373                                     tag :'span',
40374                                     cls : 'input-group-addon',
40375                                     cn : [
40376                                         {
40377                                             tag: 'span',
40378                                             cls: 'caret'
40379                                         }
40380                                     ]
40381                                 }
40382                             ]
40383                         }
40384                     ]
40385                 },
40386                 {
40387                     tag : 'div',
40388                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40389                     cn : [
40390                         {
40391                             tag: 'div',
40392                             cls: this.hasFeedback ? 'has-feedback' : '',
40393                             cn: [
40394                                 input
40395                             ]
40396                         }
40397                     ]
40398                 }
40399             ]
40400             
40401         };
40402         
40403         if (this.fieldLabel.length) {
40404             var indicator = {
40405                 tag: 'i',
40406                 tooltip: 'This field is required'
40407             };
40408
40409             var label = {
40410                 tag: 'label',
40411                 'for':  id,
40412                 cls: 'control-label',
40413                 cn: []
40414             };
40415
40416             var label_text = {
40417                 tag: 'span',
40418                 html: this.fieldLabel
40419             };
40420
40421             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40422             label.cn = [
40423                 indicator,
40424                 label_text
40425             ];
40426
40427             if(this.indicatorpos == 'right') {
40428                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40429                 label.cn = [
40430                     label_text,
40431                     indicator
40432                 ];
40433             }
40434
40435             if(align == 'left') {
40436                 container = {
40437                     tag: 'div',
40438                     cn: [
40439                         container
40440                     ]
40441                 };
40442
40443                 if(this.labelWidth > 12){
40444                     label.style = "width: " + this.labelWidth + 'px';
40445                 }
40446                 if(this.labelWidth < 13 && this.labelmd == 0){
40447                     this.labelmd = this.labelWidth;
40448                 }
40449                 if(this.labellg > 0){
40450                     label.cls += ' col-lg-' + this.labellg;
40451                     input.cls += ' col-lg-' + (12 - this.labellg);
40452                 }
40453                 if(this.labelmd > 0){
40454                     label.cls += ' col-md-' + this.labelmd;
40455                     container.cls += ' col-md-' + (12 - this.labelmd);
40456                 }
40457                 if(this.labelsm > 0){
40458                     label.cls += ' col-sm-' + this.labelsm;
40459                     container.cls += ' col-sm-' + (12 - this.labelsm);
40460                 }
40461                 if(this.labelxs > 0){
40462                     label.cls += ' col-xs-' + this.labelxs;
40463                     container.cls += ' col-xs-' + (12 - this.labelxs);
40464                 }
40465             }
40466         }
40467
40468         cfg.cn = [
40469             label,
40470             container,
40471             hiddenInput
40472         ];
40473         
40474         var settings = this;
40475
40476         ['xs','sm','md','lg'].map(function(size){
40477             if (settings[size]) {
40478                 cfg.cls += ' col-' + size + '-' + settings[size];
40479             }
40480         });
40481         
40482         return cfg;
40483     },
40484     
40485     initEvents : function()
40486     {
40487         this.indicator = this.indicatorEl();
40488         
40489         this.initCurrencyEvent();
40490         
40491         this.initNumberEvent();
40492     },
40493     
40494     initCurrencyEvent : function()
40495     {
40496         if (!this.store) {
40497             throw "can not find store for combo";
40498         }
40499         
40500         this.store = Roo.factory(this.store, Roo.data);
40501         this.store.parent = this;
40502         
40503         this.createList();
40504         
40505         this.triggerEl = this.el.select('.input-group-addon', true).first();
40506         
40507         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40508         
40509         var _this = this;
40510         
40511         (function(){
40512             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40513             _this.list.setWidth(lw);
40514         }).defer(100);
40515         
40516         this.list.on('mouseover', this.onViewOver, this);
40517         this.list.on('mousemove', this.onViewMove, this);
40518         this.list.on('scroll', this.onViewScroll, this);
40519         
40520         if(!this.tpl){
40521             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40522         }
40523         
40524         this.view = new Roo.View(this.list, this.tpl, {
40525             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40526         });
40527         
40528         this.view.on('click', this.onViewClick, this);
40529         
40530         this.store.on('beforeload', this.onBeforeLoad, this);
40531         this.store.on('load', this.onLoad, this);
40532         this.store.on('loadexception', this.onLoadException, this);
40533         
40534         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40535             "up" : function(e){
40536                 this.inKeyMode = true;
40537                 this.selectPrev();
40538             },
40539
40540             "down" : function(e){
40541                 if(!this.isExpanded()){
40542                     this.onTriggerClick();
40543                 }else{
40544                     this.inKeyMode = true;
40545                     this.selectNext();
40546                 }
40547             },
40548
40549             "enter" : function(e){
40550                 this.collapse();
40551                 
40552                 if(this.fireEvent("specialkey", this, e)){
40553                     this.onViewClick(false);
40554                 }
40555                 
40556                 return true;
40557             },
40558
40559             "esc" : function(e){
40560                 this.collapse();
40561             },
40562
40563             "tab" : function(e){
40564                 this.collapse();
40565                 
40566                 if(this.fireEvent("specialkey", this, e)){
40567                     this.onViewClick(false);
40568                 }
40569                 
40570                 return true;
40571             },
40572
40573             scope : this,
40574
40575             doRelay : function(foo, bar, hname){
40576                 if(hname == 'down' || this.scope.isExpanded()){
40577                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40578                 }
40579                 return true;
40580             },
40581
40582             forceKeyDown: true
40583         });
40584         
40585         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40586         
40587     },
40588     
40589     initNumberEvent : function(e)
40590     {
40591         this.inputEl().on("keydown" , this.fireKey,  this);
40592         this.inputEl().on("focus", this.onFocus,  this);
40593         this.inputEl().on("blur", this.onBlur,  this);
40594         
40595         this.inputEl().relayEvent('keyup', this);
40596         
40597         if(this.indicator){
40598             this.indicator.addClass('invisible');
40599         }
40600  
40601         this.originalValue = this.getValue();
40602         
40603         if(this.validationEvent == 'keyup'){
40604             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40605             this.inputEl().on('keyup', this.filterValidation, this);
40606         }
40607         else if(this.validationEvent !== false){
40608             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40609         }
40610         
40611         if(this.selectOnFocus){
40612             this.on("focus", this.preFocus, this);
40613             
40614         }
40615         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40616             this.inputEl().on("keypress", this.filterKeys, this);
40617         } else {
40618             this.inputEl().relayEvent('keypress', this);
40619         }
40620         
40621         var allowed = "0123456789";
40622         
40623         if(this.allowDecimals){
40624             allowed += this.decimalSeparator;
40625         }
40626         
40627         if(this.allowNegative){
40628             allowed += "-";
40629         }
40630         
40631         if(this.thousandsDelimiter) {
40632             allowed += ",";
40633         }
40634         
40635         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40636         
40637         var keyPress = function(e){
40638             
40639             var k = e.getKey();
40640             
40641             var c = e.getCharCode();
40642             
40643             if(
40644                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40645                     allowed.indexOf(String.fromCharCode(c)) === -1
40646             ){
40647                 e.stopEvent();
40648                 return;
40649             }
40650             
40651             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40652                 return;
40653             }
40654             
40655             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40656                 e.stopEvent();
40657             }
40658         };
40659         
40660         this.inputEl().on("keypress", keyPress, this);
40661         
40662     },
40663     
40664     onTriggerClick : function(e)
40665     {   
40666         if(this.disabled){
40667             return;
40668         }
40669         
40670         this.page = 0;
40671         this.loadNext = false;
40672         
40673         if(this.isExpanded()){
40674             this.collapse();
40675             return;
40676         }
40677         
40678         this.hasFocus = true;
40679         
40680         if(this.triggerAction == 'all') {
40681             this.doQuery(this.allQuery, true);
40682             return;
40683         }
40684         
40685         this.doQuery(this.getRawValue());
40686     },
40687     
40688     getCurrency : function()
40689     {   
40690         var v = this.currencyEl().getValue();
40691         
40692         return v;
40693     },
40694     
40695     restrictHeight : function()
40696     {
40697         this.list.alignTo(this.currencyEl(), this.listAlign);
40698         this.list.alignTo(this.currencyEl(), this.listAlign);
40699     },
40700     
40701     onViewClick : function(view, doFocus, el, e)
40702     {
40703         var index = this.view.getSelectedIndexes()[0];
40704         
40705         var r = this.store.getAt(index);
40706         
40707         if(r){
40708             this.onSelect(r, index);
40709         }
40710     },
40711     
40712     onSelect : function(record, index){
40713         
40714         if(this.fireEvent('beforeselect', this, record, index) !== false){
40715         
40716             this.setFromCurrencyData(index > -1 ? record.data : false);
40717             
40718             this.collapse();
40719             
40720             this.fireEvent('select', this, record, index);
40721         }
40722     },
40723     
40724     setFromCurrencyData : function(o)
40725     {
40726         var currency = '';
40727         
40728         this.lastCurrency = o;
40729         
40730         if (this.currencyField) {
40731             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40732         } else {
40733             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40734         }
40735         
40736         this.lastSelectionText = currency;
40737         
40738         //setting default currency
40739         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40740             this.setCurrency(this.defaultCurrency);
40741             return;
40742         }
40743         
40744         this.setCurrency(currency);
40745     },
40746     
40747     setFromData : function(o)
40748     {
40749         var c = {};
40750         
40751         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40752         
40753         this.setFromCurrencyData(c);
40754         
40755         var value = '';
40756         
40757         if (this.name) {
40758             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40759         } else {
40760             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40761         }
40762         
40763         this.setValue(value);
40764         
40765     },
40766     
40767     setCurrency : function(v)
40768     {   
40769         this.currencyValue = v;
40770         
40771         if(this.rendered){
40772             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40773             this.validate();
40774         }
40775     },
40776     
40777     setValue : function(v)
40778     {
40779         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40780         
40781         this.value = v;
40782         
40783         if(this.rendered){
40784             
40785             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40786             
40787             this.inputEl().dom.value = (v == '') ? '' :
40788                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40789             
40790             if(!this.allowZero && v === '0') {
40791                 this.hiddenEl().dom.value = '';
40792                 this.inputEl().dom.value = '';
40793             }
40794             
40795             this.validate();
40796         }
40797     },
40798     
40799     getRawValue : function()
40800     {
40801         var v = this.inputEl().getValue();
40802         
40803         return v;
40804     },
40805     
40806     getValue : function()
40807     {
40808         return this.fixPrecision(this.parseValue(this.getRawValue()));
40809     },
40810     
40811     parseValue : function(value)
40812     {
40813         if(this.thousandsDelimiter) {
40814             value += "";
40815             r = new RegExp(",", "g");
40816             value = value.replace(r, "");
40817         }
40818         
40819         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40820         return isNaN(value) ? '' : value;
40821         
40822     },
40823     
40824     fixPrecision : function(value)
40825     {
40826         if(this.thousandsDelimiter) {
40827             value += "";
40828             r = new RegExp(",", "g");
40829             value = value.replace(r, "");
40830         }
40831         
40832         var nan = isNaN(value);
40833         
40834         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40835             return nan ? '' : value;
40836         }
40837         return parseFloat(value).toFixed(this.decimalPrecision);
40838     },
40839     
40840     decimalPrecisionFcn : function(v)
40841     {
40842         return Math.floor(v);
40843     },
40844     
40845     validateValue : function(value)
40846     {
40847         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40848             return false;
40849         }
40850         
40851         var num = this.parseValue(value);
40852         
40853         if(isNaN(num)){
40854             this.markInvalid(String.format(this.nanText, value));
40855             return false;
40856         }
40857         
40858         if(num < this.minValue){
40859             this.markInvalid(String.format(this.minText, this.minValue));
40860             return false;
40861         }
40862         
40863         if(num > this.maxValue){
40864             this.markInvalid(String.format(this.maxText, this.maxValue));
40865             return false;
40866         }
40867         
40868         return true;
40869     },
40870     
40871     validate : function()
40872     {
40873         if(this.disabled || this.allowBlank){
40874             this.markValid();
40875             return true;
40876         }
40877         
40878         var currency = this.getCurrency();
40879         
40880         if(this.validateValue(this.getRawValue()) && currency.length){
40881             this.markValid();
40882             return true;
40883         }
40884         
40885         this.markInvalid();
40886         return false;
40887     },
40888     
40889     getName: function()
40890     {
40891         return this.name;
40892     },
40893     
40894     beforeBlur : function()
40895     {
40896         if(!this.castInt){
40897             return;
40898         }
40899         
40900         var v = this.parseValue(this.getRawValue());
40901         
40902         if(v || v == 0){
40903             this.setValue(v);
40904         }
40905     },
40906     
40907     onBlur : function()
40908     {
40909         this.beforeBlur();
40910         
40911         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40912             //this.el.removeClass(this.focusClass);
40913         }
40914         
40915         this.hasFocus = false;
40916         
40917         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40918             this.validate();
40919         }
40920         
40921         var v = this.getValue();
40922         
40923         if(String(v) !== String(this.startValue)){
40924             this.fireEvent('change', this, v, this.startValue);
40925         }
40926         
40927         this.fireEvent("blur", this);
40928     },
40929     
40930     inputEl : function()
40931     {
40932         return this.el.select('.roo-money-amount-input', true).first();
40933     },
40934     
40935     currencyEl : function()
40936     {
40937         return this.el.select('.roo-money-currency-input', true).first();
40938     },
40939     
40940     hiddenEl : function()
40941     {
40942         return this.el.select('input.hidden-number-input',true).first();
40943     }
40944     
40945 });