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 
564  * @cfg {Boolean} inverse 
565  * @cfg {Boolean} toggle 
566  * @cfg {String} ontext text for on toggle state
567  * @cfg {String} offtext text for off toggle state
568  * @cfg {Boolean} defaulton 
569  * @cfg {Boolean} preventDefault  default true
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} this
595          * @param {Roo.EventObject} e
596          */
597         "click" : true,
598          /**
599          * @event toggle
600          * After the button has been toggles
601          * @param {Roo.EventObject} e
602          * @param {boolean} pressed (also available as button.pressed)
603          */
604         "toggle" : true
605     });
606 };
607
608 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
609     html: false,
610     active: false,
611     weight: '',
612     size: '',
613     tag: 'button',
614     href: '',
615     disabled: false,
616     isClose: false,
617     glyphicon: '',
618     badge: '',
619     theme: 'default',
620     inverse: false,
621     
622     toggle: false,
623     ontext: 'ON',
624     offtext: 'OFF',
625     defaulton: true,
626     preventDefault: true,
627     removeClass: false,
628     name: false,
629     target: false,
630     
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) {
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' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
797         }
798         
799         if (cfg.tag !== 'a' && this.href !== '') {
800             throw "Tag must be a to set href.";
801         } else if (this.href.length > 0) {
802             cfg.href = this.href;
803         }
804         
805         if(this.removeClass){
806             cfg.cls = '';
807         }
808         
809         if(this.target){
810             cfg.target = this.target;
811         }
812         
813         return cfg;
814     },
815     initEvents: function() {
816        // Roo.log('init events?');
817 //        Roo.log(this.el.dom);
818         // add the menu...
819         
820         if (typeof (this.menu) != 'undefined') {
821             this.menu.parentType = this.xtype;
822             this.menu.triggerEl = this.el;
823             this.addxtype(Roo.apply({}, this.menu));
824         }
825
826
827        if (this.el.hasClass('roo-button')) {
828             this.el.on('click', this.onClick, this);
829        } else {
830             this.el.select('.roo-button').on('click', this.onClick, this);
831        }
832        
833        if(this.removeClass){
834            this.el.on('click', this.onClick, this);
835        }
836        
837        this.el.enableDisplayMode();
838         
839     },
840     onClick : function(e)
841     {
842         if (this.disabled) {
843             return;
844         }
845         
846         
847         Roo.log('button on click ');
848         if(this.preventDefault){
849             e.preventDefault();
850         }
851         if (this.pressed === true || this.pressed === false) {
852             this.pressed = !this.pressed;
853             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
854             this.fireEvent('toggle', this, e, this.pressed);
855         }
856         
857         
858         this.fireEvent('click', this, e);
859     },
860     
861     /**
862      * Enables this button
863      */
864     enable : function()
865     {
866         this.disabled = false;
867         this.el.removeClass('disabled');
868     },
869     
870     /**
871      * Disable this button
872      */
873     disable : function()
874     {
875         this.disabled = true;
876         this.el.addClass('disabled');
877     },
878      /**
879      * sets the active state on/off, 
880      * @param {Boolean} state (optional) Force a particular state
881      */
882     setActive : function(v) {
883         
884         this.el[v ? 'addClass' : 'removeClass']('active');
885     },
886      /**
887      * toggles the current active state 
888      */
889     toggleActive : function()
890     {
891        var active = this.el.hasClass('active');
892        this.setActive(!active);
893        
894         
895     },
896     setText : function(str)
897     {
898         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
899     },
900     getText : function()
901     {
902         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
903     },
904     hide: function() {
905        
906      
907         this.el.hide();   
908     },
909     show: function() {
910        
911         this.el.show();   
912     },
913     setWeight : function(str)
914     {
915           this.el.removeClass(this.weightClass);
916         this.el.addClass('btn-' + str);        
917     }
918     
919     
920 });
921
922  /*
923  * - LGPL
924  *
925  * column
926  * 
927  */
928
929 /**
930  * @class Roo.bootstrap.Column
931  * @extends Roo.bootstrap.Component
932  * Bootstrap Column class
933  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
934  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
935  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
936  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
937  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
938  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
939  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
940  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
941  *
942  * 
943  * @cfg {Boolean} hidden (true|false) hide the element
944  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
945  * @cfg {String} fa (ban|check|...) font awesome icon
946  * @cfg {Number} fasize (1|2|....) font awsome size
947
948  * @cfg {String} icon (info-sign|check|...) glyphicon name
949
950  * @cfg {String} html content of column.
951  * 
952  * @constructor
953  * Create a new Column
954  * @param {Object} config The config object
955  */
956
957 Roo.bootstrap.Column = function(config){
958     Roo.bootstrap.Column.superclass.constructor.call(this, config);
959 };
960
961 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
962     
963     xs: false,
964     sm: false,
965     md: false,
966     lg: false,
967     xsoff: false,
968     smoff: false,
969     mdoff: false,
970     lgoff: false,
971     html: '',
972     offset: 0,
973     alert: false,
974     fa: false,
975     icon : false,
976     hidden : false,
977     fasize : 1,
978     
979     getAutoCreate : function(){
980         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
981         
982         cfg = {
983             tag: 'div',
984             cls: 'column'
985         };
986         
987         var settings=this;
988         ['xs','sm','md','lg'].map(function(size){
989             //Roo.log( size + ':' + settings[size]);
990             
991             if (settings[size+'off'] !== false) {
992                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
993             }
994             
995             if (settings[size] === false) {
996                 return;
997             }
998             
999             if (!settings[size]) { // 0 = hidden
1000                 cfg.cls += ' hidden-' + size;
1001                 return;
1002             }
1003             cfg.cls += ' col-' + size + '-' + settings[size];
1004             
1005         });
1006         
1007         if (this.hidden) {
1008             cfg.cls += ' hidden';
1009         }
1010         
1011         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1012             cfg.cls +=' alert alert-' + this.alert;
1013         }
1014         
1015         
1016         if (this.html.length) {
1017             cfg.html = this.html;
1018         }
1019         if (this.fa) {
1020             var fasize = '';
1021             if (this.fasize > 1) {
1022                 fasize = ' fa-' + this.fasize + 'x';
1023             }
1024             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1025             
1026             
1027         }
1028         if (this.icon) {
1029             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1030         }
1031         
1032         return cfg;
1033     }
1034    
1035 });
1036
1037  
1038
1039  /*
1040  * - LGPL
1041  *
1042  * page container.
1043  * 
1044  */
1045
1046
1047 /**
1048  * @class Roo.bootstrap.Container
1049  * @extends Roo.bootstrap.Component
1050  * Bootstrap Container class
1051  * @cfg {Boolean} jumbotron is it a jumbotron element
1052  * @cfg {String} html content of element
1053  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1054  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1055  * @cfg {String} header content of header (for panel)
1056  * @cfg {String} footer content of footer (for panel)
1057  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1058  * @cfg {String} tag (header|aside|section) type of HTML tag.
1059  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1060  * @cfg {String} fa font awesome icon
1061  * @cfg {String} icon (info-sign|check|...) glyphicon name
1062  * @cfg {Boolean} hidden (true|false) hide the element
1063  * @cfg {Boolean} expandable (true|false) default false
1064  * @cfg {Boolean} expanded (true|false) default true
1065  * @cfg {String} rheader contet on the right of header
1066  * @cfg {Boolean} clickable (true|false) default false
1067
1068  *     
1069  * @constructor
1070  * Create a new Container
1071  * @param {Object} config The config object
1072  */
1073
1074 Roo.bootstrap.Container = function(config){
1075     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1076     
1077     this.addEvents({
1078         // raw events
1079          /**
1080          * @event expand
1081          * After the panel has been expand
1082          * 
1083          * @param {Roo.bootstrap.Container} this
1084          */
1085         "expand" : true,
1086         /**
1087          * @event collapse
1088          * After the panel has been collapsed
1089          * 
1090          * @param {Roo.bootstrap.Container} this
1091          */
1092         "collapse" : true,
1093         /**
1094          * @event click
1095          * When a element is chick
1096          * @param {Roo.bootstrap.Container} this
1097          * @param {Roo.EventObject} e
1098          */
1099         "click" : true
1100     });
1101 };
1102
1103 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1104     
1105     jumbotron : false,
1106     well: '',
1107     panel : '',
1108     header: '',
1109     footer : '',
1110     sticky: '',
1111     tag : false,
1112     alert : false,
1113     fa: false,
1114     icon : false,
1115     expandable : false,
1116     rheader : '',
1117     expanded : true,
1118     clickable: false,
1119   
1120      
1121     getChildContainer : function() {
1122         
1123         if(!this.el){
1124             return false;
1125         }
1126         
1127         if (this.panel.length) {
1128             return this.el.select('.panel-body',true).first();
1129         }
1130         
1131         return this.el;
1132     },
1133     
1134     
1135     getAutoCreate : function(){
1136         
1137         var cfg = {
1138             tag : this.tag || 'div',
1139             html : '',
1140             cls : ''
1141         };
1142         if (this.jumbotron) {
1143             cfg.cls = 'jumbotron';
1144         }
1145         
1146         
1147         
1148         // - this is applied by the parent..
1149         //if (this.cls) {
1150         //    cfg.cls = this.cls + '';
1151         //}
1152         
1153         if (this.sticky.length) {
1154             
1155             var bd = Roo.get(document.body);
1156             if (!bd.hasClass('bootstrap-sticky')) {
1157                 bd.addClass('bootstrap-sticky');
1158                 Roo.select('html',true).setStyle('height', '100%');
1159             }
1160              
1161             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1162         }
1163         
1164         
1165         if (this.well.length) {
1166             switch (this.well) {
1167                 case 'lg':
1168                 case 'sm':
1169                     cfg.cls +=' well well-' +this.well;
1170                     break;
1171                 default:
1172                     cfg.cls +=' well';
1173                     break;
1174             }
1175         }
1176         
1177         if (this.hidden) {
1178             cfg.cls += ' hidden';
1179         }
1180         
1181         
1182         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1183             cfg.cls +=' alert alert-' + this.alert;
1184         }
1185         
1186         var body = cfg;
1187         
1188         if (this.panel.length) {
1189             cfg.cls += ' panel panel-' + this.panel;
1190             cfg.cn = [];
1191             if (this.header.length) {
1192                 
1193                 var h = [];
1194                 
1195                 if(this.expandable){
1196                     
1197                     cfg.cls = cfg.cls + ' expandable';
1198                     
1199                     h.push({
1200                         tag: 'i',
1201                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1202                     });
1203                     
1204                 }
1205                 
1206                 h.push(
1207                     {
1208                         tag: 'span',
1209                         cls : 'panel-title',
1210                         html : (this.expandable ? '&nbsp;' : '') + this.header
1211                     },
1212                     {
1213                         tag: 'span',
1214                         cls: 'panel-header-right',
1215                         html: this.rheader
1216                     }
1217                 );
1218                 
1219                 cfg.cn.push({
1220                     cls : 'panel-heading',
1221                     style : this.expandable ? 'cursor: pointer' : '',
1222                     cn : h
1223                 });
1224                 
1225             }
1226             
1227             body = false;
1228             cfg.cn.push({
1229                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1230                 html : this.html
1231             });
1232             
1233             
1234             if (this.footer.length) {
1235                 cfg.cn.push({
1236                     cls : 'panel-footer',
1237                     html : this.footer
1238                     
1239                 });
1240             }
1241             
1242         }
1243         
1244         if (body) {
1245             body.html = this.html || cfg.html;
1246             // prefix with the icons..
1247             if (this.fa) {
1248                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1249             }
1250             if (this.icon) {
1251                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1252             }
1253             
1254             
1255         }
1256         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1257             cfg.cls =  'container';
1258         }
1259         
1260         return cfg;
1261     },
1262     
1263     initEvents: function() 
1264     {
1265         if(this.expandable){
1266             var headerEl = this.headerEl();
1267         
1268             if(headerEl){
1269                 headerEl.on('click', this.onToggleClick, this);
1270             }
1271         }
1272         
1273         if(this.clickable){
1274             this.el.on('click', this.onClick, this);
1275         }
1276         
1277     },
1278     
1279     onToggleClick : function()
1280     {
1281         var headerEl = this.headerEl();
1282         
1283         if(!headerEl){
1284             return;
1285         }
1286         
1287         if(this.expanded){
1288             this.collapse();
1289             return;
1290         }
1291         
1292         this.expand();
1293     },
1294     
1295     expand : function()
1296     {
1297         if(this.fireEvent('expand', this)) {
1298             
1299             this.expanded = true;
1300             
1301             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1302             
1303             this.el.select('.panel-body',true).first().removeClass('hide');
1304             
1305             var toggleEl = this.toggleEl();
1306
1307             if(!toggleEl){
1308                 return;
1309             }
1310
1311             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1312         }
1313         
1314     },
1315     
1316     collapse : function()
1317     {
1318         if(this.fireEvent('collapse', this)) {
1319             
1320             this.expanded = false;
1321             
1322             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1323             this.el.select('.panel-body',true).first().addClass('hide');
1324         
1325             var toggleEl = this.toggleEl();
1326
1327             if(!toggleEl){
1328                 return;
1329             }
1330
1331             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1332         }
1333     },
1334     
1335     toggleEl : function()
1336     {
1337         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1338             return;
1339         }
1340         
1341         return this.el.select('.panel-heading .fa',true).first();
1342     },
1343     
1344     headerEl : function()
1345     {
1346         if(!this.el || !this.panel.length || !this.header.length){
1347             return;
1348         }
1349         
1350         return this.el.select('.panel-heading',true).first()
1351     },
1352     
1353     bodyEl : function()
1354     {
1355         if(!this.el || !this.panel.length){
1356             return;
1357         }
1358         
1359         return this.el.select('.panel-body',true).first()
1360     },
1361     
1362     titleEl : function()
1363     {
1364         if(!this.el || !this.panel.length || !this.header.length){
1365             return;
1366         }
1367         
1368         return this.el.select('.panel-title',true).first();
1369     },
1370     
1371     setTitle : function(v)
1372     {
1373         var titleEl = this.titleEl();
1374         
1375         if(!titleEl){
1376             return;
1377         }
1378         
1379         titleEl.dom.innerHTML = v;
1380     },
1381     
1382     getTitle : function()
1383     {
1384         
1385         var titleEl = this.titleEl();
1386         
1387         if(!titleEl){
1388             return '';
1389         }
1390         
1391         return titleEl.dom.innerHTML;
1392     },
1393     
1394     setRightTitle : function(v)
1395     {
1396         var t = this.el.select('.panel-header-right',true).first();
1397         
1398         if(!t){
1399             return;
1400         }
1401         
1402         t.dom.innerHTML = v;
1403     },
1404     
1405     onClick : function(e)
1406     {
1407         e.preventDefault();
1408         
1409         this.fireEvent('click', this, e);
1410     },
1411     
1412     allChildren : function()
1413     {
1414         var r=new Roo.util.MixedCollection(false, function(o){
1415             return o.id || (o.id = Roo.id());
1416         });
1417         var iter = function(el) {
1418             if (el.inputEl) {
1419                 r.add(el);
1420             }
1421             if (!el.items) {
1422                 return;
1423             }
1424             Roo.each(el.items,function(e) {
1425                 iter(e);
1426             });
1427         };
1428
1429         iter(this);
1430         return r;
1431     },
1432     
1433     checkEmpty : function()
1434     {
1435         var items = this.allChildren();
1436         var isEmpty = true;
1437         
1438         items.each(function(f){
1439             if(f.el.isVisible()) {
1440                 isEmpty = false;
1441             }
1442         });
1443         
1444         return isEmpty;
1445     }
1446    
1447 });
1448
1449  /*
1450  * - LGPL
1451  *
1452  * image
1453  * 
1454  */
1455
1456
1457 /**
1458  * @class Roo.bootstrap.Img
1459  * @extends Roo.bootstrap.Component
1460  * Bootstrap Img class
1461  * @cfg {Boolean} imgResponsive false | true
1462  * @cfg {String} border rounded | circle | thumbnail
1463  * @cfg {String} src image source
1464  * @cfg {String} alt image alternative text
1465  * @cfg {String} href a tag href
1466  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1467  * @cfg {String} xsUrl xs image source
1468  * @cfg {String} smUrl sm image source
1469  * @cfg {String} mdUrl md image source
1470  * @cfg {String} lgUrl lg image source
1471  * 
1472  * @constructor
1473  * Create a new Input
1474  * @param {Object} config The config object
1475  */
1476
1477 Roo.bootstrap.Img = function(config){
1478     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1479     
1480     this.addEvents({
1481         // img events
1482         /**
1483          * @event click
1484          * The img click event for the img.
1485          * @param {Roo.EventObject} e
1486          */
1487         "click" : true
1488     });
1489 };
1490
1491 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1492     
1493     imgResponsive: true,
1494     border: '',
1495     src: 'about:blank',
1496     href: false,
1497     target: false,
1498     xsUrl: '',
1499     smUrl: '',
1500     mdUrl: '',
1501     lgUrl: '',
1502
1503     getAutoCreate : function()
1504     {   
1505         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1506             return this.createSingleImg();
1507         }
1508         
1509         var cfg = {
1510             tag: 'div',
1511             cls: 'roo-image-responsive-group',
1512             cn: []
1513         };
1514         var _this = this;
1515         
1516         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1517             
1518             if(!_this[size + 'Url']){
1519                 return;
1520             }
1521             
1522             var img = {
1523                 tag: 'img',
1524                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1525                 html: _this.html || cfg.html,
1526                 src: _this[size + 'Url']
1527             };
1528             
1529             img.cls += ' roo-image-responsive-' + size;
1530             
1531             var s = ['xs', 'sm', 'md', 'lg'];
1532             
1533             s.splice(s.indexOf(size), 1);
1534             
1535             Roo.each(s, function(ss){
1536                 img.cls += ' hidden-' + ss;
1537             });
1538             
1539             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1540                 cfg.cls += ' img-' + _this.border;
1541             }
1542             
1543             if(_this.alt){
1544                 cfg.alt = _this.alt;
1545             }
1546             
1547             if(_this.href){
1548                 var a = {
1549                     tag: 'a',
1550                     href: _this.href,
1551                     cn: [
1552                         img
1553                     ]
1554                 };
1555
1556                 if(this.target){
1557                     a.target = _this.target;
1558                 }
1559             }
1560             
1561             cfg.cn.push((_this.href) ? a : img);
1562             
1563         });
1564         
1565         return cfg;
1566     },
1567     
1568     createSingleImg : function()
1569     {
1570         var cfg = {
1571             tag: 'img',
1572             cls: (this.imgResponsive) ? 'img-responsive' : '',
1573             html : null,
1574             src : 'about:blank'  // just incase src get's set to undefined?!?
1575         };
1576         
1577         cfg.html = this.html || cfg.html;
1578         
1579         cfg.src = this.src || cfg.src;
1580         
1581         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1582             cfg.cls += ' img-' + this.border;
1583         }
1584         
1585         if(this.alt){
1586             cfg.alt = this.alt;
1587         }
1588         
1589         if(this.href){
1590             var a = {
1591                 tag: 'a',
1592                 href: this.href,
1593                 cn: [
1594                     cfg
1595                 ]
1596             };
1597             
1598             if(this.target){
1599                 a.target = this.target;
1600             }
1601             
1602         }
1603         
1604         return (this.href) ? a : cfg;
1605     },
1606     
1607     initEvents: function() 
1608     {
1609         if(!this.href){
1610             this.el.on('click', this.onClick, this);
1611         }
1612         
1613     },
1614     
1615     onClick : function(e)
1616     {
1617         Roo.log('img onclick');
1618         this.fireEvent('click', this, e);
1619     },
1620     /**
1621      * Sets the url of the image - used to update it
1622      * @param {String} url the url of the image
1623      */
1624     
1625     setSrc : function(url)
1626     {
1627         this.src =  url;
1628         
1629         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1630             this.el.dom.src =  url;
1631             return;
1632         }
1633         
1634         this.el.select('img', true).first().dom.src =  url;
1635     }
1636     
1637     
1638    
1639 });
1640
1641  /*
1642  * - LGPL
1643  *
1644  * image
1645  * 
1646  */
1647
1648
1649 /**
1650  * @class Roo.bootstrap.Link
1651  * @extends Roo.bootstrap.Component
1652  * Bootstrap Link Class
1653  * @cfg {String} alt image alternative text
1654  * @cfg {String} href a tag href
1655  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1656  * @cfg {String} html the content of the link.
1657  * @cfg {String} anchor name for the anchor link
1658  * @cfg {String} fa - favicon
1659
1660  * @cfg {Boolean} preventDefault (true | false) default false
1661
1662  * 
1663  * @constructor
1664  * Create a new Input
1665  * @param {Object} config The config object
1666  */
1667
1668 Roo.bootstrap.Link = function(config){
1669     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1670     
1671     this.addEvents({
1672         // img events
1673         /**
1674          * @event click
1675          * The img click event for the img.
1676          * @param {Roo.EventObject} e
1677          */
1678         "click" : true
1679     });
1680 };
1681
1682 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1683     
1684     href: false,
1685     target: false,
1686     preventDefault: false,
1687     anchor : false,
1688     alt : false,
1689     fa: false,
1690
1691
1692     getAutoCreate : function()
1693     {
1694         var html = this.html || '';
1695         
1696         if (this.fa !== false) {
1697             html = '<i class="fa fa-' + this.fa + '"></i>';
1698         }
1699         var cfg = {
1700             tag: 'a'
1701         };
1702         // anchor's do not require html/href...
1703         if (this.anchor === false) {
1704             cfg.html = html;
1705             cfg.href = this.href || '#';
1706         } else {
1707             cfg.name = this.anchor;
1708             if (this.html !== false || this.fa !== false) {
1709                 cfg.html = html;
1710             }
1711             if (this.href !== false) {
1712                 cfg.href = this.href;
1713             }
1714         }
1715         
1716         if(this.alt !== false){
1717             cfg.alt = this.alt;
1718         }
1719         
1720         
1721         if(this.target !== false) {
1722             cfg.target = this.target;
1723         }
1724         
1725         return cfg;
1726     },
1727     
1728     initEvents: function() {
1729         
1730         if(!this.href || this.preventDefault){
1731             this.el.on('click', this.onClick, this);
1732         }
1733     },
1734     
1735     onClick : function(e)
1736     {
1737         if(this.preventDefault){
1738             e.preventDefault();
1739         }
1740         //Roo.log('img onclick');
1741         this.fireEvent('click', this, e);
1742     }
1743    
1744 });
1745
1746  /*
1747  * - LGPL
1748  *
1749  * header
1750  * 
1751  */
1752
1753 /**
1754  * @class Roo.bootstrap.Header
1755  * @extends Roo.bootstrap.Component
1756  * Bootstrap Header class
1757  * @cfg {String} html content of header
1758  * @cfg {Number} level (1|2|3|4|5|6) default 1
1759  * 
1760  * @constructor
1761  * Create a new Header
1762  * @param {Object} config The config object
1763  */
1764
1765
1766 Roo.bootstrap.Header  = function(config){
1767     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1768 };
1769
1770 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1771     
1772     //href : false,
1773     html : false,
1774     level : 1,
1775     
1776     
1777     
1778     getAutoCreate : function(){
1779         
1780         
1781         
1782         var cfg = {
1783             tag: 'h' + (1 *this.level),
1784             html: this.html || ''
1785         } ;
1786         
1787         return cfg;
1788     }
1789    
1790 });
1791
1792  
1793
1794  /*
1795  * Based on:
1796  * Ext JS Library 1.1.1
1797  * Copyright(c) 2006-2007, Ext JS, LLC.
1798  *
1799  * Originally Released Under LGPL - original licence link has changed is not relivant.
1800  *
1801  * Fork - LGPL
1802  * <script type="text/javascript">
1803  */
1804  
1805 /**
1806  * @class Roo.bootstrap.MenuMgr
1807  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1808  * @singleton
1809  */
1810 Roo.bootstrap.MenuMgr = function(){
1811    var menus, active, groups = {}, attached = false, lastShow = new Date();
1812
1813    // private - called when first menu is created
1814    function init(){
1815        menus = {};
1816        active = new Roo.util.MixedCollection();
1817        Roo.get(document).addKeyListener(27, function(){
1818            if(active.length > 0){
1819                hideAll();
1820            }
1821        });
1822    }
1823
1824    // private
1825    function hideAll(){
1826        if(active && active.length > 0){
1827            var c = active.clone();
1828            c.each(function(m){
1829                m.hide();
1830            });
1831        }
1832    }
1833
1834    // private
1835    function onHide(m){
1836        active.remove(m);
1837        if(active.length < 1){
1838            Roo.get(document).un("mouseup", onMouseDown);
1839             
1840            attached = false;
1841        }
1842    }
1843
1844    // private
1845    function onShow(m){
1846        var last = active.last();
1847        lastShow = new Date();
1848        active.add(m);
1849        if(!attached){
1850           Roo.get(document).on("mouseup", onMouseDown);
1851            
1852            attached = true;
1853        }
1854        if(m.parentMenu){
1855           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1856           m.parentMenu.activeChild = m;
1857        }else if(last && last.isVisible()){
1858           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1859        }
1860    }
1861
1862    // private
1863    function onBeforeHide(m){
1864        if(m.activeChild){
1865            m.activeChild.hide();
1866        }
1867        if(m.autoHideTimer){
1868            clearTimeout(m.autoHideTimer);
1869            delete m.autoHideTimer;
1870        }
1871    }
1872
1873    // private
1874    function onBeforeShow(m){
1875        var pm = m.parentMenu;
1876        if(!pm && !m.allowOtherMenus){
1877            hideAll();
1878        }else if(pm && pm.activeChild && active != m){
1879            pm.activeChild.hide();
1880        }
1881    }
1882
1883    // private this should really trigger on mouseup..
1884    function onMouseDown(e){
1885         Roo.log("on Mouse Up");
1886         
1887         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1888             Roo.log("MenuManager hideAll");
1889             hideAll();
1890             e.stopEvent();
1891         }
1892         
1893         
1894    }
1895
1896    // private
1897    function onBeforeCheck(mi, state){
1898        if(state){
1899            var g = groups[mi.group];
1900            for(var i = 0, l = g.length; i < l; i++){
1901                if(g[i] != mi){
1902                    g[i].setChecked(false);
1903                }
1904            }
1905        }
1906    }
1907
1908    return {
1909
1910        /**
1911         * Hides all menus that are currently visible
1912         */
1913        hideAll : function(){
1914             hideAll();  
1915        },
1916
1917        // private
1918        register : function(menu){
1919            if(!menus){
1920                init();
1921            }
1922            menus[menu.id] = menu;
1923            menu.on("beforehide", onBeforeHide);
1924            menu.on("hide", onHide);
1925            menu.on("beforeshow", onBeforeShow);
1926            menu.on("show", onShow);
1927            var g = menu.group;
1928            if(g && menu.events["checkchange"]){
1929                if(!groups[g]){
1930                    groups[g] = [];
1931                }
1932                groups[g].push(menu);
1933                menu.on("checkchange", onCheck);
1934            }
1935        },
1936
1937         /**
1938          * Returns a {@link Roo.menu.Menu} object
1939          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1940          * be used to generate and return a new Menu instance.
1941          */
1942        get : function(menu){
1943            if(typeof menu == "string"){ // menu id
1944                return menus[menu];
1945            }else if(menu.events){  // menu instance
1946                return menu;
1947            }
1948            /*else if(typeof menu.length == 'number'){ // array of menu items?
1949                return new Roo.bootstrap.Menu({items:menu});
1950            }else{ // otherwise, must be a config
1951                return new Roo.bootstrap.Menu(menu);
1952            }
1953            */
1954            return false;
1955        },
1956
1957        // private
1958        unregister : function(menu){
1959            delete menus[menu.id];
1960            menu.un("beforehide", onBeforeHide);
1961            menu.un("hide", onHide);
1962            menu.un("beforeshow", onBeforeShow);
1963            menu.un("show", onShow);
1964            var g = menu.group;
1965            if(g && menu.events["checkchange"]){
1966                groups[g].remove(menu);
1967                menu.un("checkchange", onCheck);
1968            }
1969        },
1970
1971        // private
1972        registerCheckable : function(menuItem){
1973            var g = menuItem.group;
1974            if(g){
1975                if(!groups[g]){
1976                    groups[g] = [];
1977                }
1978                groups[g].push(menuItem);
1979                menuItem.on("beforecheckchange", onBeforeCheck);
1980            }
1981        },
1982
1983        // private
1984        unregisterCheckable : function(menuItem){
1985            var g = menuItem.group;
1986            if(g){
1987                groups[g].remove(menuItem);
1988                menuItem.un("beforecheckchange", onBeforeCheck);
1989            }
1990        }
1991    };
1992 }();/*
1993  * - LGPL
1994  *
1995  * menu
1996  * 
1997  */
1998
1999 /**
2000  * @class Roo.bootstrap.Menu
2001  * @extends Roo.bootstrap.Component
2002  * Bootstrap Menu class - container for MenuItems
2003  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2004  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2005  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2006  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2007  * 
2008  * @constructor
2009  * Create a new Menu
2010  * @param {Object} config The config object
2011  */
2012
2013
2014 Roo.bootstrap.Menu = function(config){
2015     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2016     if (this.registerMenu && this.type != 'treeview')  {
2017         Roo.bootstrap.MenuMgr.register(this);
2018     }
2019     this.addEvents({
2020         /**
2021          * @event beforeshow
2022          * Fires before this menu is displayed
2023          * @param {Roo.menu.Menu} this
2024          */
2025         beforeshow : true,
2026         /**
2027          * @event beforehide
2028          * Fires before this menu is hidden
2029          * @param {Roo.menu.Menu} this
2030          */
2031         beforehide : true,
2032         /**
2033          * @event show
2034          * Fires after this menu is displayed
2035          * @param {Roo.menu.Menu} this
2036          */
2037         show : true,
2038         /**
2039          * @event hide
2040          * Fires after this menu is hidden
2041          * @param {Roo.menu.Menu} this
2042          */
2043         hide : true,
2044         /**
2045          * @event click
2046          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2047          * @param {Roo.menu.Menu} this
2048          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2049          * @param {Roo.EventObject} e
2050          */
2051         click : true,
2052         /**
2053          * @event mouseover
2054          * Fires when the mouse is hovering over this menu
2055          * @param {Roo.menu.Menu} this
2056          * @param {Roo.EventObject} e
2057          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2058          */
2059         mouseover : true,
2060         /**
2061          * @event mouseout
2062          * Fires when the mouse exits this menu
2063          * @param {Roo.menu.Menu} this
2064          * @param {Roo.EventObject} e
2065          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2066          */
2067         mouseout : true,
2068         /**
2069          * @event itemclick
2070          * Fires when a menu item contained in this menu is clicked
2071          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2072          * @param {Roo.EventObject} e
2073          */
2074         itemclick: true
2075     });
2076     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2077 };
2078
2079 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2080     
2081    /// html : false,
2082     //align : '',
2083     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2084     type: false,
2085     /**
2086      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2087      */
2088     registerMenu : true,
2089     
2090     menuItems :false, // stores the menu items..
2091     
2092     hidden:true,
2093         
2094     parentMenu : false,
2095     
2096     stopEvent : true,
2097     
2098     isLink : false,
2099     
2100     getChildContainer : function() {
2101         return this.el;  
2102     },
2103     
2104     getAutoCreate : function(){
2105          
2106         //if (['right'].indexOf(this.align)!==-1) {
2107         //    cfg.cn[1].cls += ' pull-right'
2108         //}
2109         
2110         
2111         var cfg = {
2112             tag : 'ul',
2113             cls : 'dropdown-menu' ,
2114             style : 'z-index:1000'
2115             
2116         };
2117         
2118         if (this.type === 'submenu') {
2119             cfg.cls = 'submenu active';
2120         }
2121         if (this.type === 'treeview') {
2122             cfg.cls = 'treeview-menu';
2123         }
2124         
2125         return cfg;
2126     },
2127     initEvents : function() {
2128         
2129        // Roo.log("ADD event");
2130        // Roo.log(this.triggerEl.dom);
2131         
2132         this.triggerEl.on('click', this.onTriggerClick, this);
2133         
2134         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2135         
2136         this.triggerEl.addClass('dropdown-toggle');
2137         
2138         if (Roo.isTouch) {
2139             this.el.on('touchstart'  , this.onTouch, this);
2140         }
2141         this.el.on('click' , this.onClick, this);
2142
2143         this.el.on("mouseover", this.onMouseOver, this);
2144         this.el.on("mouseout", this.onMouseOut, this);
2145         
2146     },
2147     
2148     findTargetItem : function(e)
2149     {
2150         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2151         if(!t){
2152             return false;
2153         }
2154         //Roo.log(t);         Roo.log(t.id);
2155         if(t && t.id){
2156             //Roo.log(this.menuitems);
2157             return this.menuitems.get(t.id);
2158             
2159             //return this.items.get(t.menuItemId);
2160         }
2161         
2162         return false;
2163     },
2164     
2165     onTouch : function(e) 
2166     {
2167         Roo.log("menu.onTouch");
2168         //e.stopEvent(); this make the user popdown broken
2169         this.onClick(e);
2170     },
2171     
2172     onClick : function(e)
2173     {
2174         Roo.log("menu.onClick");
2175         
2176         var t = this.findTargetItem(e);
2177         if(!t || t.isContainer){
2178             return;
2179         }
2180         Roo.log(e);
2181         /*
2182         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2183             if(t == this.activeItem && t.shouldDeactivate(e)){
2184                 this.activeItem.deactivate();
2185                 delete this.activeItem;
2186                 return;
2187             }
2188             if(t.canActivate){
2189                 this.setActiveItem(t, true);
2190             }
2191             return;
2192             
2193             
2194         }
2195         */
2196        
2197         Roo.log('pass click event');
2198         
2199         t.onClick(e);
2200         
2201         this.fireEvent("click", this, t, e);
2202         
2203         var _this = this;
2204         
2205         if(!t.href.length || t.href == '#'){
2206             (function() { _this.hide(); }).defer(100);
2207         }
2208         
2209     },
2210     
2211     onMouseOver : function(e){
2212         var t  = this.findTargetItem(e);
2213         //Roo.log(t);
2214         //if(t){
2215         //    if(t.canActivate && !t.disabled){
2216         //        this.setActiveItem(t, true);
2217         //    }
2218         //}
2219         
2220         this.fireEvent("mouseover", this, e, t);
2221     },
2222     isVisible : function(){
2223         return !this.hidden;
2224     },
2225      onMouseOut : function(e){
2226         var t  = this.findTargetItem(e);
2227         
2228         //if(t ){
2229         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2230         //        this.activeItem.deactivate();
2231         //        delete this.activeItem;
2232         //    }
2233         //}
2234         this.fireEvent("mouseout", this, e, t);
2235     },
2236     
2237     
2238     /**
2239      * Displays this menu relative to another element
2240      * @param {String/HTMLElement/Roo.Element} element The element to align to
2241      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2242      * the element (defaults to this.defaultAlign)
2243      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2244      */
2245     show : function(el, pos, parentMenu){
2246         this.parentMenu = parentMenu;
2247         if(!this.el){
2248             this.render();
2249         }
2250         this.fireEvent("beforeshow", this);
2251         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2252     },
2253      /**
2254      * Displays this menu at a specific xy position
2255      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2256      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2257      */
2258     showAt : function(xy, parentMenu, /* private: */_e){
2259         this.parentMenu = parentMenu;
2260         if(!this.el){
2261             this.render();
2262         }
2263         if(_e !== false){
2264             this.fireEvent("beforeshow", this);
2265             //xy = this.el.adjustForConstraints(xy);
2266         }
2267         
2268         //this.el.show();
2269         this.hideMenuItems();
2270         this.hidden = false;
2271         this.triggerEl.addClass('open');
2272         
2273         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2274             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2275         }
2276         
2277         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2278             this.el.setXY(xy);
2279         }
2280         
2281         this.focus();
2282         this.fireEvent("show", this);
2283     },
2284     
2285     focus : function(){
2286         return;
2287         if(!this.hidden){
2288             this.doFocus.defer(50, this);
2289         }
2290     },
2291
2292     doFocus : function(){
2293         if(!this.hidden){
2294             this.focusEl.focus();
2295         }
2296     },
2297
2298     /**
2299      * Hides this menu and optionally all parent menus
2300      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2301      */
2302     hide : function(deep)
2303     {
2304         
2305         this.hideMenuItems();
2306         if(this.el && this.isVisible()){
2307             this.fireEvent("beforehide", this);
2308             if(this.activeItem){
2309                 this.activeItem.deactivate();
2310                 this.activeItem = null;
2311             }
2312             this.triggerEl.removeClass('open');;
2313             this.hidden = true;
2314             this.fireEvent("hide", this);
2315         }
2316         if(deep === true && this.parentMenu){
2317             this.parentMenu.hide(true);
2318         }
2319     },
2320     
2321     onTriggerClick : function(e)
2322     {
2323         Roo.log('trigger click');
2324         
2325         var target = e.getTarget();
2326         
2327         Roo.log(target.nodeName.toLowerCase());
2328         
2329         if(target.nodeName.toLowerCase() === 'i'){
2330             e.preventDefault();
2331         }
2332         
2333     },
2334     
2335     onTriggerPress  : function(e)
2336     {
2337         Roo.log('trigger press');
2338         //Roo.log(e.getTarget());
2339        // Roo.log(this.triggerEl.dom);
2340        
2341         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2342         var pel = Roo.get(e.getTarget());
2343         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2344             Roo.log('is treeview or dropdown?');
2345             return;
2346         }
2347         
2348         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2349             return;
2350         }
2351         
2352         if (this.isVisible()) {
2353             Roo.log('hide');
2354             this.hide();
2355         } else {
2356             Roo.log('show');
2357             this.show(this.triggerEl, false, false);
2358         }
2359         
2360         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2361             e.stopEvent();
2362         }
2363         
2364     },
2365        
2366     
2367     hideMenuItems : function()
2368     {
2369         Roo.log("hide Menu Items");
2370         if (!this.el) { 
2371             return;
2372         }
2373         //$(backdrop).remove()
2374         this.el.select('.open',true).each(function(aa) {
2375             
2376             aa.removeClass('open');
2377           //var parent = getParent($(this))
2378           //var relatedTarget = { relatedTarget: this }
2379           
2380            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2381           //if (e.isDefaultPrevented()) return
2382            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2383         });
2384     },
2385     addxtypeChild : function (tree, cntr) {
2386         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2387           
2388         this.menuitems.add(comp);
2389         return comp;
2390
2391     },
2392     getEl : function()
2393     {
2394         Roo.log(this.el);
2395         return this.el;
2396     },
2397     
2398     clear : function()
2399     {
2400         this.getEl().dom.innerHTML = '';
2401         this.menuitems.clear();
2402     }
2403 });
2404
2405  
2406  /*
2407  * - LGPL
2408  *
2409  * menu item
2410  * 
2411  */
2412
2413
2414 /**
2415  * @class Roo.bootstrap.MenuItem
2416  * @extends Roo.bootstrap.Component
2417  * Bootstrap MenuItem class
2418  * @cfg {String} html the menu label
2419  * @cfg {String} href the link
2420  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2421  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2422  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2423  * @cfg {String} fa favicon to show on left of menu item.
2424  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2425  * 
2426  * 
2427  * @constructor
2428  * Create a new MenuItem
2429  * @param {Object} config The config object
2430  */
2431
2432
2433 Roo.bootstrap.MenuItem = function(config){
2434     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2435     this.addEvents({
2436         // raw events
2437         /**
2438          * @event click
2439          * The raw click event for the entire grid.
2440          * @param {Roo.bootstrap.MenuItem} this
2441          * @param {Roo.EventObject} e
2442          */
2443         "click" : true
2444     });
2445 };
2446
2447 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2448     
2449     href : false,
2450     html : false,
2451     preventDefault: false,
2452     isContainer : false,
2453     active : false,
2454     fa: false,
2455     
2456     getAutoCreate : function(){
2457         
2458         if(this.isContainer){
2459             return {
2460                 tag: 'li',
2461                 cls: 'dropdown-menu-item'
2462             };
2463         }
2464         var ctag = {
2465             tag: 'span',
2466             html: 'Link'
2467         };
2468         
2469         var anc = {
2470             tag : 'a',
2471             href : '#',
2472             cn : [  ]
2473         };
2474         
2475         if (this.fa !== false) {
2476             anc.cn.push({
2477                 tag : 'i',
2478                 cls : 'fa fa-' + this.fa
2479             });
2480         }
2481         
2482         anc.cn.push(ctag);
2483         
2484         
2485         var cfg= {
2486             tag: 'li',
2487             cls: 'dropdown-menu-item',
2488             cn: [ anc ]
2489         };
2490         if (this.parent().type == 'treeview') {
2491             cfg.cls = 'treeview-menu';
2492         }
2493         if (this.active) {
2494             cfg.cls += ' active';
2495         }
2496         
2497         
2498         
2499         anc.href = this.href || cfg.cn[0].href ;
2500         ctag.html = this.html || cfg.cn[0].html ;
2501         return cfg;
2502     },
2503     
2504     initEvents: function()
2505     {
2506         if (this.parent().type == 'treeview') {
2507             this.el.select('a').on('click', this.onClick, this);
2508         }
2509         
2510         if (this.menu) {
2511             this.menu.parentType = this.xtype;
2512             this.menu.triggerEl = this.el;
2513             this.menu = this.addxtype(Roo.apply({}, this.menu));
2514         }
2515         
2516     },
2517     onClick : function(e)
2518     {
2519         Roo.log('item on click ');
2520         
2521         if(this.preventDefault){
2522             e.preventDefault();
2523         }
2524         //this.parent().hideMenuItems();
2525         
2526         this.fireEvent('click', this, e);
2527     },
2528     getEl : function()
2529     {
2530         return this.el;
2531     } 
2532 });
2533
2534  
2535
2536  /*
2537  * - LGPL
2538  *
2539  * menu separator
2540  * 
2541  */
2542
2543
2544 /**
2545  * @class Roo.bootstrap.MenuSeparator
2546  * @extends Roo.bootstrap.Component
2547  * Bootstrap MenuSeparator class
2548  * 
2549  * @constructor
2550  * Create a new MenuItem
2551  * @param {Object} config The config object
2552  */
2553
2554
2555 Roo.bootstrap.MenuSeparator = function(config){
2556     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2557 };
2558
2559 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2560     
2561     getAutoCreate : function(){
2562         var cfg = {
2563             cls: 'divider',
2564             tag : 'li'
2565         };
2566         
2567         return cfg;
2568     }
2569    
2570 });
2571
2572  
2573
2574  
2575 /*
2576 * Licence: LGPL
2577 */
2578
2579 /**
2580  * @class Roo.bootstrap.Modal
2581  * @extends Roo.bootstrap.Component
2582  * Bootstrap Modal class
2583  * @cfg {String} title Title of dialog
2584  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2585  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2586  * @cfg {Boolean} specificTitle default false
2587  * @cfg {Array} buttons Array of buttons or standard button set..
2588  * @cfg {String} buttonPosition (left|right|center) default right
2589  * @cfg {Boolean} animate default true
2590  * @cfg {Boolean} allow_close default true
2591  * @cfg {Boolean} fitwindow default false
2592  * @cfg {String} size (sm|lg) default empty
2593  *
2594  *
2595  * @constructor
2596  * Create a new Modal Dialog
2597  * @param {Object} config The config object
2598  */
2599
2600 Roo.bootstrap.Modal = function(config){
2601     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2602     this.addEvents({
2603         // raw events
2604         /**
2605          * @event btnclick
2606          * The raw btnclick event for the button
2607          * @param {Roo.EventObject} e
2608          */
2609         "btnclick" : true,
2610         /**
2611          * @event resize
2612          * Fire when dialog resize
2613          * @param {Roo.bootstrap.Modal} this
2614          * @param {Roo.EventObject} e
2615          */
2616         "resize" : true
2617     });
2618     this.buttons = this.buttons || [];
2619
2620     if (this.tmpl) {
2621         this.tmpl = Roo.factory(this.tmpl);
2622     }
2623
2624 };
2625
2626 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2627
2628     title : 'test dialog',
2629
2630     buttons : false,
2631
2632     // set on load...
2633
2634     html: false,
2635
2636     tmp: false,
2637
2638     specificTitle: false,
2639
2640     buttonPosition: 'right',
2641
2642     allow_close : true,
2643
2644     animate : true,
2645
2646     fitwindow: false,
2647
2648
2649      // private
2650     dialogEl: false,
2651     bodyEl:  false,
2652     footerEl:  false,
2653     titleEl:  false,
2654     closeEl:  false,
2655
2656     size: '',
2657
2658
2659     onRender : function(ct, position)
2660     {
2661         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2662
2663         if(!this.el){
2664             var cfg = Roo.apply({},  this.getAutoCreate());
2665             cfg.id = Roo.id();
2666             //if(!cfg.name){
2667             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2668             //}
2669             //if (!cfg.name.length) {
2670             //    delete cfg.name;
2671            // }
2672             if (this.cls) {
2673                 cfg.cls += ' ' + this.cls;
2674             }
2675             if (this.style) {
2676                 cfg.style = this.style;
2677             }
2678             this.el = Roo.get(document.body).createChild(cfg, position);
2679         }
2680         //var type = this.el.dom.type;
2681
2682
2683         if(this.tabIndex !== undefined){
2684             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2685         }
2686
2687         this.dialogEl = this.el.select('.modal-dialog',true).first();
2688         this.bodyEl = this.el.select('.modal-body',true).first();
2689         this.closeEl = this.el.select('.modal-header .close', true).first();
2690         this.headerEl = this.el.select('.modal-header',true).first();
2691         this.titleEl = this.el.select('.modal-title',true).first();
2692         this.footerEl = this.el.select('.modal-footer',true).first();
2693
2694         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2695         this.maskEl.enableDisplayMode("block");
2696         this.maskEl.hide();
2697         //this.el.addClass("x-dlg-modal");
2698
2699         if (this.buttons.length) {
2700             Roo.each(this.buttons, function(bb) {
2701                 var b = Roo.apply({}, bb);
2702                 b.xns = b.xns || Roo.bootstrap;
2703                 b.xtype = b.xtype || 'Button';
2704                 if (typeof(b.listeners) == 'undefined') {
2705                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2706                 }
2707
2708                 var btn = Roo.factory(b);
2709
2710                 btn.render(this.el.select('.modal-footer div').first());
2711
2712             },this);
2713         }
2714         // render the children.
2715         var nitems = [];
2716
2717         if(typeof(this.items) != 'undefined'){
2718             var items = this.items;
2719             delete this.items;
2720
2721             for(var i =0;i < items.length;i++) {
2722                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2723             }
2724         }
2725
2726         this.items = nitems;
2727
2728         // where are these used - they used to be body/close/footer
2729
2730
2731         this.initEvents();
2732         //this.el.addClass([this.fieldClass, this.cls]);
2733
2734     },
2735
2736     getAutoCreate : function(){
2737
2738
2739         var bdy = {
2740                 cls : 'modal-body',
2741                 html : this.html || ''
2742         };
2743
2744         var title = {
2745             tag: 'h4',
2746             cls : 'modal-title',
2747             html : this.title
2748         };
2749
2750         if(this.specificTitle){
2751             title = this.title;
2752
2753         };
2754
2755         var header = [];
2756         if (this.allow_close) {
2757             header.push({
2758                 tag: 'button',
2759                 cls : 'close',
2760                 html : '&times'
2761             });
2762         }
2763
2764         header.push(title);
2765
2766         var size = '';
2767
2768         if(this.size.length){
2769             size = 'modal-' + this.size;
2770         }
2771
2772         var modal = {
2773             cls: "modal",
2774             style : 'display: none',
2775             cn : [
2776                 {
2777                     cls: "modal-dialog " + size,
2778                     cn : [
2779                         {
2780                             cls : "modal-content",
2781                             cn : [
2782                                 {
2783                                     cls : 'modal-header',
2784                                     cn : header
2785                                 },
2786                                 bdy,
2787                                 {
2788                                     cls : 'modal-footer',
2789                                     cn : [
2790                                         {
2791                                             tag: 'div',
2792                                             cls: 'btn-' + this.buttonPosition
2793                                         }
2794                                     ]
2795
2796                                 }
2797
2798
2799                             ]
2800
2801                         }
2802                     ]
2803
2804                 }
2805             ]
2806         };
2807
2808         if(this.animate){
2809             modal.cls += ' fade';
2810         }
2811
2812         return modal;
2813
2814     },
2815     getChildContainer : function() {
2816
2817          return this.bodyEl;
2818
2819     },
2820     getButtonContainer : function() {
2821          return this.el.select('.modal-footer div',true).first();
2822
2823     },
2824     initEvents : function()
2825     {
2826         if (this.allow_close) {
2827             this.closeEl.on('click', this.hide, this);
2828         }
2829         Roo.EventManager.onWindowResize(this.resize, this, true);
2830
2831
2832     },
2833
2834     resize : function()
2835     {
2836         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2837         if (this.fitwindow) {
2838             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2839             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2840             this.setSize(w,h);
2841         }
2842     },
2843
2844     setSize : function(w,h)
2845     {
2846         if (!w && !h) {
2847             return;
2848         }
2849         this.resizeTo(w,h);
2850     },
2851
2852     show : function() {
2853
2854         if (!this.rendered) {
2855             this.render();
2856         }
2857
2858         this.el.setStyle('display', 'block');
2859
2860         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2861             var _this = this;
2862             (function(){
2863                 this.el.addClass('in');
2864             }).defer(50, this);
2865         }else{
2866             this.el.addClass('in');
2867
2868         }
2869
2870         // not sure how we can show data in here..
2871         //if (this.tmpl) {
2872         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2873         //}
2874
2875         Roo.get(document.body).addClass("x-body-masked");
2876         
2877         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2878         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2879         this.maskEl.show();
2880         
2881         this.resize();
2882         
2883         this.fireEvent('show', this);
2884
2885         // set zindex here - otherwise it appears to be ignored...
2886         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2887
2888         (function () {
2889             this.items.forEach( function(e) {
2890                 e.layout ? e.layout() : false;
2891
2892             });
2893         }).defer(100,this);
2894
2895     },
2896     hide : function()
2897     {
2898         if(this.fireEvent("beforehide", this) !== false){
2899             this.maskEl.hide();
2900             Roo.get(document.body).removeClass("x-body-masked");
2901             this.el.removeClass('in');
2902             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2903
2904             if(this.animate){ // why
2905                 var _this = this;
2906                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2907             }else{
2908                 this.el.setStyle('display', 'none');
2909             }
2910             this.fireEvent('hide', this);
2911         }
2912     },
2913
2914     addButton : function(str, cb)
2915     {
2916
2917
2918         var b = Roo.apply({}, { html : str } );
2919         b.xns = b.xns || Roo.bootstrap;
2920         b.xtype = b.xtype || 'Button';
2921         if (typeof(b.listeners) == 'undefined') {
2922             b.listeners = { click : cb.createDelegate(this)  };
2923         }
2924
2925         var btn = Roo.factory(b);
2926
2927         btn.render(this.el.select('.modal-footer div').first());
2928
2929         return btn;
2930
2931     },
2932
2933     setDefaultButton : function(btn)
2934     {
2935         //this.el.select('.modal-footer').()
2936     },
2937     diff : false,
2938
2939     resizeTo: function(w,h)
2940     {
2941         // skip.. ?? why??
2942
2943         this.dialogEl.setWidth(w);
2944         if (this.diff === false) {
2945             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2946         }
2947
2948         this.bodyEl.setHeight(h-this.diff);
2949
2950         this.fireEvent('resize', this);
2951
2952     },
2953     setContentSize  : function(w, h)
2954     {
2955
2956     },
2957     onButtonClick: function(btn,e)
2958     {
2959         //Roo.log([a,b,c]);
2960         this.fireEvent('btnclick', btn.name, e);
2961     },
2962      /**
2963      * Set the title of the Dialog
2964      * @param {String} str new Title
2965      */
2966     setTitle: function(str) {
2967         this.titleEl.dom.innerHTML = str;
2968     },
2969     /**
2970      * Set the body of the Dialog
2971      * @param {String} str new Title
2972      */
2973     setBody: function(str) {
2974         this.bodyEl.dom.innerHTML = str;
2975     },
2976     /**
2977      * Set the body of the Dialog using the template
2978      * @param {Obj} data - apply this data to the template and replace the body contents.
2979      */
2980     applyBody: function(obj)
2981     {
2982         if (!this.tmpl) {
2983             Roo.log("Error - using apply Body without a template");
2984             //code
2985         }
2986         this.tmpl.overwrite(this.bodyEl, obj);
2987     }
2988
2989 });
2990
2991
2992 Roo.apply(Roo.bootstrap.Modal,  {
2993     /**
2994          * Button config that displays a single OK button
2995          * @type Object
2996          */
2997         OK :  [{
2998             name : 'ok',
2999             weight : 'primary',
3000             html : 'OK'
3001         }],
3002         /**
3003          * Button config that displays Yes and No buttons
3004          * @type Object
3005          */
3006         YESNO : [
3007             {
3008                 name  : 'no',
3009                 html : 'No'
3010             },
3011             {
3012                 name  :'yes',
3013                 weight : 'primary',
3014                 html : 'Yes'
3015             }
3016         ],
3017
3018         /**
3019          * Button config that displays OK and Cancel buttons
3020          * @type Object
3021          */
3022         OKCANCEL : [
3023             {
3024                name : 'cancel',
3025                 html : 'Cancel'
3026             },
3027             {
3028                 name : 'ok',
3029                 weight : 'primary',
3030                 html : 'OK'
3031             }
3032         ],
3033         /**
3034          * Button config that displays Yes, No and Cancel buttons
3035          * @type Object
3036          */
3037         YESNOCANCEL : [
3038             {
3039                 name : 'yes',
3040                 weight : 'primary',
3041                 html : 'Yes'
3042             },
3043             {
3044                 name : 'no',
3045                 html : 'No'
3046             },
3047             {
3048                 name : 'cancel',
3049                 html : 'Cancel'
3050             }
3051         ],
3052         
3053         zIndex : 10001
3054 });
3055 /*
3056  * - LGPL
3057  *
3058  * messagebox - can be used as a replace
3059  * 
3060  */
3061 /**
3062  * @class Roo.MessageBox
3063  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3064  * Example usage:
3065  *<pre><code>
3066 // Basic alert:
3067 Roo.Msg.alert('Status', 'Changes saved successfully.');
3068
3069 // Prompt for user data:
3070 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3071     if (btn == 'ok'){
3072         // process text value...
3073     }
3074 });
3075
3076 // Show a dialog using config options:
3077 Roo.Msg.show({
3078    title:'Save Changes?',
3079    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3080    buttons: Roo.Msg.YESNOCANCEL,
3081    fn: processResult,
3082    animEl: 'elId'
3083 });
3084 </code></pre>
3085  * @singleton
3086  */
3087 Roo.bootstrap.MessageBox = function(){
3088     var dlg, opt, mask, waitTimer;
3089     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3090     var buttons, activeTextEl, bwidth;
3091
3092     
3093     // private
3094     var handleButton = function(button){
3095         dlg.hide();
3096         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3097     };
3098
3099     // private
3100     var handleHide = function(){
3101         if(opt && opt.cls){
3102             dlg.el.removeClass(opt.cls);
3103         }
3104         //if(waitTimer){
3105         //    Roo.TaskMgr.stop(waitTimer);
3106         //    waitTimer = null;
3107         //}
3108     };
3109
3110     // private
3111     var updateButtons = function(b){
3112         var width = 0;
3113         if(!b){
3114             buttons["ok"].hide();
3115             buttons["cancel"].hide();
3116             buttons["yes"].hide();
3117             buttons["no"].hide();
3118             //dlg.footer.dom.style.display = 'none';
3119             return width;
3120         }
3121         dlg.footerEl.dom.style.display = '';
3122         for(var k in buttons){
3123             if(typeof buttons[k] != "function"){
3124                 if(b[k]){
3125                     buttons[k].show();
3126                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3127                     width += buttons[k].el.getWidth()+15;
3128                 }else{
3129                     buttons[k].hide();
3130                 }
3131             }
3132         }
3133         return width;
3134     };
3135
3136     // private
3137     var handleEsc = function(d, k, e){
3138         if(opt && opt.closable !== false){
3139             dlg.hide();
3140         }
3141         if(e){
3142             e.stopEvent();
3143         }
3144     };
3145
3146     return {
3147         /**
3148          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3149          * @return {Roo.BasicDialog} The BasicDialog element
3150          */
3151         getDialog : function(){
3152            if(!dlg){
3153                 dlg = new Roo.bootstrap.Modal( {
3154                     //draggable: true,
3155                     //resizable:false,
3156                     //constraintoviewport:false,
3157                     //fixedcenter:true,
3158                     //collapsible : false,
3159                     //shim:true,
3160                     //modal: true,
3161                 //    width: 'auto',
3162                   //  height:100,
3163                     //buttonAlign:"center",
3164                     closeClick : function(){
3165                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3166                             handleButton("no");
3167                         }else{
3168                             handleButton("cancel");
3169                         }
3170                     }
3171                 });
3172                 dlg.render();
3173                 dlg.on("hide", handleHide);
3174                 mask = dlg.mask;
3175                 //dlg.addKeyListener(27, handleEsc);
3176                 buttons = {};
3177                 this.buttons = buttons;
3178                 var bt = this.buttonText;
3179                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3180                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3181                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3182                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3183                 //Roo.log(buttons);
3184                 bodyEl = dlg.bodyEl.createChild({
3185
3186                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3187                         '<textarea class="roo-mb-textarea"></textarea>' +
3188                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3189                 });
3190                 msgEl = bodyEl.dom.firstChild;
3191                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3192                 textboxEl.enableDisplayMode();
3193                 textboxEl.addKeyListener([10,13], function(){
3194                     if(dlg.isVisible() && opt && opt.buttons){
3195                         if(opt.buttons.ok){
3196                             handleButton("ok");
3197                         }else if(opt.buttons.yes){
3198                             handleButton("yes");
3199                         }
3200                     }
3201                 });
3202                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3203                 textareaEl.enableDisplayMode();
3204                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3205                 progressEl.enableDisplayMode();
3206                 
3207                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3208                 var pf = progressEl.dom.firstChild;
3209                 if (pf) {
3210                     pp = Roo.get(pf.firstChild);
3211                     pp.setHeight(pf.offsetHeight);
3212                 }
3213                 
3214             }
3215             return dlg;
3216         },
3217
3218         /**
3219          * Updates the message box body text
3220          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3221          * the XHTML-compliant non-breaking space character '&amp;#160;')
3222          * @return {Roo.MessageBox} This message box
3223          */
3224         updateText : function(text)
3225         {
3226             if(!dlg.isVisible() && !opt.width){
3227                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3228                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3229             }
3230             msgEl.innerHTML = text || '&#160;';
3231       
3232             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3233             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3234             var w = Math.max(
3235                     Math.min(opt.width || cw , this.maxWidth), 
3236                     Math.max(opt.minWidth || this.minWidth, bwidth)
3237             );
3238             if(opt.prompt){
3239                 activeTextEl.setWidth(w);
3240             }
3241             if(dlg.isVisible()){
3242                 dlg.fixedcenter = false;
3243             }
3244             // to big, make it scroll. = But as usual stupid IE does not support
3245             // !important..
3246             
3247             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3248                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3249                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3250             } else {
3251                 bodyEl.dom.style.height = '';
3252                 bodyEl.dom.style.overflowY = '';
3253             }
3254             if (cw > w) {
3255                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3256             } else {
3257                 bodyEl.dom.style.overflowX = '';
3258             }
3259             
3260             dlg.setContentSize(w, bodyEl.getHeight());
3261             if(dlg.isVisible()){
3262                 dlg.fixedcenter = true;
3263             }
3264             return this;
3265         },
3266
3267         /**
3268          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3269          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3270          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3271          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3272          * @return {Roo.MessageBox} This message box
3273          */
3274         updateProgress : function(value, text){
3275             if(text){
3276                 this.updateText(text);
3277             }
3278             
3279             if (pp) { // weird bug on my firefox - for some reason this is not defined
3280                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3281                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3282             }
3283             return this;
3284         },        
3285
3286         /**
3287          * Returns true if the message box is currently displayed
3288          * @return {Boolean} True if the message box is visible, else false
3289          */
3290         isVisible : function(){
3291             return dlg && dlg.isVisible();  
3292         },
3293
3294         /**
3295          * Hides the message box if it is displayed
3296          */
3297         hide : function(){
3298             if(this.isVisible()){
3299                 dlg.hide();
3300             }  
3301         },
3302
3303         /**
3304          * Displays a new message box, or reinitializes an existing message box, based on the config options
3305          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3306          * The following config object properties are supported:
3307          * <pre>
3308 Property    Type             Description
3309 ----------  ---------------  ------------------------------------------------------------------------------------
3310 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3311                                    closes (defaults to undefined)
3312 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3313                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3314 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3315                                    progress and wait dialogs will ignore this property and always hide the
3316                                    close button as they can only be closed programmatically.
3317 cls               String           A custom CSS class to apply to the message box element
3318 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3319                                    displayed (defaults to 75)
3320 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3321                                    function will be btn (the name of the button that was clicked, if applicable,
3322                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3323                                    Progress and wait dialogs will ignore this option since they do not respond to
3324                                    user actions and can only be closed programmatically, so any required function
3325                                    should be called by the same code after it closes the dialog.
3326 icon              String           A CSS class that provides a background image to be used as an icon for
3327                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3328 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3329 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3330 modal             Boolean          False to allow user interaction with the page while the message box is
3331                                    displayed (defaults to true)
3332 msg               String           A string that will replace the existing message box body text (defaults
3333                                    to the XHTML-compliant non-breaking space character '&#160;')
3334 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3335 progress          Boolean          True to display a progress bar (defaults to false)
3336 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3337 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3338 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3339 title             String           The title text
3340 value             String           The string value to set into the active textbox element if displayed
3341 wait              Boolean          True to display a progress bar (defaults to false)
3342 width             Number           The width of the dialog in pixels
3343 </pre>
3344          *
3345          * Example usage:
3346          * <pre><code>
3347 Roo.Msg.show({
3348    title: 'Address',
3349    msg: 'Please enter your address:',
3350    width: 300,
3351    buttons: Roo.MessageBox.OKCANCEL,
3352    multiline: true,
3353    fn: saveAddress,
3354    animEl: 'addAddressBtn'
3355 });
3356 </code></pre>
3357          * @param {Object} config Configuration options
3358          * @return {Roo.MessageBox} This message box
3359          */
3360         show : function(options)
3361         {
3362             
3363             // this causes nightmares if you show one dialog after another
3364             // especially on callbacks..
3365              
3366             if(this.isVisible()){
3367                 
3368                 this.hide();
3369                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3370                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3371                 Roo.log("New Dialog Message:" +  options.msg )
3372                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3373                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3374                 
3375             }
3376             var d = this.getDialog();
3377             opt = options;
3378             d.setTitle(opt.title || "&#160;");
3379             d.closeEl.setDisplayed(opt.closable !== false);
3380             activeTextEl = textboxEl;
3381             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3382             if(opt.prompt){
3383                 if(opt.multiline){
3384                     textboxEl.hide();
3385                     textareaEl.show();
3386                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3387                         opt.multiline : this.defaultTextHeight);
3388                     activeTextEl = textareaEl;
3389                 }else{
3390                     textboxEl.show();
3391                     textareaEl.hide();
3392                 }
3393             }else{
3394                 textboxEl.hide();
3395                 textareaEl.hide();
3396             }
3397             progressEl.setDisplayed(opt.progress === true);
3398             this.updateProgress(0);
3399             activeTextEl.dom.value = opt.value || "";
3400             if(opt.prompt){
3401                 dlg.setDefaultButton(activeTextEl);
3402             }else{
3403                 var bs = opt.buttons;
3404                 var db = null;
3405                 if(bs && bs.ok){
3406                     db = buttons["ok"];
3407                 }else if(bs && bs.yes){
3408                     db = buttons["yes"];
3409                 }
3410                 dlg.setDefaultButton(db);
3411             }
3412             bwidth = updateButtons(opt.buttons);
3413             this.updateText(opt.msg);
3414             if(opt.cls){
3415                 d.el.addClass(opt.cls);
3416             }
3417             d.proxyDrag = opt.proxyDrag === true;
3418             d.modal = opt.modal !== false;
3419             d.mask = opt.modal !== false ? mask : false;
3420             if(!d.isVisible()){
3421                 // force it to the end of the z-index stack so it gets a cursor in FF
3422                 document.body.appendChild(dlg.el.dom);
3423                 d.animateTarget = null;
3424                 d.show(options.animEl);
3425             }
3426             return this;
3427         },
3428
3429         /**
3430          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3431          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3432          * and closing the message box when the process is complete.
3433          * @param {String} title The title bar text
3434          * @param {String} msg The message box body text
3435          * @return {Roo.MessageBox} This message box
3436          */
3437         progress : function(title, msg){
3438             this.show({
3439                 title : title,
3440                 msg : msg,
3441                 buttons: false,
3442                 progress:true,
3443                 closable:false,
3444                 minWidth: this.minProgressWidth,
3445                 modal : true
3446             });
3447             return this;
3448         },
3449
3450         /**
3451          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3452          * If a callback function is passed it will be called after the user clicks the button, and the
3453          * id of the button that was clicked will be passed as the only parameter to the callback
3454          * (could also be the top-right close button).
3455          * @param {String} title The title bar text
3456          * @param {String} msg The message box body text
3457          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3458          * @param {Object} scope (optional) The scope of the callback function
3459          * @return {Roo.MessageBox} This message box
3460          */
3461         alert : function(title, msg, fn, scope)
3462         {
3463             this.show({
3464                 title : title,
3465                 msg : msg,
3466                 buttons: this.OK,
3467                 fn: fn,
3468                 closable : false,
3469                 scope : scope,
3470                 modal : true
3471             });
3472             return this;
3473         },
3474
3475         /**
3476          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3477          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3478          * You are responsible for closing the message box when the process is complete.
3479          * @param {String} msg The message box body text
3480          * @param {String} title (optional) The title bar text
3481          * @return {Roo.MessageBox} This message box
3482          */
3483         wait : function(msg, title){
3484             this.show({
3485                 title : title,
3486                 msg : msg,
3487                 buttons: false,
3488                 closable:false,
3489                 progress:true,
3490                 modal:true,
3491                 width:300,
3492                 wait:true
3493             });
3494             waitTimer = Roo.TaskMgr.start({
3495                 run: function(i){
3496                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3497                 },
3498                 interval: 1000
3499             });
3500             return this;
3501         },
3502
3503         /**
3504          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3505          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3506          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3507          * @param {String} title The title bar text
3508          * @param {String} msg The message box body text
3509          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3510          * @param {Object} scope (optional) The scope of the callback function
3511          * @return {Roo.MessageBox} This message box
3512          */
3513         confirm : function(title, msg, fn, scope){
3514             this.show({
3515                 title : title,
3516                 msg : msg,
3517                 buttons: this.YESNO,
3518                 fn: fn,
3519                 scope : scope,
3520                 modal : true
3521             });
3522             return this;
3523         },
3524
3525         /**
3526          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3527          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3528          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3529          * (could also be the top-right close button) and the text that was entered will be passed as the two
3530          * parameters to the callback.
3531          * @param {String} title The title bar text
3532          * @param {String} msg The message box body text
3533          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3534          * @param {Object} scope (optional) The scope of the callback function
3535          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3536          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3537          * @return {Roo.MessageBox} This message box
3538          */
3539         prompt : function(title, msg, fn, scope, multiline){
3540             this.show({
3541                 title : title,
3542                 msg : msg,
3543                 buttons: this.OKCANCEL,
3544                 fn: fn,
3545                 minWidth:250,
3546                 scope : scope,
3547                 prompt:true,
3548                 multiline: multiline,
3549                 modal : true
3550             });
3551             return this;
3552         },
3553
3554         /**
3555          * Button config that displays a single OK button
3556          * @type Object
3557          */
3558         OK : {ok:true},
3559         /**
3560          * Button config that displays Yes and No buttons
3561          * @type Object
3562          */
3563         YESNO : {yes:true, no:true},
3564         /**
3565          * Button config that displays OK and Cancel buttons
3566          * @type Object
3567          */
3568         OKCANCEL : {ok:true, cancel:true},
3569         /**
3570          * Button config that displays Yes, No and Cancel buttons
3571          * @type Object
3572          */
3573         YESNOCANCEL : {yes:true, no:true, cancel:true},
3574
3575         /**
3576          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3577          * @type Number
3578          */
3579         defaultTextHeight : 75,
3580         /**
3581          * The maximum width in pixels of the message box (defaults to 600)
3582          * @type Number
3583          */
3584         maxWidth : 600,
3585         /**
3586          * The minimum width in pixels of the message box (defaults to 100)
3587          * @type Number
3588          */
3589         minWidth : 100,
3590         /**
3591          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3592          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3593          * @type Number
3594          */
3595         minProgressWidth : 250,
3596         /**
3597          * An object containing the default button text strings that can be overriden for localized language support.
3598          * Supported properties are: ok, cancel, yes and no.
3599          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3600          * @type Object
3601          */
3602         buttonText : {
3603             ok : "OK",
3604             cancel : "Cancel",
3605             yes : "Yes",
3606             no : "No"
3607         }
3608     };
3609 }();
3610
3611 /**
3612  * Shorthand for {@link Roo.MessageBox}
3613  */
3614 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3615 Roo.Msg = Roo.Msg || Roo.MessageBox;
3616 /*
3617  * - LGPL
3618  *
3619  * navbar
3620  * 
3621  */
3622
3623 /**
3624  * @class Roo.bootstrap.Navbar
3625  * @extends Roo.bootstrap.Component
3626  * Bootstrap Navbar class
3627
3628  * @constructor
3629  * Create a new Navbar
3630  * @param {Object} config The config object
3631  */
3632
3633
3634 Roo.bootstrap.Navbar = function(config){
3635     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3636     this.addEvents({
3637         // raw events
3638         /**
3639          * @event beforetoggle
3640          * Fire before toggle the menu
3641          * @param {Roo.EventObject} e
3642          */
3643         "beforetoggle" : true
3644     });
3645 };
3646
3647 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3648     
3649     
3650    
3651     // private
3652     navItems : false,
3653     loadMask : false,
3654     
3655     
3656     getAutoCreate : function(){
3657         
3658         
3659         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3660         
3661     },
3662     
3663     initEvents :function ()
3664     {
3665         //Roo.log(this.el.select('.navbar-toggle',true));
3666         this.el.select('.navbar-toggle',true).on('click', function() {
3667             if(this.fireEvent('beforetoggle', this) !== false){
3668                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3669             }
3670             
3671         }, this);
3672         
3673         var mark = {
3674             tag: "div",
3675             cls:"x-dlg-mask"
3676         };
3677         
3678         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3679         
3680         var size = this.el.getSize();
3681         this.maskEl.setSize(size.width, size.height);
3682         this.maskEl.enableDisplayMode("block");
3683         this.maskEl.hide();
3684         
3685         if(this.loadMask){
3686             this.maskEl.show();
3687         }
3688     },
3689     
3690     
3691     getChildContainer : function()
3692     {
3693         if (this.el.select('.collapse').getCount()) {
3694             return this.el.select('.collapse',true).first();
3695         }
3696         
3697         return this.el;
3698     },
3699     
3700     mask : function()
3701     {
3702         this.maskEl.show();
3703     },
3704     
3705     unmask : function()
3706     {
3707         this.maskEl.hide();
3708     } 
3709     
3710     
3711     
3712     
3713 });
3714
3715
3716
3717  
3718
3719  /*
3720  * - LGPL
3721  *
3722  * navbar
3723  * 
3724  */
3725
3726 /**
3727  * @class Roo.bootstrap.NavSimplebar
3728  * @extends Roo.bootstrap.Navbar
3729  * Bootstrap Sidebar class
3730  *
3731  * @cfg {Boolean} inverse is inverted color
3732  * 
3733  * @cfg {String} type (nav | pills | tabs)
3734  * @cfg {Boolean} arrangement stacked | justified
3735  * @cfg {String} align (left | right) alignment
3736  * 
3737  * @cfg {Boolean} main (true|false) main nav bar? default false
3738  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3739  * 
3740  * @cfg {String} tag (header|footer|nav|div) default is nav 
3741
3742  * 
3743  * 
3744  * 
3745  * @constructor
3746  * Create a new Sidebar
3747  * @param {Object} config The config object
3748  */
3749
3750
3751 Roo.bootstrap.NavSimplebar = function(config){
3752     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3753 };
3754
3755 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3756     
3757     inverse: false,
3758     
3759     type: false,
3760     arrangement: '',
3761     align : false,
3762     
3763     
3764     
3765     main : false,
3766     
3767     
3768     tag : false,
3769     
3770     
3771     getAutoCreate : function(){
3772         
3773         
3774         var cfg = {
3775             tag : this.tag || 'div',
3776             cls : 'navbar'
3777         };
3778           
3779         
3780         cfg.cn = [
3781             {
3782                 cls: 'nav',
3783                 tag : 'ul'
3784             }
3785         ];
3786         
3787          
3788         this.type = this.type || 'nav';
3789         if (['tabs','pills'].indexOf(this.type)!==-1) {
3790             cfg.cn[0].cls += ' nav-' + this.type
3791         
3792         
3793         } else {
3794             if (this.type!=='nav') {
3795                 Roo.log('nav type must be nav/tabs/pills')
3796             }
3797             cfg.cn[0].cls += ' navbar-nav'
3798         }
3799         
3800         
3801         
3802         
3803         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3804             cfg.cn[0].cls += ' nav-' + this.arrangement;
3805         }
3806         
3807         
3808         if (this.align === 'right') {
3809             cfg.cn[0].cls += ' navbar-right';
3810         }
3811         
3812         if (this.inverse) {
3813             cfg.cls += ' navbar-inverse';
3814             
3815         }
3816         
3817         
3818         return cfg;
3819     
3820         
3821     }
3822     
3823     
3824     
3825 });
3826
3827
3828
3829  
3830
3831  
3832        /*
3833  * - LGPL
3834  *
3835  * navbar
3836  * 
3837  */
3838
3839 /**
3840  * @class Roo.bootstrap.NavHeaderbar
3841  * @extends Roo.bootstrap.NavSimplebar
3842  * Bootstrap Sidebar class
3843  *
3844  * @cfg {String} brand what is brand
3845  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3846  * @cfg {String} brand_href href of the brand
3847  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3848  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3849  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3850  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3851  * 
3852  * @constructor
3853  * Create a new Sidebar
3854  * @param {Object} config The config object
3855  */
3856
3857
3858 Roo.bootstrap.NavHeaderbar = function(config){
3859     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3860       
3861 };
3862
3863 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3864     
3865     position: '',
3866     brand: '',
3867     brand_href: false,
3868     srButton : true,
3869     autohide : false,
3870     desktopCenter : false,
3871    
3872     
3873     getAutoCreate : function(){
3874         
3875         var   cfg = {
3876             tag: this.nav || 'nav',
3877             cls: 'navbar',
3878             role: 'navigation',
3879             cn: []
3880         };
3881         
3882         var cn = cfg.cn;
3883         if (this.desktopCenter) {
3884             cn.push({cls : 'container', cn : []});
3885             cn = cn[0].cn;
3886         }
3887         
3888         if(this.srButton){
3889             cn.push({
3890                 tag: 'div',
3891                 cls: 'navbar-header',
3892                 cn: [
3893                     {
3894                         tag: 'button',
3895                         type: 'button',
3896                         cls: 'navbar-toggle',
3897                         'data-toggle': 'collapse',
3898                         cn: [
3899                             {
3900                                 tag: 'span',
3901                                 cls: 'sr-only',
3902                                 html: 'Toggle navigation'
3903                             },
3904                             {
3905                                 tag: 'span',
3906                                 cls: 'icon-bar'
3907                             },
3908                             {
3909                                 tag: 'span',
3910                                 cls: 'icon-bar'
3911                             },
3912                             {
3913                                 tag: 'span',
3914                                 cls: 'icon-bar'
3915                             }
3916                         ]
3917                     }
3918                 ]
3919             });
3920         }
3921         
3922         cn.push({
3923             tag: 'div',
3924             cls: 'collapse navbar-collapse',
3925             cn : []
3926         });
3927         
3928         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3929         
3930         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3931             cfg.cls += ' navbar-' + this.position;
3932             
3933             // tag can override this..
3934             
3935             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3936         }
3937         
3938         if (this.brand !== '') {
3939             cn[0].cn.push({
3940                 tag: 'a',
3941                 href: this.brand_href ? this.brand_href : '#',
3942                 cls: 'navbar-brand',
3943                 cn: [
3944                 this.brand
3945                 ]
3946             });
3947         }
3948         
3949         if(this.main){
3950             cfg.cls += ' main-nav';
3951         }
3952         
3953         
3954         return cfg;
3955
3956         
3957     },
3958     getHeaderChildContainer : function()
3959     {
3960         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3961             return this.el.select('.navbar-header',true).first();
3962         }
3963         
3964         return this.getChildContainer();
3965     },
3966     
3967     
3968     initEvents : function()
3969     {
3970         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3971         
3972         if (this.autohide) {
3973             
3974             var prevScroll = 0;
3975             var ft = this.el;
3976             
3977             Roo.get(document).on('scroll',function(e) {
3978                 var ns = Roo.get(document).getScroll().top;
3979                 var os = prevScroll;
3980                 prevScroll = ns;
3981                 
3982                 if(ns > os){
3983                     ft.removeClass('slideDown');
3984                     ft.addClass('slideUp');
3985                     return;
3986                 }
3987                 ft.removeClass('slideUp');
3988                 ft.addClass('slideDown');
3989                  
3990               
3991           },this);
3992         }
3993     }    
3994     
3995 });
3996
3997
3998
3999  
4000
4001  /*
4002  * - LGPL
4003  *
4004  * navbar
4005  * 
4006  */
4007
4008 /**
4009  * @class Roo.bootstrap.NavSidebar
4010  * @extends Roo.bootstrap.Navbar
4011  * Bootstrap Sidebar class
4012  * 
4013  * @constructor
4014  * Create a new Sidebar
4015  * @param {Object} config The config object
4016  */
4017
4018
4019 Roo.bootstrap.NavSidebar = function(config){
4020     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4021 };
4022
4023 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4024     
4025     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4026     
4027     getAutoCreate : function(){
4028         
4029         
4030         return  {
4031             tag: 'div',
4032             cls: 'sidebar sidebar-nav'
4033         };
4034     
4035         
4036     }
4037     
4038     
4039     
4040 });
4041
4042
4043
4044  
4045
4046  /*
4047  * - LGPL
4048  *
4049  * nav group
4050  * 
4051  */
4052
4053 /**
4054  * @class Roo.bootstrap.NavGroup
4055  * @extends Roo.bootstrap.Component
4056  * Bootstrap NavGroup class
4057  * @cfg {String} align (left|right)
4058  * @cfg {Boolean} inverse
4059  * @cfg {String} type (nav|pills|tab) default nav
4060  * @cfg {String} navId - reference Id for navbar.
4061
4062  * 
4063  * @constructor
4064  * Create a new nav group
4065  * @param {Object} config The config object
4066  */
4067
4068 Roo.bootstrap.NavGroup = function(config){
4069     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4070     this.navItems = [];
4071    
4072     Roo.bootstrap.NavGroup.register(this);
4073      this.addEvents({
4074         /**
4075              * @event changed
4076              * Fires when the active item changes
4077              * @param {Roo.bootstrap.NavGroup} this
4078              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4079              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4080          */
4081         'changed': true
4082      });
4083     
4084 };
4085
4086 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4087     
4088     align: '',
4089     inverse: false,
4090     form: false,
4091     type: 'nav',
4092     navId : '',
4093     // private
4094     
4095     navItems : false, 
4096     
4097     getAutoCreate : function()
4098     {
4099         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4100         
4101         cfg = {
4102             tag : 'ul',
4103             cls: 'nav' 
4104         };
4105         
4106         if (['tabs','pills'].indexOf(this.type)!==-1) {
4107             cfg.cls += ' nav-' + this.type
4108         } else {
4109             if (this.type!=='nav') {
4110                 Roo.log('nav type must be nav/tabs/pills')
4111             }
4112             cfg.cls += ' navbar-nav'
4113         }
4114         
4115         if (this.parent() && this.parent().sidebar) {
4116             cfg = {
4117                 tag: 'ul',
4118                 cls: 'dashboard-menu sidebar-menu'
4119             };
4120             
4121             return cfg;
4122         }
4123         
4124         if (this.form === true) {
4125             cfg = {
4126                 tag: 'form',
4127                 cls: 'navbar-form'
4128             };
4129             
4130             if (this.align === 'right') {
4131                 cfg.cls += ' navbar-right';
4132             } else {
4133                 cfg.cls += ' navbar-left';
4134             }
4135         }
4136         
4137         if (this.align === 'right') {
4138             cfg.cls += ' navbar-right';
4139         }
4140         
4141         if (this.inverse) {
4142             cfg.cls += ' navbar-inverse';
4143             
4144         }
4145         
4146         
4147         return cfg;
4148     },
4149     /**
4150     * sets the active Navigation item
4151     * @param {Roo.bootstrap.NavItem} the new current navitem
4152     */
4153     setActiveItem : function(item)
4154     {
4155         var prev = false;
4156         Roo.each(this.navItems, function(v){
4157             if (v == item) {
4158                 return ;
4159             }
4160             if (v.isActive()) {
4161                 v.setActive(false, true);
4162                 prev = v;
4163                 
4164             }
4165             
4166         });
4167
4168         item.setActive(true, true);
4169         this.fireEvent('changed', this, item, prev);
4170         
4171         
4172     },
4173     /**
4174     * gets the active Navigation item
4175     * @return {Roo.bootstrap.NavItem} the current navitem
4176     */
4177     getActive : function()
4178     {
4179         
4180         var prev = false;
4181         Roo.each(this.navItems, function(v){
4182             
4183             if (v.isActive()) {
4184                 prev = v;
4185                 
4186             }
4187             
4188         });
4189         return prev;
4190     },
4191     
4192     indexOfNav : function()
4193     {
4194         
4195         var prev = false;
4196         Roo.each(this.navItems, function(v,i){
4197             
4198             if (v.isActive()) {
4199                 prev = i;
4200                 
4201             }
4202             
4203         });
4204         return prev;
4205     },
4206     /**
4207     * adds a Navigation item
4208     * @param {Roo.bootstrap.NavItem} the navitem to add
4209     */
4210     addItem : function(cfg)
4211     {
4212         var cn = new Roo.bootstrap.NavItem(cfg);
4213         this.register(cn);
4214         cn.parentId = this.id;
4215         cn.onRender(this.el, null);
4216         return cn;
4217     },
4218     /**
4219     * register a Navigation item
4220     * @param {Roo.bootstrap.NavItem} the navitem to add
4221     */
4222     register : function(item)
4223     {
4224         this.navItems.push( item);
4225         item.navId = this.navId;
4226     
4227     },
4228     
4229     /**
4230     * clear all the Navigation item
4231     */
4232    
4233     clearAll : function()
4234     {
4235         this.navItems = [];
4236         this.el.dom.innerHTML = '';
4237     },
4238     
4239     getNavItem: function(tabId)
4240     {
4241         var ret = false;
4242         Roo.each(this.navItems, function(e) {
4243             if (e.tabId == tabId) {
4244                ret =  e;
4245                return false;
4246             }
4247             return true;
4248             
4249         });
4250         return ret;
4251     },
4252     
4253     setActiveNext : function()
4254     {
4255         var i = this.indexOfNav(this.getActive());
4256         if (i > this.navItems.length) {
4257             return;
4258         }
4259         this.setActiveItem(this.navItems[i+1]);
4260     },
4261     setActivePrev : function()
4262     {
4263         var i = this.indexOfNav(this.getActive());
4264         if (i  < 1) {
4265             return;
4266         }
4267         this.setActiveItem(this.navItems[i-1]);
4268     },
4269     clearWasActive : function(except) {
4270         Roo.each(this.navItems, function(e) {
4271             if (e.tabId != except.tabId && e.was_active) {
4272                e.was_active = false;
4273                return false;
4274             }
4275             return true;
4276             
4277         });
4278     },
4279     getWasActive : function ()
4280     {
4281         var r = false;
4282         Roo.each(this.navItems, function(e) {
4283             if (e.was_active) {
4284                r = e;
4285                return false;
4286             }
4287             return true;
4288             
4289         });
4290         return r;
4291     }
4292     
4293     
4294 });
4295
4296  
4297 Roo.apply(Roo.bootstrap.NavGroup, {
4298     
4299     groups: {},
4300      /**
4301     * register a Navigation Group
4302     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4303     */
4304     register : function(navgrp)
4305     {
4306         this.groups[navgrp.navId] = navgrp;
4307         
4308     },
4309     /**
4310     * fetch a Navigation Group based on the navigation ID
4311     * @param {string} the navgroup to add
4312     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4313     */
4314     get: function(navId) {
4315         if (typeof(this.groups[navId]) == 'undefined') {
4316             return false;
4317             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4318         }
4319         return this.groups[navId] ;
4320     }
4321     
4322     
4323     
4324 });
4325
4326  /*
4327  * - LGPL
4328  *
4329  * row
4330  * 
4331  */
4332
4333 /**
4334  * @class Roo.bootstrap.NavItem
4335  * @extends Roo.bootstrap.Component
4336  * Bootstrap Navbar.NavItem class
4337  * @cfg {String} href  link to
4338  * @cfg {String} html content of button
4339  * @cfg {String} badge text inside badge
4340  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4341  * @cfg {String} glyphicon name of glyphicon
4342  * @cfg {String} icon name of font awesome icon
4343  * @cfg {Boolean} active Is item active
4344  * @cfg {Boolean} disabled Is item disabled
4345  
4346  * @cfg {Boolean} preventDefault (true | false) default false
4347  * @cfg {String} tabId the tab that this item activates.
4348  * @cfg {String} tagtype (a|span) render as a href or span?
4349  * @cfg {Boolean} animateRef (true|false) link to element default false  
4350   
4351  * @constructor
4352  * Create a new Navbar Item
4353  * @param {Object} config The config object
4354  */
4355 Roo.bootstrap.NavItem = function(config){
4356     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4357     this.addEvents({
4358         // raw events
4359         /**
4360          * @event click
4361          * The raw click event for the entire grid.
4362          * @param {Roo.EventObject} e
4363          */
4364         "click" : true,
4365          /**
4366             * @event changed
4367             * Fires when the active item active state changes
4368             * @param {Roo.bootstrap.NavItem} this
4369             * @param {boolean} state the new state
4370              
4371          */
4372         'changed': true,
4373         /**
4374             * @event scrollto
4375             * Fires when scroll to element
4376             * @param {Roo.bootstrap.NavItem} this
4377             * @param {Object} options
4378             * @param {Roo.EventObject} e
4379              
4380          */
4381         'scrollto': true
4382     });
4383    
4384 };
4385
4386 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4387     
4388     href: false,
4389     html: '',
4390     badge: '',
4391     icon: false,
4392     glyphicon: false,
4393     active: false,
4394     preventDefault : false,
4395     tabId : false,
4396     tagtype : 'a',
4397     disabled : false,
4398     animateRef : false,
4399     was_active : false,
4400     
4401     getAutoCreate : function(){
4402          
4403         var cfg = {
4404             tag: 'li',
4405             cls: 'nav-item'
4406             
4407         };
4408         
4409         if (this.active) {
4410             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4411         }
4412         if (this.disabled) {
4413             cfg.cls += ' disabled';
4414         }
4415         
4416         if (this.href || this.html || this.glyphicon || this.icon) {
4417             cfg.cn = [
4418                 {
4419                     tag: this.tagtype,
4420                     href : this.href || "#",
4421                     html: this.html || ''
4422                 }
4423             ];
4424             
4425             if (this.icon) {
4426                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4427             }
4428
4429             if(this.glyphicon) {
4430                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4431             }
4432             
4433             if (this.menu) {
4434                 
4435                 cfg.cn[0].html += " <span class='caret'></span>";
4436              
4437             }
4438             
4439             if (this.badge !== '') {
4440                  
4441                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4442             }
4443         }
4444         
4445         
4446         
4447         return cfg;
4448     },
4449     initEvents: function() 
4450     {
4451         if (typeof (this.menu) != 'undefined') {
4452             this.menu.parentType = this.xtype;
4453             this.menu.triggerEl = this.el;
4454             this.menu = this.addxtype(Roo.apply({}, this.menu));
4455         }
4456         
4457         this.el.select('a',true).on('click', this.onClick, this);
4458         
4459         if(this.tagtype == 'span'){
4460             this.el.select('span',true).on('click', this.onClick, this);
4461         }
4462        
4463         // at this point parent should be available..
4464         this.parent().register(this);
4465     },
4466     
4467     onClick : function(e)
4468     {
4469         if (e.getTarget('.dropdown-menu-item')) {
4470             // did you click on a menu itemm.... - then don't trigger onclick..
4471             return;
4472         }
4473         
4474         if(
4475                 this.preventDefault || 
4476                 this.href == '#' 
4477         ){
4478             Roo.log("NavItem - prevent Default?");
4479             e.preventDefault();
4480         }
4481         
4482         if (this.disabled) {
4483             return;
4484         }
4485         
4486         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4487         if (tg && tg.transition) {
4488             Roo.log("waiting for the transitionend");
4489             return;
4490         }
4491         
4492         
4493         
4494         //Roo.log("fire event clicked");
4495         if(this.fireEvent('click', this, e) === false){
4496             return;
4497         };
4498         
4499         if(this.tagtype == 'span'){
4500             return;
4501         }
4502         
4503         //Roo.log(this.href);
4504         var ael = this.el.select('a',true).first();
4505         //Roo.log(ael);
4506         
4507         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4508             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4509             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4510                 return; // ignore... - it's a 'hash' to another page.
4511             }
4512             Roo.log("NavItem - prevent Default?");
4513             e.preventDefault();
4514             this.scrollToElement(e);
4515         }
4516         
4517         
4518         var p =  this.parent();
4519    
4520         if (['tabs','pills'].indexOf(p.type)!==-1) {
4521             if (typeof(p.setActiveItem) !== 'undefined') {
4522                 p.setActiveItem(this);
4523             }
4524         }
4525         
4526         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4527         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4528             // remove the collapsed menu expand...
4529             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4530         }
4531     },
4532     
4533     isActive: function () {
4534         return this.active
4535     },
4536     setActive : function(state, fire, is_was_active)
4537     {
4538         if (this.active && !state && this.navId) {
4539             this.was_active = true;
4540             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4541             if (nv) {
4542                 nv.clearWasActive(this);
4543             }
4544             
4545         }
4546         this.active = state;
4547         
4548         if (!state ) {
4549             this.el.removeClass('active');
4550         } else if (!this.el.hasClass('active')) {
4551             this.el.addClass('active');
4552         }
4553         if (fire) {
4554             this.fireEvent('changed', this, state);
4555         }
4556         
4557         // show a panel if it's registered and related..
4558         
4559         if (!this.navId || !this.tabId || !state || is_was_active) {
4560             return;
4561         }
4562         
4563         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4564         if (!tg) {
4565             return;
4566         }
4567         var pan = tg.getPanelByName(this.tabId);
4568         if (!pan) {
4569             return;
4570         }
4571         // if we can not flip to new panel - go back to old nav highlight..
4572         if (false == tg.showPanel(pan)) {
4573             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4574             if (nv) {
4575                 var onav = nv.getWasActive();
4576                 if (onav) {
4577                     onav.setActive(true, false, true);
4578                 }
4579             }
4580             
4581         }
4582         
4583         
4584         
4585     },
4586      // this should not be here...
4587     setDisabled : function(state)
4588     {
4589         this.disabled = state;
4590         if (!state ) {
4591             this.el.removeClass('disabled');
4592         } else if (!this.el.hasClass('disabled')) {
4593             this.el.addClass('disabled');
4594         }
4595         
4596     },
4597     
4598     /**
4599      * Fetch the element to display the tooltip on.
4600      * @return {Roo.Element} defaults to this.el
4601      */
4602     tooltipEl : function()
4603     {
4604         return this.el.select('' + this.tagtype + '', true).first();
4605     },
4606     
4607     scrollToElement : function(e)
4608     {
4609         var c = document.body;
4610         
4611         /*
4612          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4613          */
4614         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4615             c = document.documentElement;
4616         }
4617         
4618         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4619         
4620         if(!target){
4621             return;
4622         }
4623
4624         var o = target.calcOffsetsTo(c);
4625         
4626         var options = {
4627             target : target,
4628             value : o[1]
4629         };
4630         
4631         this.fireEvent('scrollto', this, options, e);
4632         
4633         Roo.get(c).scrollTo('top', options.value, true);
4634         
4635         return;
4636     }
4637 });
4638  
4639
4640  /*
4641  * - LGPL
4642  *
4643  * sidebar item
4644  *
4645  *  li
4646  *    <span> icon </span>
4647  *    <span> text </span>
4648  *    <span>badge </span>
4649  */
4650
4651 /**
4652  * @class Roo.bootstrap.NavSidebarItem
4653  * @extends Roo.bootstrap.NavItem
4654  * Bootstrap Navbar.NavSidebarItem class
4655  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4656  * {Boolean} open is the menu open
4657  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4658  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4659  * {String} buttonSize (sm|md|lg)the extra classes for the button
4660  * {Boolean} showArrow show arrow next to the text (default true)
4661  * @constructor
4662  * Create a new Navbar Button
4663  * @param {Object} config The config object
4664  */
4665 Roo.bootstrap.NavSidebarItem = function(config){
4666     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4667     this.addEvents({
4668         // raw events
4669         /**
4670          * @event click
4671          * The raw click event for the entire grid.
4672          * @param {Roo.EventObject} e
4673          */
4674         "click" : true,
4675          /**
4676             * @event changed
4677             * Fires when the active item active state changes
4678             * @param {Roo.bootstrap.NavSidebarItem} this
4679             * @param {boolean} state the new state
4680              
4681          */
4682         'changed': true
4683     });
4684    
4685 };
4686
4687 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4688     
4689     badgeWeight : 'default',
4690     
4691     open: false,
4692     
4693     buttonView : false,
4694     
4695     buttonWeight : 'default',
4696     
4697     buttonSize : 'md',
4698     
4699     showArrow : true,
4700     
4701     getAutoCreate : function(){
4702         
4703         
4704         var a = {
4705                 tag: 'a',
4706                 href : this.href || '#',
4707                 cls: '',
4708                 html : '',
4709                 cn : []
4710         };
4711         
4712         if(this.buttonView){
4713             a = {
4714                 tag: 'button',
4715                 href : this.href || '#',
4716                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4717                 html : this.html,
4718                 cn : []
4719             };
4720         }
4721         
4722         var cfg = {
4723             tag: 'li',
4724             cls: '',
4725             cn: [ a ]
4726         };
4727         
4728         if (this.active) {
4729             cfg.cls += ' active';
4730         }
4731         
4732         if (this.disabled) {
4733             cfg.cls += ' disabled';
4734         }
4735         if (this.open) {
4736             cfg.cls += ' open x-open';
4737         }
4738         // left icon..
4739         if (this.glyphicon || this.icon) {
4740             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4741             a.cn.push({ tag : 'i', cls : c }) ;
4742         }
4743         
4744         if(!this.buttonView){
4745             var span = {
4746                 tag: 'span',
4747                 html : this.html || ''
4748             };
4749
4750             a.cn.push(span);
4751             
4752         }
4753         
4754         if (this.badge !== '') {
4755             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4756         }
4757         
4758         if (this.menu) {
4759             
4760             if(this.showArrow){
4761                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4762             }
4763             
4764             a.cls += ' dropdown-toggle treeview' ;
4765         }
4766         
4767         return cfg;
4768     },
4769     
4770     initEvents : function()
4771     { 
4772         if (typeof (this.menu) != 'undefined') {
4773             this.menu.parentType = this.xtype;
4774             this.menu.triggerEl = this.el;
4775             this.menu = this.addxtype(Roo.apply({}, this.menu));
4776         }
4777         
4778         this.el.on('click', this.onClick, this);
4779         
4780         if(this.badge !== ''){
4781             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4782         }
4783         
4784     },
4785     
4786     onClick : function(e)
4787     {
4788         if(this.disabled){
4789             e.preventDefault();
4790             return;
4791         }
4792         
4793         if(this.preventDefault){
4794             e.preventDefault();
4795         }
4796         
4797         this.fireEvent('click', this);
4798     },
4799     
4800     disable : function()
4801     {
4802         this.setDisabled(true);
4803     },
4804     
4805     enable : function()
4806     {
4807         this.setDisabled(false);
4808     },
4809     
4810     setDisabled : function(state)
4811     {
4812         if(this.disabled == state){
4813             return;
4814         }
4815         
4816         this.disabled = state;
4817         
4818         if (state) {
4819             this.el.addClass('disabled');
4820             return;
4821         }
4822         
4823         this.el.removeClass('disabled');
4824         
4825         return;
4826     },
4827     
4828     setActive : function(state)
4829     {
4830         if(this.active == state){
4831             return;
4832         }
4833         
4834         this.active = state;
4835         
4836         if (state) {
4837             this.el.addClass('active');
4838             return;
4839         }
4840         
4841         this.el.removeClass('active');
4842         
4843         return;
4844     },
4845     
4846     isActive: function () 
4847     {
4848         return this.active;
4849     },
4850     
4851     setBadge : function(str)
4852     {
4853         if(!this.badgeEl){
4854             return;
4855         }
4856         
4857         this.badgeEl.dom.innerHTML = str;
4858     }
4859     
4860    
4861      
4862  
4863 });
4864  
4865
4866  /*
4867  * - LGPL
4868  *
4869  * row
4870  * 
4871  */
4872
4873 /**
4874  * @class Roo.bootstrap.Row
4875  * @extends Roo.bootstrap.Component
4876  * Bootstrap Row class (contains columns...)
4877  * 
4878  * @constructor
4879  * Create a new Row
4880  * @param {Object} config The config object
4881  */
4882
4883 Roo.bootstrap.Row = function(config){
4884     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4885 };
4886
4887 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4888     
4889     getAutoCreate : function(){
4890        return {
4891             cls: 'row clearfix'
4892        };
4893     }
4894     
4895     
4896 });
4897
4898  
4899
4900  /*
4901  * - LGPL
4902  *
4903  * element
4904  * 
4905  */
4906
4907 /**
4908  * @class Roo.bootstrap.Element
4909  * @extends Roo.bootstrap.Component
4910  * Bootstrap Element class
4911  * @cfg {String} html contents of the element
4912  * @cfg {String} tag tag of the element
4913  * @cfg {String} cls class of the element
4914  * @cfg {Boolean} preventDefault (true|false) default false
4915  * @cfg {Boolean} clickable (true|false) default false
4916  * 
4917  * @constructor
4918  * Create a new Element
4919  * @param {Object} config The config object
4920  */
4921
4922 Roo.bootstrap.Element = function(config){
4923     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4924     
4925     this.addEvents({
4926         // raw events
4927         /**
4928          * @event click
4929          * When a element is chick
4930          * @param {Roo.bootstrap.Element} this
4931          * @param {Roo.EventObject} e
4932          */
4933         "click" : true
4934     });
4935 };
4936
4937 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4938     
4939     tag: 'div',
4940     cls: '',
4941     html: '',
4942     preventDefault: false, 
4943     clickable: false,
4944     
4945     getAutoCreate : function(){
4946         
4947         var cfg = {
4948             tag: this.tag,
4949             // cls: this.cls, double assign in parent class Component.js :: onRender
4950             html: this.html
4951         };
4952         
4953         return cfg;
4954     },
4955     
4956     initEvents: function() 
4957     {
4958         Roo.bootstrap.Element.superclass.initEvents.call(this);
4959         
4960         if(this.clickable){
4961             this.el.on('click', this.onClick, this);
4962         }
4963         
4964     },
4965     
4966     onClick : function(e)
4967     {
4968         if(this.preventDefault){
4969             e.preventDefault();
4970         }
4971         
4972         this.fireEvent('click', this, e);
4973     },
4974     
4975     getValue : function()
4976     {
4977         return this.el.dom.innerHTML;
4978     },
4979     
4980     setValue : function(value)
4981     {
4982         this.el.dom.innerHTML = value;
4983     }
4984    
4985 });
4986
4987  
4988
4989  /*
4990  * - LGPL
4991  *
4992  * pagination
4993  * 
4994  */
4995
4996 /**
4997  * @class Roo.bootstrap.Pagination
4998  * @extends Roo.bootstrap.Component
4999  * Bootstrap Pagination class
5000  * @cfg {String} size xs | sm | md | lg
5001  * @cfg {Boolean} inverse false | true
5002  * 
5003  * @constructor
5004  * Create a new Pagination
5005  * @param {Object} config The config object
5006  */
5007
5008 Roo.bootstrap.Pagination = function(config){
5009     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5010 };
5011
5012 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5013     
5014     cls: false,
5015     size: false,
5016     inverse: false,
5017     
5018     getAutoCreate : function(){
5019         var cfg = {
5020             tag: 'ul',
5021                 cls: 'pagination'
5022         };
5023         if (this.inverse) {
5024             cfg.cls += ' inverse';
5025         }
5026         if (this.html) {
5027             cfg.html=this.html;
5028         }
5029         if (this.cls) {
5030             cfg.cls += " " + this.cls;
5031         }
5032         return cfg;
5033     }
5034    
5035 });
5036
5037  
5038
5039  /*
5040  * - LGPL
5041  *
5042  * Pagination item
5043  * 
5044  */
5045
5046
5047 /**
5048  * @class Roo.bootstrap.PaginationItem
5049  * @extends Roo.bootstrap.Component
5050  * Bootstrap PaginationItem class
5051  * @cfg {String} html text
5052  * @cfg {String} href the link
5053  * @cfg {Boolean} preventDefault (true | false) default true
5054  * @cfg {Boolean} active (true | false) default false
5055  * @cfg {Boolean} disabled default false
5056  * 
5057  * 
5058  * @constructor
5059  * Create a new PaginationItem
5060  * @param {Object} config The config object
5061  */
5062
5063
5064 Roo.bootstrap.PaginationItem = function(config){
5065     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5066     this.addEvents({
5067         // raw events
5068         /**
5069          * @event click
5070          * The raw click event for the entire grid.
5071          * @param {Roo.EventObject} e
5072          */
5073         "click" : true
5074     });
5075 };
5076
5077 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5078     
5079     href : false,
5080     html : false,
5081     preventDefault: true,
5082     active : false,
5083     cls : false,
5084     disabled: false,
5085     
5086     getAutoCreate : function(){
5087         var cfg= {
5088             tag: 'li',
5089             cn: [
5090                 {
5091                     tag : 'a',
5092                     href : this.href ? this.href : '#',
5093                     html : this.html ? this.html : ''
5094                 }
5095             ]
5096         };
5097         
5098         if(this.cls){
5099             cfg.cls = this.cls;
5100         }
5101         
5102         if(this.disabled){
5103             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5104         }
5105         
5106         if(this.active){
5107             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5108         }
5109         
5110         return cfg;
5111     },
5112     
5113     initEvents: function() {
5114         
5115         this.el.on('click', this.onClick, this);
5116         
5117     },
5118     onClick : function(e)
5119     {
5120         Roo.log('PaginationItem on click ');
5121         if(this.preventDefault){
5122             e.preventDefault();
5123         }
5124         
5125         if(this.disabled){
5126             return;
5127         }
5128         
5129         this.fireEvent('click', this, e);
5130     }
5131    
5132 });
5133
5134  
5135
5136  /*
5137  * - LGPL
5138  *
5139  * slider
5140  * 
5141  */
5142
5143
5144 /**
5145  * @class Roo.bootstrap.Slider
5146  * @extends Roo.bootstrap.Component
5147  * Bootstrap Slider class
5148  *    
5149  * @constructor
5150  * Create a new Slider
5151  * @param {Object} config The config object
5152  */
5153
5154 Roo.bootstrap.Slider = function(config){
5155     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5156 };
5157
5158 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5159     
5160     getAutoCreate : function(){
5161         
5162         var cfg = {
5163             tag: 'div',
5164             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5165             cn: [
5166                 {
5167                     tag: 'a',
5168                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5169                 }
5170             ]
5171         };
5172         
5173         return cfg;
5174     }
5175    
5176 });
5177
5178  /*
5179  * Based on:
5180  * Ext JS Library 1.1.1
5181  * Copyright(c) 2006-2007, Ext JS, LLC.
5182  *
5183  * Originally Released Under LGPL - original licence link has changed is not relivant.
5184  *
5185  * Fork - LGPL
5186  * <script type="text/javascript">
5187  */
5188  
5189
5190 /**
5191  * @class Roo.grid.ColumnModel
5192  * @extends Roo.util.Observable
5193  * This is the default implementation of a ColumnModel used by the Grid. It defines
5194  * the columns in the grid.
5195  * <br>Usage:<br>
5196  <pre><code>
5197  var colModel = new Roo.grid.ColumnModel([
5198         {header: "Ticker", width: 60, sortable: true, locked: true},
5199         {header: "Company Name", width: 150, sortable: true},
5200         {header: "Market Cap.", width: 100, sortable: true},
5201         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5202         {header: "Employees", width: 100, sortable: true, resizable: false}
5203  ]);
5204  </code></pre>
5205  * <p>
5206  
5207  * The config options listed for this class are options which may appear in each
5208  * individual column definition.
5209  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5210  * @constructor
5211  * @param {Object} config An Array of column config objects. See this class's
5212  * config objects for details.
5213 */
5214 Roo.grid.ColumnModel = function(config){
5215         /**
5216      * The config passed into the constructor
5217      */
5218     this.config = config;
5219     this.lookup = {};
5220
5221     // if no id, create one
5222     // if the column does not have a dataIndex mapping,
5223     // map it to the order it is in the config
5224     for(var i = 0, len = config.length; i < len; i++){
5225         var c = config[i];
5226         if(typeof c.dataIndex == "undefined"){
5227             c.dataIndex = i;
5228         }
5229         if(typeof c.renderer == "string"){
5230             c.renderer = Roo.util.Format[c.renderer];
5231         }
5232         if(typeof c.id == "undefined"){
5233             c.id = Roo.id();
5234         }
5235         if(c.editor && c.editor.xtype){
5236             c.editor  = Roo.factory(c.editor, Roo.grid);
5237         }
5238         if(c.editor && c.editor.isFormField){
5239             c.editor = new Roo.grid.GridEditor(c.editor);
5240         }
5241         this.lookup[c.id] = c;
5242     }
5243
5244     /**
5245      * The width of columns which have no width specified (defaults to 100)
5246      * @type Number
5247      */
5248     this.defaultWidth = 100;
5249
5250     /**
5251      * Default sortable of columns which have no sortable specified (defaults to false)
5252      * @type Boolean
5253      */
5254     this.defaultSortable = false;
5255
5256     this.addEvents({
5257         /**
5258              * @event widthchange
5259              * Fires when the width of a column changes.
5260              * @param {ColumnModel} this
5261              * @param {Number} columnIndex The column index
5262              * @param {Number} newWidth The new width
5263              */
5264             "widthchange": true,
5265         /**
5266              * @event headerchange
5267              * Fires when the text of a header changes.
5268              * @param {ColumnModel} this
5269              * @param {Number} columnIndex The column index
5270              * @param {Number} newText The new header text
5271              */
5272             "headerchange": true,
5273         /**
5274              * @event hiddenchange
5275              * Fires when a column is hidden or "unhidden".
5276              * @param {ColumnModel} this
5277              * @param {Number} columnIndex The column index
5278              * @param {Boolean} hidden true if hidden, false otherwise
5279              */
5280             "hiddenchange": true,
5281             /**
5282          * @event columnmoved
5283          * Fires when a column is moved.
5284          * @param {ColumnModel} this
5285          * @param {Number} oldIndex
5286          * @param {Number} newIndex
5287          */
5288         "columnmoved" : true,
5289         /**
5290          * @event columlockchange
5291          * Fires when a column's locked state is changed
5292          * @param {ColumnModel} this
5293          * @param {Number} colIndex
5294          * @param {Boolean} locked true if locked
5295          */
5296         "columnlockchange" : true
5297     });
5298     Roo.grid.ColumnModel.superclass.constructor.call(this);
5299 };
5300 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5301     /**
5302      * @cfg {String} header The header text to display in the Grid view.
5303      */
5304     /**
5305      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5306      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5307      * specified, the column's index is used as an index into the Record's data Array.
5308      */
5309     /**
5310      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5311      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5312      */
5313     /**
5314      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5315      * Defaults to the value of the {@link #defaultSortable} property.
5316      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5317      */
5318     /**
5319      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5320      */
5321     /**
5322      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5323      */
5324     /**
5325      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5326      */
5327     /**
5328      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5329      */
5330     /**
5331      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5332      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5333      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5334      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5335      */
5336        /**
5337      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5338      */
5339     /**
5340      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5341      */
5342     /**
5343      * @cfg {String} cursor (Optional)
5344      */
5345     /**
5346      * @cfg {String} tooltip (Optional)
5347      */
5348     /**
5349      * @cfg {Number} xs (Optional)
5350      */
5351     /**
5352      * @cfg {Number} sm (Optional)
5353      */
5354     /**
5355      * @cfg {Number} md (Optional)
5356      */
5357     /**
5358      * @cfg {Number} lg (Optional)
5359      */
5360     /**
5361      * Returns the id of the column at the specified index.
5362      * @param {Number} index The column index
5363      * @return {String} the id
5364      */
5365     getColumnId : function(index){
5366         return this.config[index].id;
5367     },
5368
5369     /**
5370      * Returns the column for a specified id.
5371      * @param {String} id The column id
5372      * @return {Object} the column
5373      */
5374     getColumnById : function(id){
5375         return this.lookup[id];
5376     },
5377
5378     
5379     /**
5380      * Returns the column for a specified dataIndex.
5381      * @param {String} dataIndex The column dataIndex
5382      * @return {Object|Boolean} the column or false if not found
5383      */
5384     getColumnByDataIndex: function(dataIndex){
5385         var index = this.findColumnIndex(dataIndex);
5386         return index > -1 ? this.config[index] : false;
5387     },
5388     
5389     /**
5390      * Returns the index for a specified column id.
5391      * @param {String} id The column id
5392      * @return {Number} the index, or -1 if not found
5393      */
5394     getIndexById : function(id){
5395         for(var i = 0, len = this.config.length; i < len; i++){
5396             if(this.config[i].id == id){
5397                 return i;
5398             }
5399         }
5400         return -1;
5401     },
5402     
5403     /**
5404      * Returns the index for a specified column dataIndex.
5405      * @param {String} dataIndex The column dataIndex
5406      * @return {Number} the index, or -1 if not found
5407      */
5408     
5409     findColumnIndex : function(dataIndex){
5410         for(var i = 0, len = this.config.length; i < len; i++){
5411             if(this.config[i].dataIndex == dataIndex){
5412                 return i;
5413             }
5414         }
5415         return -1;
5416     },
5417     
5418     
5419     moveColumn : function(oldIndex, newIndex){
5420         var c = this.config[oldIndex];
5421         this.config.splice(oldIndex, 1);
5422         this.config.splice(newIndex, 0, c);
5423         this.dataMap = null;
5424         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5425     },
5426
5427     isLocked : function(colIndex){
5428         return this.config[colIndex].locked === true;
5429     },
5430
5431     setLocked : function(colIndex, value, suppressEvent){
5432         if(this.isLocked(colIndex) == value){
5433             return;
5434         }
5435         this.config[colIndex].locked = value;
5436         if(!suppressEvent){
5437             this.fireEvent("columnlockchange", this, colIndex, value);
5438         }
5439     },
5440
5441     getTotalLockedWidth : function(){
5442         var totalWidth = 0;
5443         for(var i = 0; i < this.config.length; i++){
5444             if(this.isLocked(i) && !this.isHidden(i)){
5445                 this.totalWidth += this.getColumnWidth(i);
5446             }
5447         }
5448         return totalWidth;
5449     },
5450
5451     getLockedCount : function(){
5452         for(var i = 0, len = this.config.length; i < len; i++){
5453             if(!this.isLocked(i)){
5454                 return i;
5455             }
5456         }
5457         
5458         return this.config.length;
5459     },
5460
5461     /**
5462      * Returns the number of columns.
5463      * @return {Number}
5464      */
5465     getColumnCount : function(visibleOnly){
5466         if(visibleOnly === true){
5467             var c = 0;
5468             for(var i = 0, len = this.config.length; i < len; i++){
5469                 if(!this.isHidden(i)){
5470                     c++;
5471                 }
5472             }
5473             return c;
5474         }
5475         return this.config.length;
5476     },
5477
5478     /**
5479      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5480      * @param {Function} fn
5481      * @param {Object} scope (optional)
5482      * @return {Array} result
5483      */
5484     getColumnsBy : function(fn, scope){
5485         var r = [];
5486         for(var i = 0, len = this.config.length; i < len; i++){
5487             var c = this.config[i];
5488             if(fn.call(scope||this, c, i) === true){
5489                 r[r.length] = c;
5490             }
5491         }
5492         return r;
5493     },
5494
5495     /**
5496      * Returns true if the specified column is sortable.
5497      * @param {Number} col The column index
5498      * @return {Boolean}
5499      */
5500     isSortable : function(col){
5501         if(typeof this.config[col].sortable == "undefined"){
5502             return this.defaultSortable;
5503         }
5504         return this.config[col].sortable;
5505     },
5506
5507     /**
5508      * Returns the rendering (formatting) function defined for the column.
5509      * @param {Number} col The column index.
5510      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5511      */
5512     getRenderer : function(col){
5513         if(!this.config[col].renderer){
5514             return Roo.grid.ColumnModel.defaultRenderer;
5515         }
5516         return this.config[col].renderer;
5517     },
5518
5519     /**
5520      * Sets the rendering (formatting) function for a column.
5521      * @param {Number} col The column index
5522      * @param {Function} fn The function to use to process the cell's raw data
5523      * to return HTML markup for the grid view. The render function is called with
5524      * the following parameters:<ul>
5525      * <li>Data value.</li>
5526      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5527      * <li>css A CSS style string to apply to the table cell.</li>
5528      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5529      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5530      * <li>Row index</li>
5531      * <li>Column index</li>
5532      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5533      */
5534     setRenderer : function(col, fn){
5535         this.config[col].renderer = fn;
5536     },
5537
5538     /**
5539      * Returns the width for the specified column.
5540      * @param {Number} col The column index
5541      * @return {Number}
5542      */
5543     getColumnWidth : function(col){
5544         return this.config[col].width * 1 || this.defaultWidth;
5545     },
5546
5547     /**
5548      * Sets the width for a column.
5549      * @param {Number} col The column index
5550      * @param {Number} width The new width
5551      */
5552     setColumnWidth : function(col, width, suppressEvent){
5553         this.config[col].width = width;
5554         this.totalWidth = null;
5555         if(!suppressEvent){
5556              this.fireEvent("widthchange", this, col, width);
5557         }
5558     },
5559
5560     /**
5561      * Returns the total width of all columns.
5562      * @param {Boolean} includeHidden True to include hidden column widths
5563      * @return {Number}
5564      */
5565     getTotalWidth : function(includeHidden){
5566         if(!this.totalWidth){
5567             this.totalWidth = 0;
5568             for(var i = 0, len = this.config.length; i < len; i++){
5569                 if(includeHidden || !this.isHidden(i)){
5570                     this.totalWidth += this.getColumnWidth(i);
5571                 }
5572             }
5573         }
5574         return this.totalWidth;
5575     },
5576
5577     /**
5578      * Returns the header for the specified column.
5579      * @param {Number} col The column index
5580      * @return {String}
5581      */
5582     getColumnHeader : function(col){
5583         return this.config[col].header;
5584     },
5585
5586     /**
5587      * Sets the header for a column.
5588      * @param {Number} col The column index
5589      * @param {String} header The new header
5590      */
5591     setColumnHeader : function(col, header){
5592         this.config[col].header = header;
5593         this.fireEvent("headerchange", this, col, header);
5594     },
5595
5596     /**
5597      * Returns the tooltip for the specified column.
5598      * @param {Number} col The column index
5599      * @return {String}
5600      */
5601     getColumnTooltip : function(col){
5602             return this.config[col].tooltip;
5603     },
5604     /**
5605      * Sets the tooltip for a column.
5606      * @param {Number} col The column index
5607      * @param {String} tooltip The new tooltip
5608      */
5609     setColumnTooltip : function(col, tooltip){
5610             this.config[col].tooltip = tooltip;
5611     },
5612
5613     /**
5614      * Returns the dataIndex for the specified column.
5615      * @param {Number} col The column index
5616      * @return {Number}
5617      */
5618     getDataIndex : function(col){
5619         return this.config[col].dataIndex;
5620     },
5621
5622     /**
5623      * Sets the dataIndex for a column.
5624      * @param {Number} col The column index
5625      * @param {Number} dataIndex The new dataIndex
5626      */
5627     setDataIndex : function(col, dataIndex){
5628         this.config[col].dataIndex = dataIndex;
5629     },
5630
5631     
5632     
5633     /**
5634      * Returns true if the cell is editable.
5635      * @param {Number} colIndex The column index
5636      * @param {Number} rowIndex The row index - this is nto actually used..?
5637      * @return {Boolean}
5638      */
5639     isCellEditable : function(colIndex, rowIndex){
5640         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5641     },
5642
5643     /**
5644      * Returns the editor defined for the cell/column.
5645      * return false or null to disable editing.
5646      * @param {Number} colIndex The column index
5647      * @param {Number} rowIndex The row index
5648      * @return {Object}
5649      */
5650     getCellEditor : function(colIndex, rowIndex){
5651         return this.config[colIndex].editor;
5652     },
5653
5654     /**
5655      * Sets if a column is editable.
5656      * @param {Number} col The column index
5657      * @param {Boolean} editable True if the column is editable
5658      */
5659     setEditable : function(col, editable){
5660         this.config[col].editable = editable;
5661     },
5662
5663
5664     /**
5665      * Returns true if the column is hidden.
5666      * @param {Number} colIndex The column index
5667      * @return {Boolean}
5668      */
5669     isHidden : function(colIndex){
5670         return this.config[colIndex].hidden;
5671     },
5672
5673
5674     /**
5675      * Returns true if the column width cannot be changed
5676      */
5677     isFixed : function(colIndex){
5678         return this.config[colIndex].fixed;
5679     },
5680
5681     /**
5682      * Returns true if the column can be resized
5683      * @return {Boolean}
5684      */
5685     isResizable : function(colIndex){
5686         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5687     },
5688     /**
5689      * Sets if a column is hidden.
5690      * @param {Number} colIndex The column index
5691      * @param {Boolean} hidden True if the column is hidden
5692      */
5693     setHidden : function(colIndex, hidden){
5694         this.config[colIndex].hidden = hidden;
5695         this.totalWidth = null;
5696         this.fireEvent("hiddenchange", this, colIndex, hidden);
5697     },
5698
5699     /**
5700      * Sets the editor for a column.
5701      * @param {Number} col The column index
5702      * @param {Object} editor The editor object
5703      */
5704     setEditor : function(col, editor){
5705         this.config[col].editor = editor;
5706     }
5707 });
5708
5709 Roo.grid.ColumnModel.defaultRenderer = function(value)
5710 {
5711     if(typeof value == "object") {
5712         return value;
5713     }
5714         if(typeof value == "string" && value.length < 1){
5715             return "&#160;";
5716         }
5717     
5718         return String.format("{0}", value);
5719 };
5720
5721 // Alias for backwards compatibility
5722 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5723 /*
5724  * Based on:
5725  * Ext JS Library 1.1.1
5726  * Copyright(c) 2006-2007, Ext JS, LLC.
5727  *
5728  * Originally Released Under LGPL - original licence link has changed is not relivant.
5729  *
5730  * Fork - LGPL
5731  * <script type="text/javascript">
5732  */
5733  
5734 /**
5735  * @class Roo.LoadMask
5736  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5737  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5738  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5739  * element's UpdateManager load indicator and will be destroyed after the initial load.
5740  * @constructor
5741  * Create a new LoadMask
5742  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5743  * @param {Object} config The config object
5744  */
5745 Roo.LoadMask = function(el, config){
5746     this.el = Roo.get(el);
5747     Roo.apply(this, config);
5748     if(this.store){
5749         this.store.on('beforeload', this.onBeforeLoad, this);
5750         this.store.on('load', this.onLoad, this);
5751         this.store.on('loadexception', this.onLoadException, this);
5752         this.removeMask = false;
5753     }else{
5754         var um = this.el.getUpdateManager();
5755         um.showLoadIndicator = false; // disable the default indicator
5756         um.on('beforeupdate', this.onBeforeLoad, this);
5757         um.on('update', this.onLoad, this);
5758         um.on('failure', this.onLoad, this);
5759         this.removeMask = true;
5760     }
5761 };
5762
5763 Roo.LoadMask.prototype = {
5764     /**
5765      * @cfg {Boolean} removeMask
5766      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5767      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5768      */
5769     /**
5770      * @cfg {String} msg
5771      * The text to display in a centered loading message box (defaults to 'Loading...')
5772      */
5773     msg : 'Loading...',
5774     /**
5775      * @cfg {String} msgCls
5776      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5777      */
5778     msgCls : 'x-mask-loading',
5779
5780     /**
5781      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5782      * @type Boolean
5783      */
5784     disabled: false,
5785
5786     /**
5787      * Disables the mask to prevent it from being displayed
5788      */
5789     disable : function(){
5790        this.disabled = true;
5791     },
5792
5793     /**
5794      * Enables the mask so that it can be displayed
5795      */
5796     enable : function(){
5797         this.disabled = false;
5798     },
5799     
5800     onLoadException : function()
5801     {
5802         Roo.log(arguments);
5803         
5804         if (typeof(arguments[3]) != 'undefined') {
5805             Roo.MessageBox.alert("Error loading",arguments[3]);
5806         } 
5807         /*
5808         try {
5809             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5810                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5811             }   
5812         } catch(e) {
5813             
5814         }
5815         */
5816     
5817         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5818     },
5819     // private
5820     onLoad : function()
5821     {
5822         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5823     },
5824
5825     // private
5826     onBeforeLoad : function(){
5827         if(!this.disabled){
5828             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5829         }
5830     },
5831
5832     // private
5833     destroy : function(){
5834         if(this.store){
5835             this.store.un('beforeload', this.onBeforeLoad, this);
5836             this.store.un('load', this.onLoad, this);
5837             this.store.un('loadexception', this.onLoadException, this);
5838         }else{
5839             var um = this.el.getUpdateManager();
5840             um.un('beforeupdate', this.onBeforeLoad, this);
5841             um.un('update', this.onLoad, this);
5842             um.un('failure', this.onLoad, this);
5843         }
5844     }
5845 };/*
5846  * - LGPL
5847  *
5848  * table
5849  * 
5850  */
5851
5852 /**
5853  * @class Roo.bootstrap.Table
5854  * @extends Roo.bootstrap.Component
5855  * Bootstrap Table class
5856  * @cfg {String} cls table class
5857  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5858  * @cfg {String} bgcolor Specifies the background color for a table
5859  * @cfg {Number} border Specifies whether the table cells should have borders or not
5860  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5861  * @cfg {Number} cellspacing Specifies the space between cells
5862  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5863  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5864  * @cfg {String} sortable Specifies that the table should be sortable
5865  * @cfg {String} summary Specifies a summary of the content of a table
5866  * @cfg {Number} width Specifies the width of a table
5867  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5868  * 
5869  * @cfg {boolean} striped Should the rows be alternative striped
5870  * @cfg {boolean} bordered Add borders to the table
5871  * @cfg {boolean} hover Add hover highlighting
5872  * @cfg {boolean} condensed Format condensed
5873  * @cfg {boolean} responsive Format condensed
5874  * @cfg {Boolean} loadMask (true|false) default false
5875  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5876  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5877  * @cfg {Boolean} rowSelection (true|false) default false
5878  * @cfg {Boolean} cellSelection (true|false) default false
5879  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5880  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5881  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5882  
5883  * 
5884  * @constructor
5885  * Create a new Table
5886  * @param {Object} config The config object
5887  */
5888
5889 Roo.bootstrap.Table = function(config){
5890     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5891     
5892   
5893     
5894     // BC...
5895     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5896     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5897     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5898     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5899     
5900     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5901     if (this.sm) {
5902         this.sm.grid = this;
5903         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5904         this.sm = this.selModel;
5905         this.sm.xmodule = this.xmodule || false;
5906     }
5907     
5908     if (this.cm && typeof(this.cm.config) == 'undefined') {
5909         this.colModel = new Roo.grid.ColumnModel(this.cm);
5910         this.cm = this.colModel;
5911         this.cm.xmodule = this.xmodule || false;
5912     }
5913     if (this.store) {
5914         this.store= Roo.factory(this.store, Roo.data);
5915         this.ds = this.store;
5916         this.ds.xmodule = this.xmodule || false;
5917          
5918     }
5919     if (this.footer && this.store) {
5920         this.footer.dataSource = this.ds;
5921         this.footer = Roo.factory(this.footer);
5922     }
5923     
5924     /** @private */
5925     this.addEvents({
5926         /**
5927          * @event cellclick
5928          * Fires when a cell is clicked
5929          * @param {Roo.bootstrap.Table} this
5930          * @param {Roo.Element} el
5931          * @param {Number} rowIndex
5932          * @param {Number} columnIndex
5933          * @param {Roo.EventObject} e
5934          */
5935         "cellclick" : true,
5936         /**
5937          * @event celldblclick
5938          * Fires when a cell is double clicked
5939          * @param {Roo.bootstrap.Table} this
5940          * @param {Roo.Element} el
5941          * @param {Number} rowIndex
5942          * @param {Number} columnIndex
5943          * @param {Roo.EventObject} e
5944          */
5945         "celldblclick" : true,
5946         /**
5947          * @event rowclick
5948          * Fires when a row is clicked
5949          * @param {Roo.bootstrap.Table} this
5950          * @param {Roo.Element} el
5951          * @param {Number} rowIndex
5952          * @param {Roo.EventObject} e
5953          */
5954         "rowclick" : true,
5955         /**
5956          * @event rowdblclick
5957          * Fires when a row is double clicked
5958          * @param {Roo.bootstrap.Table} this
5959          * @param {Roo.Element} el
5960          * @param {Number} rowIndex
5961          * @param {Roo.EventObject} e
5962          */
5963         "rowdblclick" : true,
5964         /**
5965          * @event mouseover
5966          * Fires when a mouseover occur
5967          * @param {Roo.bootstrap.Table} this
5968          * @param {Roo.Element} el
5969          * @param {Number} rowIndex
5970          * @param {Number} columnIndex
5971          * @param {Roo.EventObject} e
5972          */
5973         "mouseover" : true,
5974         /**
5975          * @event mouseout
5976          * Fires when a mouseout occur
5977          * @param {Roo.bootstrap.Table} this
5978          * @param {Roo.Element} el
5979          * @param {Number} rowIndex
5980          * @param {Number} columnIndex
5981          * @param {Roo.EventObject} e
5982          */
5983         "mouseout" : true,
5984         /**
5985          * @event rowclass
5986          * Fires when a row is rendered, so you can change add a style to it.
5987          * @param {Roo.bootstrap.Table} this
5988          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5989          */
5990         'rowclass' : true,
5991           /**
5992          * @event rowsrendered
5993          * Fires when all the  rows have been rendered
5994          * @param {Roo.bootstrap.Table} this
5995          */
5996         'rowsrendered' : true,
5997         /**
5998          * @event contextmenu
5999          * The raw contextmenu event for the entire grid.
6000          * @param {Roo.EventObject} e
6001          */
6002         "contextmenu" : true,
6003         /**
6004          * @event rowcontextmenu
6005          * Fires when a row is right clicked
6006          * @param {Roo.bootstrap.Table} this
6007          * @param {Number} rowIndex
6008          * @param {Roo.EventObject} e
6009          */
6010         "rowcontextmenu" : true,
6011         /**
6012          * @event cellcontextmenu
6013          * Fires when a cell is right clicked
6014          * @param {Roo.bootstrap.Table} this
6015          * @param {Number} rowIndex
6016          * @param {Number} cellIndex
6017          * @param {Roo.EventObject} e
6018          */
6019          "cellcontextmenu" : true,
6020          /**
6021          * @event headercontextmenu
6022          * Fires when a header is right clicked
6023          * @param {Roo.bootstrap.Table} this
6024          * @param {Number} columnIndex
6025          * @param {Roo.EventObject} e
6026          */
6027         "headercontextmenu" : true
6028     });
6029 };
6030
6031 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6032     
6033     cls: false,
6034     align: false,
6035     bgcolor: false,
6036     border: false,
6037     cellpadding: false,
6038     cellspacing: false,
6039     frame: false,
6040     rules: false,
6041     sortable: false,
6042     summary: false,
6043     width: false,
6044     striped : false,
6045     scrollBody : false,
6046     bordered: false,
6047     hover:  false,
6048     condensed : false,
6049     responsive : false,
6050     sm : false,
6051     cm : false,
6052     store : false,
6053     loadMask : false,
6054     footerShow : true,
6055     headerShow : true,
6056   
6057     rowSelection : false,
6058     cellSelection : false,
6059     layout : false,
6060     
6061     // Roo.Element - the tbody
6062     mainBody: false,
6063     // Roo.Element - thead element
6064     mainHead: false,
6065     
6066     container: false, // used by gridpanel...
6067     
6068     lazyLoad : false,
6069     
6070     getAutoCreate : function()
6071     {
6072         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6073         
6074         cfg = {
6075             tag: 'table',
6076             cls : 'table',
6077             cn : []
6078         };
6079         if (this.scrollBody) {
6080             cfg.cls += ' table-body-fixed';
6081         }    
6082         if (this.striped) {
6083             cfg.cls += ' table-striped';
6084         }
6085         
6086         if (this.hover) {
6087             cfg.cls += ' table-hover';
6088         }
6089         if (this.bordered) {
6090             cfg.cls += ' table-bordered';
6091         }
6092         if (this.condensed) {
6093             cfg.cls += ' table-condensed';
6094         }
6095         if (this.responsive) {
6096             cfg.cls += ' table-responsive';
6097         }
6098         
6099         if (this.cls) {
6100             cfg.cls+=  ' ' +this.cls;
6101         }
6102         
6103         // this lot should be simplifed...
6104         
6105         if (this.align) {
6106             cfg.align=this.align;
6107         }
6108         if (this.bgcolor) {
6109             cfg.bgcolor=this.bgcolor;
6110         }
6111         if (this.border) {
6112             cfg.border=this.border;
6113         }
6114         if (this.cellpadding) {
6115             cfg.cellpadding=this.cellpadding;
6116         }
6117         if (this.cellspacing) {
6118             cfg.cellspacing=this.cellspacing;
6119         }
6120         if (this.frame) {
6121             cfg.frame=this.frame;
6122         }
6123         if (this.rules) {
6124             cfg.rules=this.rules;
6125         }
6126         if (this.sortable) {
6127             cfg.sortable=this.sortable;
6128         }
6129         if (this.summary) {
6130             cfg.summary=this.summary;
6131         }
6132         if (this.width) {
6133             cfg.width=this.width;
6134         }
6135         if (this.layout) {
6136             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6137         }
6138         
6139         if(this.store || this.cm){
6140             if(this.headerShow){
6141                 cfg.cn.push(this.renderHeader());
6142             }
6143             
6144             cfg.cn.push(this.renderBody());
6145             
6146             if(this.footerShow){
6147                 cfg.cn.push(this.renderFooter());
6148             }
6149             // where does this come from?
6150             //cfg.cls+=  ' TableGrid';
6151         }
6152         
6153         return { cn : [ cfg ] };
6154     },
6155     
6156     initEvents : function()
6157     {   
6158         if(!this.store || !this.cm){
6159             return;
6160         }
6161         if (this.selModel) {
6162             this.selModel.initEvents();
6163         }
6164         
6165         
6166         //Roo.log('initEvents with ds!!!!');
6167         
6168         this.mainBody = this.el.select('tbody', true).first();
6169         this.mainHead = this.el.select('thead', true).first();
6170         
6171         
6172         
6173         
6174         var _this = this;
6175         
6176         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6177             e.on('click', _this.sort, _this);
6178         });
6179         
6180         this.mainBody.on("click", this.onClick, this);
6181         this.mainBody.on("dblclick", this.onDblClick, this);
6182         
6183         // why is this done????? = it breaks dialogs??
6184         //this.parent().el.setStyle('position', 'relative');
6185         
6186         
6187         if (this.footer) {
6188             this.footer.parentId = this.id;
6189             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6190             
6191             if(this.lazyLoad){
6192                 this.el.select('tfoot tr td').first().addClass('hide');
6193             }
6194         } 
6195         
6196         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6197         
6198         this.store.on('load', this.onLoad, this);
6199         this.store.on('beforeload', this.onBeforeLoad, this);
6200         this.store.on('update', this.onUpdate, this);
6201         this.store.on('add', this.onAdd, this);
6202         this.store.on("clear", this.clear, this);
6203         
6204         this.el.on("contextmenu", this.onContextMenu, this);
6205         
6206         this.mainBody.on('scroll', this.onBodyScroll, this);
6207         
6208         this.cm.on("headerchange", this.onHeaderChange, this);
6209         
6210     },
6211     
6212     onContextMenu : function(e, t)
6213     {
6214         this.processEvent("contextmenu", e);
6215     },
6216     
6217     processEvent : function(name, e)
6218     {
6219         if (name != 'touchstart' ) {
6220             this.fireEvent(name, e);    
6221         }
6222         
6223         var t = e.getTarget();
6224         
6225         var cell = Roo.get(t);
6226         
6227         if(!cell){
6228             return;
6229         }
6230         
6231         if(cell.findParent('tfoot', false, true)){
6232             return;
6233         }
6234         
6235         if(cell.findParent('thead', false, true)){
6236             
6237             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6238                 cell = Roo.get(t).findParent('th', false, true);
6239                 if (!cell) {
6240                     Roo.log("failed to find th in thead?");
6241                     Roo.log(e.getTarget());
6242                     return;
6243                 }
6244             }
6245             
6246             var cellIndex = cell.dom.cellIndex;
6247             
6248             var ename = name == 'touchstart' ? 'click' : name;
6249             this.fireEvent("header" + ename, this, cellIndex, e);
6250             
6251             return;
6252         }
6253         
6254         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6255             cell = Roo.get(t).findParent('td', false, true);
6256             if (!cell) {
6257                 Roo.log("failed to find th in tbody?");
6258                 Roo.log(e.getTarget());
6259                 return;
6260             }
6261         }
6262         
6263         var row = cell.findParent('tr', false, true);
6264         var cellIndex = cell.dom.cellIndex;
6265         var rowIndex = row.dom.rowIndex - 1;
6266         
6267         if(row !== false){
6268             
6269             this.fireEvent("row" + name, this, rowIndex, e);
6270             
6271             if(cell !== false){
6272             
6273                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6274             }
6275         }
6276         
6277     },
6278     
6279     onMouseover : function(e, el)
6280     {
6281         var cell = Roo.get(el);
6282         
6283         if(!cell){
6284             return;
6285         }
6286         
6287         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6288             cell = cell.findParent('td', false, true);
6289         }
6290         
6291         var row = cell.findParent('tr', false, true);
6292         var cellIndex = cell.dom.cellIndex;
6293         var rowIndex = row.dom.rowIndex - 1; // start from 0
6294         
6295         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6296         
6297     },
6298     
6299     onMouseout : function(e, el)
6300     {
6301         var cell = Roo.get(el);
6302         
6303         if(!cell){
6304             return;
6305         }
6306         
6307         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6308             cell = cell.findParent('td', false, true);
6309         }
6310         
6311         var row = cell.findParent('tr', false, true);
6312         var cellIndex = cell.dom.cellIndex;
6313         var rowIndex = row.dom.rowIndex - 1; // start from 0
6314         
6315         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6316         
6317     },
6318     
6319     onClick : function(e, el)
6320     {
6321         var cell = Roo.get(el);
6322         
6323         if(!cell || (!this.cellSelection && !this.rowSelection)){
6324             return;
6325         }
6326         
6327         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6328             cell = cell.findParent('td', false, true);
6329         }
6330         
6331         if(!cell || typeof(cell) == 'undefined'){
6332             return;
6333         }
6334         
6335         var row = cell.findParent('tr', false, true);
6336         
6337         if(!row || typeof(row) == 'undefined'){
6338             return;
6339         }
6340         
6341         var cellIndex = cell.dom.cellIndex;
6342         var rowIndex = this.getRowIndex(row);
6343         
6344         // why??? - should these not be based on SelectionModel?
6345         if(this.cellSelection){
6346             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6347         }
6348         
6349         if(this.rowSelection){
6350             this.fireEvent('rowclick', this, row, rowIndex, e);
6351         }
6352         
6353         
6354     },
6355         
6356     onDblClick : function(e,el)
6357     {
6358         var cell = Roo.get(el);
6359         
6360         if(!cell || (!this.cellSelection && !this.rowSelection)){
6361             return;
6362         }
6363         
6364         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6365             cell = cell.findParent('td', false, true);
6366         }
6367         
6368         if(!cell || typeof(cell) == 'undefined'){
6369             return;
6370         }
6371         
6372         var row = cell.findParent('tr', false, true);
6373         
6374         if(!row || typeof(row) == 'undefined'){
6375             return;
6376         }
6377         
6378         var cellIndex = cell.dom.cellIndex;
6379         var rowIndex = this.getRowIndex(row);
6380         
6381         if(this.cellSelection){
6382             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6383         }
6384         
6385         if(this.rowSelection){
6386             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6387         }
6388     },
6389     
6390     sort : function(e,el)
6391     {
6392         var col = Roo.get(el);
6393         
6394         if(!col.hasClass('sortable')){
6395             return;
6396         }
6397         
6398         var sort = col.attr('sort');
6399         var dir = 'ASC';
6400         
6401         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6402             dir = 'DESC';
6403         }
6404         
6405         this.store.sortInfo = {field : sort, direction : dir};
6406         
6407         if (this.footer) {
6408             Roo.log("calling footer first");
6409             this.footer.onClick('first');
6410         } else {
6411         
6412             this.store.load({ params : { start : 0 } });
6413         }
6414     },
6415     
6416     renderHeader : function()
6417     {
6418         var header = {
6419             tag: 'thead',
6420             cn : []
6421         };
6422         
6423         var cm = this.cm;
6424         this.totalWidth = 0;
6425         
6426         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6427             
6428             var config = cm.config[i];
6429             
6430             var c = {
6431                 tag: 'th',
6432                 style : '',
6433                 html: cm.getColumnHeader(i)
6434             };
6435             
6436             var hh = '';
6437             
6438             if(typeof(config.sortable) != 'undefined' && config.sortable){
6439                 c.cls = 'sortable';
6440                 c.html = '<i class="glyphicon"></i>' + c.html;
6441             }
6442             
6443             if(typeof(config.lgHeader) != 'undefined'){
6444                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6445             }
6446             
6447             if(typeof(config.mdHeader) != 'undefined'){
6448                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6449             }
6450             
6451             if(typeof(config.smHeader) != 'undefined'){
6452                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6453             }
6454             
6455             if(typeof(config.xsHeader) != 'undefined'){
6456                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6457             }
6458             
6459             if(hh.length){
6460                 c.html = hh;
6461             }
6462             
6463             if(typeof(config.tooltip) != 'undefined'){
6464                 c.tooltip = config.tooltip;
6465             }
6466             
6467             if(typeof(config.colspan) != 'undefined'){
6468                 c.colspan = config.colspan;
6469             }
6470             
6471             if(typeof(config.hidden) != 'undefined' && config.hidden){
6472                 c.style += ' display:none;';
6473             }
6474             
6475             if(typeof(config.dataIndex) != 'undefined'){
6476                 c.sort = config.dataIndex;
6477             }
6478             
6479            
6480             
6481             if(typeof(config.align) != 'undefined' && config.align.length){
6482                 c.style += ' text-align:' + config.align + ';';
6483             }
6484             
6485             if(typeof(config.width) != 'undefined'){
6486                 c.style += ' width:' + config.width + 'px;';
6487                 this.totalWidth += config.width;
6488             } else {
6489                 this.totalWidth += 100; // assume minimum of 100 per column?
6490             }
6491             
6492             if(typeof(config.cls) != 'undefined'){
6493                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6494             }
6495             
6496             ['xs','sm','md','lg'].map(function(size){
6497                 
6498                 if(typeof(config[size]) == 'undefined'){
6499                     return;
6500                 }
6501                 
6502                 if (!config[size]) { // 0 = hidden
6503                     c.cls += ' hidden-' + size;
6504                     return;
6505                 }
6506                 
6507                 c.cls += ' col-' + size + '-' + config[size];
6508
6509             });
6510             
6511             header.cn.push(c)
6512         }
6513         
6514         return header;
6515     },
6516     
6517     renderBody : function()
6518     {
6519         var body = {
6520             tag: 'tbody',
6521             cn : [
6522                 {
6523                     tag: 'tr',
6524                     cn : [
6525                         {
6526                             tag : 'td',
6527                             colspan :  this.cm.getColumnCount()
6528                         }
6529                     ]
6530                 }
6531             ]
6532         };
6533         
6534         return body;
6535     },
6536     
6537     renderFooter : function()
6538     {
6539         var footer = {
6540             tag: 'tfoot',
6541             cn : [
6542                 {
6543                     tag: 'tr',
6544                     cn : [
6545                         {
6546                             tag : 'td',
6547                             colspan :  this.cm.getColumnCount()
6548                         }
6549                     ]
6550                 }
6551             ]
6552         };
6553         
6554         return footer;
6555     },
6556     
6557     
6558     
6559     onLoad : function()
6560     {
6561 //        Roo.log('ds onload');
6562         this.clear();
6563         
6564         var _this = this;
6565         var cm = this.cm;
6566         var ds = this.store;
6567         
6568         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6569             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6570             if (_this.store.sortInfo) {
6571                     
6572                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6573                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6574                 }
6575                 
6576                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6577                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6578                 }
6579             }
6580         });
6581         
6582         var tbody =  this.mainBody;
6583               
6584         if(ds.getCount() > 0){
6585             ds.data.each(function(d,rowIndex){
6586                 var row =  this.renderRow(cm, ds, rowIndex);
6587                 
6588                 tbody.createChild(row);
6589                 
6590                 var _this = this;
6591                 
6592                 if(row.cellObjects.length){
6593                     Roo.each(row.cellObjects, function(r){
6594                         _this.renderCellObject(r);
6595                     })
6596                 }
6597                 
6598             }, this);
6599         }
6600         
6601         Roo.each(this.el.select('tbody td', true).elements, function(e){
6602             e.on('mouseover', _this.onMouseover, _this);
6603         });
6604         
6605         Roo.each(this.el.select('tbody td', true).elements, function(e){
6606             e.on('mouseout', _this.onMouseout, _this);
6607         });
6608         this.fireEvent('rowsrendered', this);
6609         //if(this.loadMask){
6610         //    this.maskEl.hide();
6611         //}
6612         
6613         this.autoSize();
6614     },
6615     
6616     
6617     onUpdate : function(ds,record)
6618     {
6619         this.refreshRow(record);
6620         this.autoSize();
6621     },
6622     
6623     onRemove : function(ds, record, index, isUpdate){
6624         if(isUpdate !== true){
6625             this.fireEvent("beforerowremoved", this, index, record);
6626         }
6627         var bt = this.mainBody.dom;
6628         
6629         var rows = this.el.select('tbody > tr', true).elements;
6630         
6631         if(typeof(rows[index]) != 'undefined'){
6632             bt.removeChild(rows[index].dom);
6633         }
6634         
6635 //        if(bt.rows[index]){
6636 //            bt.removeChild(bt.rows[index]);
6637 //        }
6638         
6639         if(isUpdate !== true){
6640             //this.stripeRows(index);
6641             //this.syncRowHeights(index, index);
6642             //this.layout();
6643             this.fireEvent("rowremoved", this, index, record);
6644         }
6645     },
6646     
6647     onAdd : function(ds, records, rowIndex)
6648     {
6649         //Roo.log('on Add called');
6650         // - note this does not handle multiple adding very well..
6651         var bt = this.mainBody.dom;
6652         for (var i =0 ; i < records.length;i++) {
6653             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6654             //Roo.log(records[i]);
6655             //Roo.log(this.store.getAt(rowIndex+i));
6656             this.insertRow(this.store, rowIndex + i, false);
6657             return;
6658         }
6659         
6660     },
6661     
6662     
6663     refreshRow : function(record){
6664         var ds = this.store, index;
6665         if(typeof record == 'number'){
6666             index = record;
6667             record = ds.getAt(index);
6668         }else{
6669             index = ds.indexOf(record);
6670         }
6671         this.insertRow(ds, index, true);
6672         this.autoSize();
6673         this.onRemove(ds, record, index+1, true);
6674         this.autoSize();
6675         //this.syncRowHeights(index, index);
6676         //this.layout();
6677         this.fireEvent("rowupdated", this, index, record);
6678     },
6679     
6680     insertRow : function(dm, rowIndex, isUpdate){
6681         
6682         if(!isUpdate){
6683             this.fireEvent("beforerowsinserted", this, rowIndex);
6684         }
6685             //var s = this.getScrollState();
6686         var row = this.renderRow(this.cm, this.store, rowIndex);
6687         // insert before rowIndex..
6688         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6689         
6690         var _this = this;
6691                 
6692         if(row.cellObjects.length){
6693             Roo.each(row.cellObjects, function(r){
6694                 _this.renderCellObject(r);
6695             })
6696         }
6697             
6698         if(!isUpdate){
6699             this.fireEvent("rowsinserted", this, rowIndex);
6700             //this.syncRowHeights(firstRow, lastRow);
6701             //this.stripeRows(firstRow);
6702             //this.layout();
6703         }
6704         
6705     },
6706     
6707     
6708     getRowDom : function(rowIndex)
6709     {
6710         var rows = this.el.select('tbody > tr', true).elements;
6711         
6712         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6713         
6714     },
6715     // returns the object tree for a tr..
6716   
6717     
6718     renderRow : function(cm, ds, rowIndex) 
6719     {
6720         
6721         var d = ds.getAt(rowIndex);
6722         
6723         var row = {
6724             tag : 'tr',
6725             cn : []
6726         };
6727             
6728         var cellObjects = [];
6729         
6730         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6731             var config = cm.config[i];
6732             
6733             var renderer = cm.getRenderer(i);
6734             var value = '';
6735             var id = false;
6736             
6737             if(typeof(renderer) !== 'undefined'){
6738                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6739             }
6740             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6741             // and are rendered into the cells after the row is rendered - using the id for the element.
6742             
6743             if(typeof(value) === 'object'){
6744                 id = Roo.id();
6745                 cellObjects.push({
6746                     container : id,
6747                     cfg : value 
6748                 })
6749             }
6750             
6751             var rowcfg = {
6752                 record: d,
6753                 rowIndex : rowIndex,
6754                 colIndex : i,
6755                 rowClass : ''
6756             };
6757
6758             this.fireEvent('rowclass', this, rowcfg);
6759             
6760             var td = {
6761                 tag: 'td',
6762                 cls : rowcfg.rowClass,
6763                 style: '',
6764                 html: (typeof(value) === 'object') ? '' : value
6765             };
6766             
6767             if (id) {
6768                 td.id = id;
6769             }
6770             
6771             if(typeof(config.colspan) != 'undefined'){
6772                 td.colspan = config.colspan;
6773             }
6774             
6775             if(typeof(config.hidden) != 'undefined' && config.hidden){
6776                 td.style += ' display:none;';
6777             }
6778             
6779             if(typeof(config.align) != 'undefined' && config.align.length){
6780                 td.style += ' text-align:' + config.align + ';';
6781             }
6782             
6783             if(typeof(config.width) != 'undefined'){
6784                 td.style += ' width:' +  config.width + 'px;';
6785             }
6786             
6787             if(typeof(config.cursor) != 'undefined'){
6788                 td.style += ' cursor:' +  config.cursor + ';';
6789             }
6790             
6791             if(typeof(config.cls) != 'undefined'){
6792                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6793             }
6794             
6795             ['xs','sm','md','lg'].map(function(size){
6796                 
6797                 if(typeof(config[size]) == 'undefined'){
6798                     return;
6799                 }
6800                 
6801                 if (!config[size]) { // 0 = hidden
6802                     td.cls += ' hidden-' + size;
6803                     return;
6804                 }
6805                 
6806                 td.cls += ' col-' + size + '-' + config[size];
6807
6808             });
6809              
6810             row.cn.push(td);
6811            
6812         }
6813         
6814         row.cellObjects = cellObjects;
6815         
6816         return row;
6817           
6818     },
6819     
6820     
6821     
6822     onBeforeLoad : function()
6823     {
6824         //Roo.log('ds onBeforeLoad');
6825         
6826         //this.clear();
6827         
6828         //if(this.loadMask){
6829         //    this.maskEl.show();
6830         //}
6831     },
6832      /**
6833      * Remove all rows
6834      */
6835     clear : function()
6836     {
6837         this.el.select('tbody', true).first().dom.innerHTML = '';
6838     },
6839     /**
6840      * Show or hide a row.
6841      * @param {Number} rowIndex to show or hide
6842      * @param {Boolean} state hide
6843      */
6844     setRowVisibility : function(rowIndex, state)
6845     {
6846         var bt = this.mainBody.dom;
6847         
6848         var rows = this.el.select('tbody > tr', true).elements;
6849         
6850         if(typeof(rows[rowIndex]) == 'undefined'){
6851             return;
6852         }
6853         rows[rowIndex].dom.style.display = state ? '' : 'none';
6854     },
6855     
6856     
6857     getSelectionModel : function(){
6858         if(!this.selModel){
6859             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6860         }
6861         return this.selModel;
6862     },
6863     /*
6864      * Render the Roo.bootstrap object from renderder
6865      */
6866     renderCellObject : function(r)
6867     {
6868         var _this = this;
6869         
6870         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6871         
6872         var t = r.cfg.render(r.container);
6873         
6874         if(r.cfg.cn){
6875             Roo.each(r.cfg.cn, function(c){
6876                 var child = {
6877                     container: t.getChildContainer(),
6878                     cfg: c
6879                 };
6880                 _this.renderCellObject(child);
6881             })
6882         }
6883     },
6884     
6885     getRowIndex : function(row)
6886     {
6887         var rowIndex = -1;
6888         
6889         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6890             if(el != row){
6891                 return;
6892             }
6893             
6894             rowIndex = index;
6895         });
6896         
6897         return rowIndex;
6898     },
6899      /**
6900      * Returns the grid's underlying element = used by panel.Grid
6901      * @return {Element} The element
6902      */
6903     getGridEl : function(){
6904         return this.el;
6905     },
6906      /**
6907      * Forces a resize - used by panel.Grid
6908      * @return {Element} The element
6909      */
6910     autoSize : function()
6911     {
6912         //var ctr = Roo.get(this.container.dom.parentElement);
6913         var ctr = Roo.get(this.el.dom);
6914         
6915         var thd = this.getGridEl().select('thead',true).first();
6916         var tbd = this.getGridEl().select('tbody', true).first();
6917         var tfd = this.getGridEl().select('tfoot', true).first();
6918         
6919         var cw = ctr.getWidth();
6920         
6921         if (tbd) {
6922             
6923             tbd.setSize(ctr.getWidth(),
6924                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6925             );
6926             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6927             cw -= barsize;
6928         }
6929         cw = Math.max(cw, this.totalWidth);
6930         this.getGridEl().select('tr',true).setWidth(cw);
6931         // resize 'expandable coloumn?
6932         
6933         return; // we doe not have a view in this design..
6934         
6935     },
6936     onBodyScroll: function()
6937     {
6938         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6939         if(this.mainHead){
6940             this.mainHead.setStyle({
6941                 'position' : 'relative',
6942                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6943             });
6944         }
6945         
6946         if(this.lazyLoad){
6947             
6948             var scrollHeight = this.mainBody.dom.scrollHeight;
6949             
6950             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6951             
6952             var height = this.mainBody.getHeight();
6953             
6954             if(scrollHeight - height == scrollTop) {
6955                 
6956                 var total = this.ds.getTotalCount();
6957                 
6958                 if(this.footer.cursor + this.footer.pageSize < total){
6959                     
6960                     this.footer.ds.load({
6961                         params : {
6962                             start : this.footer.cursor + this.footer.pageSize,
6963                             limit : this.footer.pageSize
6964                         },
6965                         add : true
6966                     });
6967                 }
6968             }
6969             
6970         }
6971     },
6972     
6973     onHeaderChange : function()
6974     {
6975         
6976         var header = this.renderHeader();
6977         var table = this.el.select('table', true).first();
6978         
6979         this.mainHead.remove();
6980         this.mainHead = table.createChild(header, this.mainBody, false);
6981     }
6982     
6983 });
6984
6985  
6986
6987  /*
6988  * - LGPL
6989  *
6990  * table cell
6991  * 
6992  */
6993
6994 /**
6995  * @class Roo.bootstrap.TableCell
6996  * @extends Roo.bootstrap.Component
6997  * Bootstrap TableCell class
6998  * @cfg {String} html cell contain text
6999  * @cfg {String} cls cell class
7000  * @cfg {String} tag cell tag (td|th) default td
7001  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7002  * @cfg {String} align Aligns the content in a cell
7003  * @cfg {String} axis Categorizes cells
7004  * @cfg {String} bgcolor Specifies the background color of a cell
7005  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7006  * @cfg {Number} colspan Specifies the number of columns a cell should span
7007  * @cfg {String} headers Specifies one or more header cells a cell is related to
7008  * @cfg {Number} height Sets the height of a cell
7009  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7010  * @cfg {Number} rowspan Sets the number of rows a cell should span
7011  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7012  * @cfg {String} valign Vertical aligns the content in a cell
7013  * @cfg {Number} width Specifies the width of a cell
7014  * 
7015  * @constructor
7016  * Create a new TableCell
7017  * @param {Object} config The config object
7018  */
7019
7020 Roo.bootstrap.TableCell = function(config){
7021     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7022 };
7023
7024 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7025     
7026     html: false,
7027     cls: false,
7028     tag: false,
7029     abbr: false,
7030     align: false,
7031     axis: false,
7032     bgcolor: false,
7033     charoff: false,
7034     colspan: false,
7035     headers: false,
7036     height: false,
7037     nowrap: false,
7038     rowspan: false,
7039     scope: false,
7040     valign: false,
7041     width: false,
7042     
7043     
7044     getAutoCreate : function(){
7045         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7046         
7047         cfg = {
7048             tag: 'td'
7049         };
7050         
7051         if(this.tag){
7052             cfg.tag = this.tag;
7053         }
7054         
7055         if (this.html) {
7056             cfg.html=this.html
7057         }
7058         if (this.cls) {
7059             cfg.cls=this.cls
7060         }
7061         if (this.abbr) {
7062             cfg.abbr=this.abbr
7063         }
7064         if (this.align) {
7065             cfg.align=this.align
7066         }
7067         if (this.axis) {
7068             cfg.axis=this.axis
7069         }
7070         if (this.bgcolor) {
7071             cfg.bgcolor=this.bgcolor
7072         }
7073         if (this.charoff) {
7074             cfg.charoff=this.charoff
7075         }
7076         if (this.colspan) {
7077             cfg.colspan=this.colspan
7078         }
7079         if (this.headers) {
7080             cfg.headers=this.headers
7081         }
7082         if (this.height) {
7083             cfg.height=this.height
7084         }
7085         if (this.nowrap) {
7086             cfg.nowrap=this.nowrap
7087         }
7088         if (this.rowspan) {
7089             cfg.rowspan=this.rowspan
7090         }
7091         if (this.scope) {
7092             cfg.scope=this.scope
7093         }
7094         if (this.valign) {
7095             cfg.valign=this.valign
7096         }
7097         if (this.width) {
7098             cfg.width=this.width
7099         }
7100         
7101         
7102         return cfg;
7103     }
7104    
7105 });
7106
7107  
7108
7109  /*
7110  * - LGPL
7111  *
7112  * table row
7113  * 
7114  */
7115
7116 /**
7117  * @class Roo.bootstrap.TableRow
7118  * @extends Roo.bootstrap.Component
7119  * Bootstrap TableRow class
7120  * @cfg {String} cls row class
7121  * @cfg {String} align Aligns the content in a table row
7122  * @cfg {String} bgcolor Specifies a background color for a table row
7123  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7124  * @cfg {String} valign Vertical aligns the content in a table row
7125  * 
7126  * @constructor
7127  * Create a new TableRow
7128  * @param {Object} config The config object
7129  */
7130
7131 Roo.bootstrap.TableRow = function(config){
7132     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7133 };
7134
7135 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7136     
7137     cls: false,
7138     align: false,
7139     bgcolor: false,
7140     charoff: false,
7141     valign: false,
7142     
7143     getAutoCreate : function(){
7144         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7145         
7146         cfg = {
7147             tag: 'tr'
7148         };
7149             
7150         if(this.cls){
7151             cfg.cls = this.cls;
7152         }
7153         if(this.align){
7154             cfg.align = this.align;
7155         }
7156         if(this.bgcolor){
7157             cfg.bgcolor = this.bgcolor;
7158         }
7159         if(this.charoff){
7160             cfg.charoff = this.charoff;
7161         }
7162         if(this.valign){
7163             cfg.valign = this.valign;
7164         }
7165         
7166         return cfg;
7167     }
7168    
7169 });
7170
7171  
7172
7173  /*
7174  * - LGPL
7175  *
7176  * table body
7177  * 
7178  */
7179
7180 /**
7181  * @class Roo.bootstrap.TableBody
7182  * @extends Roo.bootstrap.Component
7183  * Bootstrap TableBody class
7184  * @cfg {String} cls element class
7185  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7186  * @cfg {String} align Aligns the content inside the element
7187  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7188  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7189  * 
7190  * @constructor
7191  * Create a new TableBody
7192  * @param {Object} config The config object
7193  */
7194
7195 Roo.bootstrap.TableBody = function(config){
7196     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7197 };
7198
7199 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7200     
7201     cls: false,
7202     tag: false,
7203     align: false,
7204     charoff: false,
7205     valign: false,
7206     
7207     getAutoCreate : function(){
7208         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7209         
7210         cfg = {
7211             tag: 'tbody'
7212         };
7213             
7214         if (this.cls) {
7215             cfg.cls=this.cls
7216         }
7217         if(this.tag){
7218             cfg.tag = this.tag;
7219         }
7220         
7221         if(this.align){
7222             cfg.align = this.align;
7223         }
7224         if(this.charoff){
7225             cfg.charoff = this.charoff;
7226         }
7227         if(this.valign){
7228             cfg.valign = this.valign;
7229         }
7230         
7231         return cfg;
7232     }
7233     
7234     
7235 //    initEvents : function()
7236 //    {
7237 //        
7238 //        if(!this.store){
7239 //            return;
7240 //        }
7241 //        
7242 //        this.store = Roo.factory(this.store, Roo.data);
7243 //        this.store.on('load', this.onLoad, this);
7244 //        
7245 //        this.store.load();
7246 //        
7247 //    },
7248 //    
7249 //    onLoad: function () 
7250 //    {   
7251 //        this.fireEvent('load', this);
7252 //    }
7253 //    
7254 //   
7255 });
7256
7257  
7258
7259  /*
7260  * Based on:
7261  * Ext JS Library 1.1.1
7262  * Copyright(c) 2006-2007, Ext JS, LLC.
7263  *
7264  * Originally Released Under LGPL - original licence link has changed is not relivant.
7265  *
7266  * Fork - LGPL
7267  * <script type="text/javascript">
7268  */
7269
7270 // as we use this in bootstrap.
7271 Roo.namespace('Roo.form');
7272  /**
7273  * @class Roo.form.Action
7274  * Internal Class used to handle form actions
7275  * @constructor
7276  * @param {Roo.form.BasicForm} el The form element or its id
7277  * @param {Object} config Configuration options
7278  */
7279
7280  
7281  
7282 // define the action interface
7283 Roo.form.Action = function(form, options){
7284     this.form = form;
7285     this.options = options || {};
7286 };
7287 /**
7288  * Client Validation Failed
7289  * @const 
7290  */
7291 Roo.form.Action.CLIENT_INVALID = 'client';
7292 /**
7293  * Server Validation Failed
7294  * @const 
7295  */
7296 Roo.form.Action.SERVER_INVALID = 'server';
7297  /**
7298  * Connect to Server Failed
7299  * @const 
7300  */
7301 Roo.form.Action.CONNECT_FAILURE = 'connect';
7302 /**
7303  * Reading Data from Server Failed
7304  * @const 
7305  */
7306 Roo.form.Action.LOAD_FAILURE = 'load';
7307
7308 Roo.form.Action.prototype = {
7309     type : 'default',
7310     failureType : undefined,
7311     response : undefined,
7312     result : undefined,
7313
7314     // interface method
7315     run : function(options){
7316
7317     },
7318
7319     // interface method
7320     success : function(response){
7321
7322     },
7323
7324     // interface method
7325     handleResponse : function(response){
7326
7327     },
7328
7329     // default connection failure
7330     failure : function(response){
7331         
7332         this.response = response;
7333         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7334         this.form.afterAction(this, false);
7335     },
7336
7337     processResponse : function(response){
7338         this.response = response;
7339         if(!response.responseText){
7340             return true;
7341         }
7342         this.result = this.handleResponse(response);
7343         return this.result;
7344     },
7345
7346     // utility functions used internally
7347     getUrl : function(appendParams){
7348         var url = this.options.url || this.form.url || this.form.el.dom.action;
7349         if(appendParams){
7350             var p = this.getParams();
7351             if(p){
7352                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7353             }
7354         }
7355         return url;
7356     },
7357
7358     getMethod : function(){
7359         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7360     },
7361
7362     getParams : function(){
7363         var bp = this.form.baseParams;
7364         var p = this.options.params;
7365         if(p){
7366             if(typeof p == "object"){
7367                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7368             }else if(typeof p == 'string' && bp){
7369                 p += '&' + Roo.urlEncode(bp);
7370             }
7371         }else if(bp){
7372             p = Roo.urlEncode(bp);
7373         }
7374         return p;
7375     },
7376
7377     createCallback : function(){
7378         return {
7379             success: this.success,
7380             failure: this.failure,
7381             scope: this,
7382             timeout: (this.form.timeout*1000),
7383             upload: this.form.fileUpload ? this.success : undefined
7384         };
7385     }
7386 };
7387
7388 Roo.form.Action.Submit = function(form, options){
7389     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7390 };
7391
7392 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7393     type : 'submit',
7394
7395     haveProgress : false,
7396     uploadComplete : false,
7397     
7398     // uploadProgress indicator.
7399     uploadProgress : function()
7400     {
7401         if (!this.form.progressUrl) {
7402             return;
7403         }
7404         
7405         if (!this.haveProgress) {
7406             Roo.MessageBox.progress("Uploading", "Uploading");
7407         }
7408         if (this.uploadComplete) {
7409            Roo.MessageBox.hide();
7410            return;
7411         }
7412         
7413         this.haveProgress = true;
7414    
7415         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7416         
7417         var c = new Roo.data.Connection();
7418         c.request({
7419             url : this.form.progressUrl,
7420             params: {
7421                 id : uid
7422             },
7423             method: 'GET',
7424             success : function(req){
7425                //console.log(data);
7426                 var rdata = false;
7427                 var edata;
7428                 try  {
7429                    rdata = Roo.decode(req.responseText)
7430                 } catch (e) {
7431                     Roo.log("Invalid data from server..");
7432                     Roo.log(edata);
7433                     return;
7434                 }
7435                 if (!rdata || !rdata.success) {
7436                     Roo.log(rdata);
7437                     Roo.MessageBox.alert(Roo.encode(rdata));
7438                     return;
7439                 }
7440                 var data = rdata.data;
7441                 
7442                 if (this.uploadComplete) {
7443                    Roo.MessageBox.hide();
7444                    return;
7445                 }
7446                    
7447                 if (data){
7448                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7449                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7450                     );
7451                 }
7452                 this.uploadProgress.defer(2000,this);
7453             },
7454        
7455             failure: function(data) {
7456                 Roo.log('progress url failed ');
7457                 Roo.log(data);
7458             },
7459             scope : this
7460         });
7461            
7462     },
7463     
7464     
7465     run : function()
7466     {
7467         // run get Values on the form, so it syncs any secondary forms.
7468         this.form.getValues();
7469         
7470         var o = this.options;
7471         var method = this.getMethod();
7472         var isPost = method == 'POST';
7473         if(o.clientValidation === false || this.form.isValid()){
7474             
7475             if (this.form.progressUrl) {
7476                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7477                     (new Date() * 1) + '' + Math.random());
7478                     
7479             } 
7480             
7481             
7482             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7483                 form:this.form.el.dom,
7484                 url:this.getUrl(!isPost),
7485                 method: method,
7486                 params:isPost ? this.getParams() : null,
7487                 isUpload: this.form.fileUpload
7488             }));
7489             
7490             this.uploadProgress();
7491
7492         }else if (o.clientValidation !== false){ // client validation failed
7493             this.failureType = Roo.form.Action.CLIENT_INVALID;
7494             this.form.afterAction(this, false);
7495         }
7496     },
7497
7498     success : function(response)
7499     {
7500         this.uploadComplete= true;
7501         if (this.haveProgress) {
7502             Roo.MessageBox.hide();
7503         }
7504         
7505         
7506         var result = this.processResponse(response);
7507         if(result === true || result.success){
7508             this.form.afterAction(this, true);
7509             return;
7510         }
7511         if(result.errors){
7512             this.form.markInvalid(result.errors);
7513             this.failureType = Roo.form.Action.SERVER_INVALID;
7514         }
7515         this.form.afterAction(this, false);
7516     },
7517     failure : function(response)
7518     {
7519         this.uploadComplete= true;
7520         if (this.haveProgress) {
7521             Roo.MessageBox.hide();
7522         }
7523         
7524         this.response = response;
7525         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7526         this.form.afterAction(this, false);
7527     },
7528     
7529     handleResponse : function(response){
7530         if(this.form.errorReader){
7531             var rs = this.form.errorReader.read(response);
7532             var errors = [];
7533             if(rs.records){
7534                 for(var i = 0, len = rs.records.length; i < len; i++) {
7535                     var r = rs.records[i];
7536                     errors[i] = r.data;
7537                 }
7538             }
7539             if(errors.length < 1){
7540                 errors = null;
7541             }
7542             return {
7543                 success : rs.success,
7544                 errors : errors
7545             };
7546         }
7547         var ret = false;
7548         try {
7549             ret = Roo.decode(response.responseText);
7550         } catch (e) {
7551             ret = {
7552                 success: false,
7553                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7554                 errors : []
7555             };
7556         }
7557         return ret;
7558         
7559     }
7560 });
7561
7562
7563 Roo.form.Action.Load = function(form, options){
7564     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7565     this.reader = this.form.reader;
7566 };
7567
7568 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7569     type : 'load',
7570
7571     run : function(){
7572         
7573         Roo.Ajax.request(Roo.apply(
7574                 this.createCallback(), {
7575                     method:this.getMethod(),
7576                     url:this.getUrl(false),
7577                     params:this.getParams()
7578         }));
7579     },
7580
7581     success : function(response){
7582         
7583         var result = this.processResponse(response);
7584         if(result === true || !result.success || !result.data){
7585             this.failureType = Roo.form.Action.LOAD_FAILURE;
7586             this.form.afterAction(this, false);
7587             return;
7588         }
7589         this.form.clearInvalid();
7590         this.form.setValues(result.data);
7591         this.form.afterAction(this, true);
7592     },
7593
7594     handleResponse : function(response){
7595         if(this.form.reader){
7596             var rs = this.form.reader.read(response);
7597             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7598             return {
7599                 success : rs.success,
7600                 data : data
7601             };
7602         }
7603         return Roo.decode(response.responseText);
7604     }
7605 });
7606
7607 Roo.form.Action.ACTION_TYPES = {
7608     'load' : Roo.form.Action.Load,
7609     'submit' : Roo.form.Action.Submit
7610 };/*
7611  * - LGPL
7612  *
7613  * form
7614  *
7615  */
7616
7617 /**
7618  * @class Roo.bootstrap.Form
7619  * @extends Roo.bootstrap.Component
7620  * Bootstrap Form class
7621  * @cfg {String} method  GET | POST (default POST)
7622  * @cfg {String} labelAlign top | left (default top)
7623  * @cfg {String} align left  | right - for navbars
7624  * @cfg {Boolean} loadMask load mask when submit (default true)
7625
7626  *
7627  * @constructor
7628  * Create a new Form
7629  * @param {Object} config The config object
7630  */
7631
7632
7633 Roo.bootstrap.Form = function(config){
7634     
7635     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7636     
7637     Roo.bootstrap.Form.popover.apply();
7638     
7639     this.addEvents({
7640         /**
7641          * @event clientvalidation
7642          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7643          * @param {Form} this
7644          * @param {Boolean} valid true if the form has passed client-side validation
7645          */
7646         clientvalidation: true,
7647         /**
7648          * @event beforeaction
7649          * Fires before any action is performed. Return false to cancel the action.
7650          * @param {Form} this
7651          * @param {Action} action The action to be performed
7652          */
7653         beforeaction: true,
7654         /**
7655          * @event actionfailed
7656          * Fires when an action fails.
7657          * @param {Form} this
7658          * @param {Action} action The action that failed
7659          */
7660         actionfailed : true,
7661         /**
7662          * @event actioncomplete
7663          * Fires when an action is completed.
7664          * @param {Form} this
7665          * @param {Action} action The action that completed
7666          */
7667         actioncomplete : true
7668     });
7669 };
7670
7671 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7672
7673      /**
7674      * @cfg {String} method
7675      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7676      */
7677     method : 'POST',
7678     /**
7679      * @cfg {String} url
7680      * The URL to use for form actions if one isn't supplied in the action options.
7681      */
7682     /**
7683      * @cfg {Boolean} fileUpload
7684      * Set to true if this form is a file upload.
7685      */
7686
7687     /**
7688      * @cfg {Object} baseParams
7689      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7690      */
7691
7692     /**
7693      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7694      */
7695     timeout: 30,
7696     /**
7697      * @cfg {Sting} align (left|right) for navbar forms
7698      */
7699     align : 'left',
7700
7701     // private
7702     activeAction : null,
7703
7704     /**
7705      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7706      * element by passing it or its id or mask the form itself by passing in true.
7707      * @type Mixed
7708      */
7709     waitMsgTarget : false,
7710
7711     loadMask : true,
7712     
7713     /**
7714      * @cfg {Boolean} errorMask (true|false) default false
7715      */
7716     errorMask : false,
7717     
7718     /**
7719      * @cfg {Number} maskOffset Default 100
7720      */
7721     maskOffset : 100,
7722     
7723     /**
7724      * @cfg {Boolean} maskBody
7725      */
7726     maskBody : false,
7727
7728     getAutoCreate : function(){
7729
7730         var cfg = {
7731             tag: 'form',
7732             method : this.method || 'POST',
7733             id : this.id || Roo.id(),
7734             cls : ''
7735         };
7736         if (this.parent().xtype.match(/^Nav/)) {
7737             cfg.cls = 'navbar-form navbar-' + this.align;
7738
7739         }
7740
7741         if (this.labelAlign == 'left' ) {
7742             cfg.cls += ' form-horizontal';
7743         }
7744
7745
7746         return cfg;
7747     },
7748     initEvents : function()
7749     {
7750         this.el.on('submit', this.onSubmit, this);
7751         // this was added as random key presses on the form where triggering form submit.
7752         this.el.on('keypress', function(e) {
7753             if (e.getCharCode() != 13) {
7754                 return true;
7755             }
7756             // we might need to allow it for textareas.. and some other items.
7757             // check e.getTarget().
7758
7759             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7760                 return true;
7761             }
7762
7763             Roo.log("keypress blocked");
7764
7765             e.preventDefault();
7766             return false;
7767         });
7768         
7769     },
7770     // private
7771     onSubmit : function(e){
7772         e.stopEvent();
7773     },
7774
7775      /**
7776      * Returns true if client-side validation on the form is successful.
7777      * @return Boolean
7778      */
7779     isValid : function(){
7780         var items = this.getItems();
7781         var valid = true;
7782         var target = false;
7783         
7784         items.each(function(f){
7785             if(f.validate()){
7786                 return;
7787             }
7788             valid = false;
7789
7790             if(!target && f.el.isVisible(true)){
7791                 target = f;
7792             }
7793            
7794         });
7795         
7796         if(this.errorMask && !valid){
7797             Roo.bootstrap.Form.popover.mask(this, target);
7798         }
7799         
7800         return valid;
7801     },
7802     
7803     /**
7804      * Returns true if any fields in this form have changed since their original load.
7805      * @return Boolean
7806      */
7807     isDirty : function(){
7808         var dirty = false;
7809         var items = this.getItems();
7810         items.each(function(f){
7811            if(f.isDirty()){
7812                dirty = true;
7813                return false;
7814            }
7815            return true;
7816         });
7817         return dirty;
7818     },
7819      /**
7820      * Performs a predefined action (submit or load) or custom actions you define on this form.
7821      * @param {String} actionName The name of the action type
7822      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7823      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7824      * accept other config options):
7825      * <pre>
7826 Property          Type             Description
7827 ----------------  ---------------  ----------------------------------------------------------------------------------
7828 url               String           The url for the action (defaults to the form's url)
7829 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7830 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7831 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7832                                    validate the form on the client (defaults to false)
7833      * </pre>
7834      * @return {BasicForm} this
7835      */
7836     doAction : function(action, options){
7837         if(typeof action == 'string'){
7838             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7839         }
7840         if(this.fireEvent('beforeaction', this, action) !== false){
7841             this.beforeAction(action);
7842             action.run.defer(100, action);
7843         }
7844         return this;
7845     },
7846
7847     // private
7848     beforeAction : function(action){
7849         var o = action.options;
7850         
7851         if(this.loadMask){
7852             
7853             if(this.maskBody){
7854                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7855             } else {
7856                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7857             }
7858         }
7859         // not really supported yet.. ??
7860
7861         //if(this.waitMsgTarget === true){
7862         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7863         //}else if(this.waitMsgTarget){
7864         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7865         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7866         //}else {
7867         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7868        // }
7869
7870     },
7871
7872     // private
7873     afterAction : function(action, success){
7874         this.activeAction = null;
7875         var o = action.options;
7876
7877         if(this.loadMask){
7878             
7879             if(this.maskBody){
7880                 Roo.get(document.body).unmask();
7881             } else {
7882                 this.el.unmask();
7883             }
7884         }
7885         
7886         //if(this.waitMsgTarget === true){
7887 //            this.el.unmask();
7888         //}else if(this.waitMsgTarget){
7889         //    this.waitMsgTarget.unmask();
7890         //}else{
7891         //    Roo.MessageBox.updateProgress(1);
7892         //    Roo.MessageBox.hide();
7893        // }
7894         //
7895         if(success){
7896             if(o.reset){
7897                 this.reset();
7898             }
7899             Roo.callback(o.success, o.scope, [this, action]);
7900             this.fireEvent('actioncomplete', this, action);
7901
7902         }else{
7903
7904             // failure condition..
7905             // we have a scenario where updates need confirming.
7906             // eg. if a locking scenario exists..
7907             // we look for { errors : { needs_confirm : true }} in the response.
7908             if (
7909                 (typeof(action.result) != 'undefined')  &&
7910                 (typeof(action.result.errors) != 'undefined')  &&
7911                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7912            ){
7913                 var _t = this;
7914                 Roo.log("not supported yet");
7915                  /*
7916
7917                 Roo.MessageBox.confirm(
7918                     "Change requires confirmation",
7919                     action.result.errorMsg,
7920                     function(r) {
7921                         if (r != 'yes') {
7922                             return;
7923                         }
7924                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7925                     }
7926
7927                 );
7928                 */
7929
7930
7931                 return;
7932             }
7933
7934             Roo.callback(o.failure, o.scope, [this, action]);
7935             // show an error message if no failed handler is set..
7936             if (!this.hasListener('actionfailed')) {
7937                 Roo.log("need to add dialog support");
7938                 /*
7939                 Roo.MessageBox.alert("Error",
7940                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7941                         action.result.errorMsg :
7942                         "Saving Failed, please check your entries or try again"
7943                 );
7944                 */
7945             }
7946
7947             this.fireEvent('actionfailed', this, action);
7948         }
7949
7950     },
7951     /**
7952      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7953      * @param {String} id The value to search for
7954      * @return Field
7955      */
7956     findField : function(id){
7957         var items = this.getItems();
7958         var field = items.get(id);
7959         if(!field){
7960              items.each(function(f){
7961                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7962                     field = f;
7963                     return false;
7964                 }
7965                 return true;
7966             });
7967         }
7968         return field || null;
7969     },
7970      /**
7971      * Mark fields in this form invalid in bulk.
7972      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7973      * @return {BasicForm} this
7974      */
7975     markInvalid : function(errors){
7976         if(errors instanceof Array){
7977             for(var i = 0, len = errors.length; i < len; i++){
7978                 var fieldError = errors[i];
7979                 var f = this.findField(fieldError.id);
7980                 if(f){
7981                     f.markInvalid(fieldError.msg);
7982                 }
7983             }
7984         }else{
7985             var field, id;
7986             for(id in errors){
7987                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7988                     field.markInvalid(errors[id]);
7989                 }
7990             }
7991         }
7992         //Roo.each(this.childForms || [], function (f) {
7993         //    f.markInvalid(errors);
7994         //});
7995
7996         return this;
7997     },
7998
7999     /**
8000      * Set values for fields in this form in bulk.
8001      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8002      * @return {BasicForm} this
8003      */
8004     setValues : function(values){
8005         if(values instanceof Array){ // array of objects
8006             for(var i = 0, len = values.length; i < len; i++){
8007                 var v = values[i];
8008                 var f = this.findField(v.id);
8009                 if(f){
8010                     f.setValue(v.value);
8011                     if(this.trackResetOnLoad){
8012                         f.originalValue = f.getValue();
8013                     }
8014                 }
8015             }
8016         }else{ // object hash
8017             var field, id;
8018             for(id in values){
8019                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8020
8021                     if (field.setFromData &&
8022                         field.valueField &&
8023                         field.displayField &&
8024                         // combos' with local stores can
8025                         // be queried via setValue()
8026                         // to set their value..
8027                         (field.store && !field.store.isLocal)
8028                         ) {
8029                         // it's a combo
8030                         var sd = { };
8031                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8032                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8033                         field.setFromData(sd);
8034
8035                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8036                         
8037                         field.setFromData(values);
8038                         
8039                     } else {
8040                         field.setValue(values[id]);
8041                     }
8042
8043
8044                     if(this.trackResetOnLoad){
8045                         field.originalValue = field.getValue();
8046                     }
8047                 }
8048             }
8049         }
8050
8051         //Roo.each(this.childForms || [], function (f) {
8052         //    f.setValues(values);
8053         //});
8054
8055         return this;
8056     },
8057
8058     /**
8059      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8060      * they are returned as an array.
8061      * @param {Boolean} asString
8062      * @return {Object}
8063      */
8064     getValues : function(asString){
8065         //if (this.childForms) {
8066             // copy values from the child forms
8067         //    Roo.each(this.childForms, function (f) {
8068         //        this.setValues(f.getValues());
8069         //    }, this);
8070         //}
8071
8072
8073
8074         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8075         if(asString === true){
8076             return fs;
8077         }
8078         return Roo.urlDecode(fs);
8079     },
8080
8081     /**
8082      * Returns the fields in this form as an object with key/value pairs.
8083      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8084      * @return {Object}
8085      */
8086     getFieldValues : function(with_hidden)
8087     {
8088         var items = this.getItems();
8089         var ret = {};
8090         items.each(function(f){
8091             
8092             if (!f.getName()) {
8093                 return;
8094             }
8095             
8096             var v = f.getValue();
8097             
8098             if (f.inputType =='radio') {
8099                 if (typeof(ret[f.getName()]) == 'undefined') {
8100                     ret[f.getName()] = ''; // empty..
8101                 }
8102
8103                 if (!f.el.dom.checked) {
8104                     return;
8105
8106                 }
8107                 v = f.el.dom.value;
8108
8109             }
8110             
8111             if(f.xtype == 'MoneyField'){
8112                 ret[f.currencyName] = f.getCurrency();
8113             }
8114
8115             // not sure if this supported any more..
8116             if ((typeof(v) == 'object') && f.getRawValue) {
8117                 v = f.getRawValue() ; // dates..
8118             }
8119             // combo boxes where name != hiddenName...
8120             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8121                 ret[f.name] = f.getRawValue();
8122             }
8123             ret[f.getName()] = v;
8124         });
8125
8126         return ret;
8127     },
8128
8129     /**
8130      * Clears all invalid messages in this form.
8131      * @return {BasicForm} this
8132      */
8133     clearInvalid : function(){
8134         var items = this.getItems();
8135
8136         items.each(function(f){
8137            f.clearInvalid();
8138         });
8139
8140         return this;
8141     },
8142
8143     /**
8144      * Resets this form.
8145      * @return {BasicForm} this
8146      */
8147     reset : function(){
8148         var items = this.getItems();
8149         items.each(function(f){
8150             f.reset();
8151         });
8152
8153         Roo.each(this.childForms || [], function (f) {
8154             f.reset();
8155         });
8156
8157
8158         return this;
8159     },
8160     
8161     getItems : function()
8162     {
8163         var r=new Roo.util.MixedCollection(false, function(o){
8164             return o.id || (o.id = Roo.id());
8165         });
8166         var iter = function(el) {
8167             if (el.inputEl) {
8168                 r.add(el);
8169             }
8170             if (!el.items) {
8171                 return;
8172             }
8173             Roo.each(el.items,function(e) {
8174                 iter(e);
8175             });
8176         };
8177
8178         iter(this);
8179         return r;
8180     },
8181     
8182     hideFields : function(items)
8183     {
8184         Roo.each(items, function(i){
8185             
8186             var f = this.findField(i);
8187             
8188             if(!f){
8189                 return;
8190             }
8191             
8192             if(f.xtype == 'DateField'){
8193                 f.setVisible(false);
8194                 return;
8195             }
8196             
8197             f.hide();
8198             
8199         }, this);
8200     },
8201     
8202     showFields : function(items)
8203     {
8204         Roo.each(items, function(i){
8205             
8206             var f = this.findField(i);
8207             
8208             if(!f){
8209                 return;
8210             }
8211             
8212             if(f.xtype == 'DateField'){
8213                 f.setVisible(true);
8214                 return;
8215             }
8216             
8217             f.show();
8218             
8219         }, this);
8220     }
8221
8222 });
8223
8224 Roo.apply(Roo.bootstrap.Form, {
8225     
8226     popover : {
8227         
8228         padding : 5,
8229         
8230         isApplied : false,
8231         
8232         isMasked : false,
8233         
8234         form : false,
8235         
8236         target : false,
8237         
8238         toolTip : false,
8239         
8240         intervalID : false,
8241         
8242         maskEl : false,
8243         
8244         apply : function()
8245         {
8246             if(this.isApplied){
8247                 return;
8248             }
8249             
8250             this.maskEl = {
8251                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8252                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8253                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8254                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8255             };
8256             
8257             this.maskEl.top.enableDisplayMode("block");
8258             this.maskEl.left.enableDisplayMode("block");
8259             this.maskEl.bottom.enableDisplayMode("block");
8260             this.maskEl.right.enableDisplayMode("block");
8261             
8262             this.toolTip = new Roo.bootstrap.Tooltip({
8263                 cls : 'roo-form-error-popover',
8264                 alignment : {
8265                     'left' : ['r-l', [-2,0], 'right'],
8266                     'right' : ['l-r', [2,0], 'left'],
8267                     'bottom' : ['tl-bl', [0,2], 'top'],
8268                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8269                 }
8270             });
8271             
8272             this.toolTip.render(Roo.get(document.body));
8273
8274             this.toolTip.el.enableDisplayMode("block");
8275             
8276             Roo.get(document.body).on('click', function(){
8277                 this.unmask();
8278             }, this);
8279             
8280             Roo.get(document.body).on('touchstart', function(){
8281                 this.unmask();
8282             }, this);
8283             
8284             this.isApplied = true
8285         },
8286         
8287         mask : function(form, target)
8288         {
8289             this.form = form;
8290             
8291             this.target = target;
8292             
8293             if(!this.form.errorMask || !target.el){
8294                 return;
8295             }
8296             
8297             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8298             
8299             Roo.log(scrollable);
8300             
8301             var ot = this.target.el.calcOffsetsTo(scrollable);
8302             
8303             var scrollTo = ot[1] - this.form.maskOffset;
8304             
8305             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8306             
8307             scrollable.scrollTo('top', scrollTo);
8308             
8309             var box = this.target.el.getBox();
8310             Roo.log(box);
8311             var zIndex = Roo.bootstrap.Modal.zIndex++;
8312
8313             
8314             this.maskEl.top.setStyle('position', 'absolute');
8315             this.maskEl.top.setStyle('z-index', zIndex);
8316             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8317             this.maskEl.top.setLeft(0);
8318             this.maskEl.top.setTop(0);
8319             this.maskEl.top.show();
8320             
8321             this.maskEl.left.setStyle('position', 'absolute');
8322             this.maskEl.left.setStyle('z-index', zIndex);
8323             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8324             this.maskEl.left.setLeft(0);
8325             this.maskEl.left.setTop(box.y - this.padding);
8326             this.maskEl.left.show();
8327
8328             this.maskEl.bottom.setStyle('position', 'absolute');
8329             this.maskEl.bottom.setStyle('z-index', zIndex);
8330             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8331             this.maskEl.bottom.setLeft(0);
8332             this.maskEl.bottom.setTop(box.bottom + this.padding);
8333             this.maskEl.bottom.show();
8334
8335             this.maskEl.right.setStyle('position', 'absolute');
8336             this.maskEl.right.setStyle('z-index', zIndex);
8337             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8338             this.maskEl.right.setLeft(box.right + this.padding);
8339             this.maskEl.right.setTop(box.y - this.padding);
8340             this.maskEl.right.show();
8341
8342             this.toolTip.bindEl = this.target.el;
8343
8344             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8345
8346             var tip = this.target.blankText;
8347
8348             if(this.target.getValue() !== '' ) {
8349                 
8350                 if (this.target.invalidText.length) {
8351                     tip = this.target.invalidText;
8352                 } else if (this.target.regexText.length){
8353                     tip = this.target.regexText;
8354                 }
8355             }
8356
8357             this.toolTip.show(tip);
8358
8359             this.intervalID = window.setInterval(function() {
8360                 Roo.bootstrap.Form.popover.unmask();
8361             }, 10000);
8362
8363             window.onwheel = function(){ return false;};
8364             
8365             (function(){ this.isMasked = true; }).defer(500, this);
8366             
8367         },
8368         
8369         unmask : function()
8370         {
8371             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8372                 return;
8373             }
8374             
8375             this.maskEl.top.setStyle('position', 'absolute');
8376             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8377             this.maskEl.top.hide();
8378
8379             this.maskEl.left.setStyle('position', 'absolute');
8380             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8381             this.maskEl.left.hide();
8382
8383             this.maskEl.bottom.setStyle('position', 'absolute');
8384             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8385             this.maskEl.bottom.hide();
8386
8387             this.maskEl.right.setStyle('position', 'absolute');
8388             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8389             this.maskEl.right.hide();
8390             
8391             this.toolTip.hide();
8392             
8393             this.toolTip.el.hide();
8394             
8395             window.onwheel = function(){ return true;};
8396             
8397             if(this.intervalID){
8398                 window.clearInterval(this.intervalID);
8399                 this.intervalID = false;
8400             }
8401             
8402             this.isMasked = false;
8403             
8404         }
8405         
8406     }
8407     
8408 });
8409
8410 /*
8411  * Based on:
8412  * Ext JS Library 1.1.1
8413  * Copyright(c) 2006-2007, Ext JS, LLC.
8414  *
8415  * Originally Released Under LGPL - original licence link has changed is not relivant.
8416  *
8417  * Fork - LGPL
8418  * <script type="text/javascript">
8419  */
8420 /**
8421  * @class Roo.form.VTypes
8422  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8423  * @singleton
8424  */
8425 Roo.form.VTypes = function(){
8426     // closure these in so they are only created once.
8427     var alpha = /^[a-zA-Z_]+$/;
8428     var alphanum = /^[a-zA-Z0-9_]+$/;
8429     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8430     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8431
8432     // All these messages and functions are configurable
8433     return {
8434         /**
8435          * The function used to validate email addresses
8436          * @param {String} value The email address
8437          */
8438         'email' : function(v){
8439             return email.test(v);
8440         },
8441         /**
8442          * The error text to display when the email validation function returns false
8443          * @type String
8444          */
8445         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8446         /**
8447          * The keystroke filter mask to be applied on email input
8448          * @type RegExp
8449          */
8450         'emailMask' : /[a-z0-9_\.\-@]/i,
8451
8452         /**
8453          * The function used to validate URLs
8454          * @param {String} value The URL
8455          */
8456         'url' : function(v){
8457             return url.test(v);
8458         },
8459         /**
8460          * The error text to display when the url validation function returns false
8461          * @type String
8462          */
8463         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8464         
8465         /**
8466          * The function used to validate alpha values
8467          * @param {String} value The value
8468          */
8469         'alpha' : function(v){
8470             return alpha.test(v);
8471         },
8472         /**
8473          * The error text to display when the alpha validation function returns false
8474          * @type String
8475          */
8476         'alphaText' : 'This field should only contain letters and _',
8477         /**
8478          * The keystroke filter mask to be applied on alpha input
8479          * @type RegExp
8480          */
8481         'alphaMask' : /[a-z_]/i,
8482
8483         /**
8484          * The function used to validate alphanumeric values
8485          * @param {String} value The value
8486          */
8487         'alphanum' : function(v){
8488             return alphanum.test(v);
8489         },
8490         /**
8491          * The error text to display when the alphanumeric validation function returns false
8492          * @type String
8493          */
8494         'alphanumText' : 'This field should only contain letters, numbers and _',
8495         /**
8496          * The keystroke filter mask to be applied on alphanumeric input
8497          * @type RegExp
8498          */
8499         'alphanumMask' : /[a-z0-9_]/i
8500     };
8501 }();/*
8502  * - LGPL
8503  *
8504  * Input
8505  * 
8506  */
8507
8508 /**
8509  * @class Roo.bootstrap.Input
8510  * @extends Roo.bootstrap.Component
8511  * Bootstrap Input class
8512  * @cfg {Boolean} disabled is it disabled
8513  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8514  * @cfg {String} name name of the input
8515  * @cfg {string} fieldLabel - the label associated
8516  * @cfg {string} placeholder - placeholder to put in text.
8517  * @cfg {string}  before - input group add on before
8518  * @cfg {string} after - input group add on after
8519  * @cfg {string} size - (lg|sm) or leave empty..
8520  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8521  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8522  * @cfg {Number} md colspan out of 12 for computer-sized screens
8523  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8524  * @cfg {string} value default value of the input
8525  * @cfg {Number} labelWidth set the width of label 
8526  * @cfg {Number} labellg set the width of label (1-12)
8527  * @cfg {Number} labelmd set the width of label (1-12)
8528  * @cfg {Number} labelsm set the width of label (1-12)
8529  * @cfg {Number} labelxs set the width of label (1-12)
8530  * @cfg {String} labelAlign (top|left)
8531  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8532  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8533  * @cfg {String} indicatorpos (left|right) default left
8534
8535  * @cfg {String} align (left|center|right) Default left
8536  * @cfg {Boolean} forceFeedback (true|false) Default false
8537  * 
8538  * @constructor
8539  * Create a new Input
8540  * @param {Object} config The config object
8541  */
8542
8543 Roo.bootstrap.Input = function(config){
8544     
8545     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8546     
8547     this.addEvents({
8548         /**
8549          * @event focus
8550          * Fires when this field receives input focus.
8551          * @param {Roo.form.Field} this
8552          */
8553         focus : true,
8554         /**
8555          * @event blur
8556          * Fires when this field loses input focus.
8557          * @param {Roo.form.Field} this
8558          */
8559         blur : true,
8560         /**
8561          * @event specialkey
8562          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8563          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8564          * @param {Roo.form.Field} this
8565          * @param {Roo.EventObject} e The event object
8566          */
8567         specialkey : true,
8568         /**
8569          * @event change
8570          * Fires just before the field blurs if the field value has changed.
8571          * @param {Roo.form.Field} this
8572          * @param {Mixed} newValue The new value
8573          * @param {Mixed} oldValue The original value
8574          */
8575         change : true,
8576         /**
8577          * @event invalid
8578          * Fires after the field has been marked as invalid.
8579          * @param {Roo.form.Field} this
8580          * @param {String} msg The validation message
8581          */
8582         invalid : true,
8583         /**
8584          * @event valid
8585          * Fires after the field has been validated with no errors.
8586          * @param {Roo.form.Field} this
8587          */
8588         valid : true,
8589          /**
8590          * @event keyup
8591          * Fires after the key up
8592          * @param {Roo.form.Field} this
8593          * @param {Roo.EventObject}  e The event Object
8594          */
8595         keyup : true
8596     });
8597 };
8598
8599 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8600      /**
8601      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8602       automatic validation (defaults to "keyup").
8603      */
8604     validationEvent : "keyup",
8605      /**
8606      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8607      */
8608     validateOnBlur : true,
8609     /**
8610      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8611      */
8612     validationDelay : 250,
8613      /**
8614      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8615      */
8616     focusClass : "x-form-focus",  // not needed???
8617     
8618        
8619     /**
8620      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8621      */
8622     invalidClass : "has-warning",
8623     
8624     /**
8625      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8626      */
8627     validClass : "has-success",
8628     
8629     /**
8630      * @cfg {Boolean} hasFeedback (true|false) default true
8631      */
8632     hasFeedback : true,
8633     
8634     /**
8635      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8636      */
8637     invalidFeedbackClass : "glyphicon-warning-sign",
8638     
8639     /**
8640      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8641      */
8642     validFeedbackClass : "glyphicon-ok",
8643     
8644     /**
8645      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8646      */
8647     selectOnFocus : false,
8648     
8649      /**
8650      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8651      */
8652     maskRe : null,
8653        /**
8654      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8655      */
8656     vtype : null,
8657     
8658       /**
8659      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8660      */
8661     disableKeyFilter : false,
8662     
8663        /**
8664      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8665      */
8666     disabled : false,
8667      /**
8668      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8669      */
8670     allowBlank : true,
8671     /**
8672      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8673      */
8674     blankText : "Please complete this mandatory field",
8675     
8676      /**
8677      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8678      */
8679     minLength : 0,
8680     /**
8681      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8682      */
8683     maxLength : Number.MAX_VALUE,
8684     /**
8685      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8686      */
8687     minLengthText : "The minimum length for this field is {0}",
8688     /**
8689      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8690      */
8691     maxLengthText : "The maximum length for this field is {0}",
8692   
8693     
8694     /**
8695      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8696      * If available, this function will be called only after the basic validators all return true, and will be passed the
8697      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8698      */
8699     validator : null,
8700     /**
8701      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8702      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8703      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8704      */
8705     regex : null,
8706     /**
8707      * @cfg {String} regexText -- Depricated - use Invalid Text
8708      */
8709     regexText : "",
8710     
8711     /**
8712      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8713      */
8714     invalidText : "",
8715     
8716     
8717     
8718     autocomplete: false,
8719     
8720     
8721     fieldLabel : '',
8722     inputType : 'text',
8723     
8724     name : false,
8725     placeholder: false,
8726     before : false,
8727     after : false,
8728     size : false,
8729     hasFocus : false,
8730     preventMark: false,
8731     isFormField : true,
8732     value : '',
8733     labelWidth : 2,
8734     labelAlign : false,
8735     readOnly : false,
8736     align : false,
8737     formatedValue : false,
8738     forceFeedback : false,
8739     
8740     indicatorpos : 'left',
8741     
8742     labellg : 0,
8743     labelmd : 0,
8744     labelsm : 0,
8745     labelxs : 0,
8746     
8747     parentLabelAlign : function()
8748     {
8749         var parent = this;
8750         while (parent.parent()) {
8751             parent = parent.parent();
8752             if (typeof(parent.labelAlign) !='undefined') {
8753                 return parent.labelAlign;
8754             }
8755         }
8756         return 'left';
8757         
8758     },
8759     
8760     getAutoCreate : function()
8761     {
8762         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8763         
8764         var id = Roo.id();
8765         
8766         var cfg = {};
8767         
8768         if(this.inputType != 'hidden'){
8769             cfg.cls = 'form-group' //input-group
8770         }
8771         
8772         var input =  {
8773             tag: 'input',
8774             id : id,
8775             type : this.inputType,
8776             value : this.value,
8777             cls : 'form-control',
8778             placeholder : this.placeholder || '',
8779             autocomplete : this.autocomplete || 'new-password'
8780         };
8781         
8782         if(this.align){
8783             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8784         }
8785         
8786         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8787             input.maxLength = this.maxLength;
8788         }
8789         
8790         if (this.disabled) {
8791             input.disabled=true;
8792         }
8793         
8794         if (this.readOnly) {
8795             input.readonly=true;
8796         }
8797         
8798         if (this.name) {
8799             input.name = this.name;
8800         }
8801         
8802         if (this.size) {
8803             input.cls += ' input-' + this.size;
8804         }
8805         
8806         var settings=this;
8807         ['xs','sm','md','lg'].map(function(size){
8808             if (settings[size]) {
8809                 cfg.cls += ' col-' + size + '-' + settings[size];
8810             }
8811         });
8812         
8813         var inputblock = input;
8814         
8815         var feedback = {
8816             tag: 'span',
8817             cls: 'glyphicon form-control-feedback'
8818         };
8819             
8820         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8821             
8822             inputblock = {
8823                 cls : 'has-feedback',
8824                 cn :  [
8825                     input,
8826                     feedback
8827                 ] 
8828             };  
8829         }
8830         
8831         if (this.before || this.after) {
8832             
8833             inputblock = {
8834                 cls : 'input-group',
8835                 cn :  [] 
8836             };
8837             
8838             if (this.before && typeof(this.before) == 'string') {
8839                 
8840                 inputblock.cn.push({
8841                     tag :'span',
8842                     cls : 'roo-input-before input-group-addon',
8843                     html : this.before
8844                 });
8845             }
8846             if (this.before && typeof(this.before) == 'object') {
8847                 this.before = Roo.factory(this.before);
8848                 
8849                 inputblock.cn.push({
8850                     tag :'span',
8851                     cls : 'roo-input-before input-group-' +
8852                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8853                 });
8854             }
8855             
8856             inputblock.cn.push(input);
8857             
8858             if (this.after && typeof(this.after) == 'string') {
8859                 inputblock.cn.push({
8860                     tag :'span',
8861                     cls : 'roo-input-after input-group-addon',
8862                     html : this.after
8863                 });
8864             }
8865             if (this.after && typeof(this.after) == 'object') {
8866                 this.after = Roo.factory(this.after);
8867                 
8868                 inputblock.cn.push({
8869                     tag :'span',
8870                     cls : 'roo-input-after input-group-' +
8871                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8872                 });
8873             }
8874             
8875             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8876                 inputblock.cls += ' has-feedback';
8877                 inputblock.cn.push(feedback);
8878             }
8879         };
8880         
8881         if (align ==='left' && this.fieldLabel.length) {
8882             
8883             cfg.cls += ' roo-form-group-label-left';
8884             
8885             cfg.cn = [
8886                 {
8887                     tag : 'i',
8888                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8889                     tooltip : 'This field is required'
8890                 },
8891                 {
8892                     tag: 'label',
8893                     'for' :  id,
8894                     cls : 'control-label',
8895                     html : this.fieldLabel
8896
8897                 },
8898                 {
8899                     cls : "", 
8900                     cn: [
8901                         inputblock
8902                     ]
8903                 }
8904             ];
8905             
8906             var labelCfg = cfg.cn[1];
8907             var contentCfg = cfg.cn[2];
8908             
8909             if(this.indicatorpos == 'right'){
8910                 cfg.cn = [
8911                     {
8912                         tag: 'label',
8913                         'for' :  id,
8914                         cls : 'control-label',
8915                         cn : [
8916                             {
8917                                 tag : 'span',
8918                                 html : this.fieldLabel
8919                             },
8920                             {
8921                                 tag : 'i',
8922                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8923                                 tooltip : 'This field is required'
8924                             }
8925                         ]
8926                     },
8927                     {
8928                         cls : "",
8929                         cn: [
8930                             inputblock
8931                         ]
8932                     }
8933
8934                 ];
8935                 
8936                 labelCfg = cfg.cn[0];
8937                 contentCfg = cfg.cn[1];
8938             
8939             }
8940             
8941             if(this.labelWidth > 12){
8942                 labelCfg.style = "width: " + this.labelWidth + 'px';
8943             }
8944             
8945             if(this.labelWidth < 13 && this.labelmd == 0){
8946                 this.labelmd = this.labelWidth;
8947             }
8948             
8949             if(this.labellg > 0){
8950                 labelCfg.cls += ' col-lg-' + this.labellg;
8951                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8952             }
8953             
8954             if(this.labelmd > 0){
8955                 labelCfg.cls += ' col-md-' + this.labelmd;
8956                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8957             }
8958             
8959             if(this.labelsm > 0){
8960                 labelCfg.cls += ' col-sm-' + this.labelsm;
8961                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8962             }
8963             
8964             if(this.labelxs > 0){
8965                 labelCfg.cls += ' col-xs-' + this.labelxs;
8966                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8967             }
8968             
8969             
8970         } else if ( this.fieldLabel.length) {
8971                 
8972             cfg.cn = [
8973                 {
8974                     tag : 'i',
8975                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8976                     tooltip : 'This field is required'
8977                 },
8978                 {
8979                     tag: 'label',
8980                    //cls : 'input-group-addon',
8981                     html : this.fieldLabel
8982
8983                 },
8984
8985                inputblock
8986
8987            ];
8988            
8989            if(this.indicatorpos == 'right'){
8990                 
8991                 cfg.cn = [
8992                     {
8993                         tag: 'label',
8994                        //cls : 'input-group-addon',
8995                         html : this.fieldLabel
8996
8997                     },
8998                     {
8999                         tag : 'i',
9000                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9001                         tooltip : 'This field is required'
9002                     },
9003
9004                    inputblock
9005
9006                ];
9007
9008             }
9009
9010         } else {
9011             
9012             cfg.cn = [
9013
9014                     inputblock
9015
9016             ];
9017                 
9018                 
9019         };
9020         
9021         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9022            cfg.cls += ' navbar-form';
9023         }
9024         
9025         if (this.parentType === 'NavGroup') {
9026            cfg.cls += ' navbar-form';
9027            cfg.tag = 'li';
9028         }
9029         
9030         return cfg;
9031         
9032     },
9033     /**
9034      * return the real input element.
9035      */
9036     inputEl: function ()
9037     {
9038         return this.el.select('input.form-control',true).first();
9039     },
9040     
9041     tooltipEl : function()
9042     {
9043         return this.inputEl();
9044     },
9045     
9046     indicatorEl : function()
9047     {
9048         var indicator = this.el.select('i.roo-required-indicator',true).first();
9049         
9050         if(!indicator){
9051             return false;
9052         }
9053         
9054         return indicator;
9055         
9056     },
9057     
9058     setDisabled : function(v)
9059     {
9060         var i  = this.inputEl().dom;
9061         if (!v) {
9062             i.removeAttribute('disabled');
9063             return;
9064             
9065         }
9066         i.setAttribute('disabled','true');
9067     },
9068     initEvents : function()
9069     {
9070           
9071         this.inputEl().on("keydown" , this.fireKey,  this);
9072         this.inputEl().on("focus", this.onFocus,  this);
9073         this.inputEl().on("blur", this.onBlur,  this);
9074         
9075         this.inputEl().relayEvent('keyup', this);
9076         
9077         this.indicator = this.indicatorEl();
9078         
9079         if(this.indicator){
9080             this.indicator.addClass('invisible');
9081             
9082         }
9083  
9084         // reference to original value for reset
9085         this.originalValue = this.getValue();
9086         //Roo.form.TextField.superclass.initEvents.call(this);
9087         if(this.validationEvent == 'keyup'){
9088             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9089             this.inputEl().on('keyup', this.filterValidation, this);
9090         }
9091         else if(this.validationEvent !== false){
9092             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9093         }
9094         
9095         if(this.selectOnFocus){
9096             this.on("focus", this.preFocus, this);
9097             
9098         }
9099         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9100             this.inputEl().on("keypress", this.filterKeys, this);
9101         } else {
9102             this.inputEl().relayEvent('keypress', this);
9103         }
9104        /* if(this.grow){
9105             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9106             this.el.on("click", this.autoSize,  this);
9107         }
9108         */
9109         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9110             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9111         }
9112         
9113         if (typeof(this.before) == 'object') {
9114             this.before.render(this.el.select('.roo-input-before',true).first());
9115         }
9116         if (typeof(this.after) == 'object') {
9117             this.after.render(this.el.select('.roo-input-after',true).first());
9118         }
9119         
9120         
9121     },
9122     filterValidation : function(e){
9123         if(!e.isNavKeyPress()){
9124             this.validationTask.delay(this.validationDelay);
9125         }
9126     },
9127      /**
9128      * Validates the field value
9129      * @return {Boolean} True if the value is valid, else false
9130      */
9131     validate : function(){
9132         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9133         if(this.disabled || this.validateValue(this.getRawValue())){
9134             this.markValid();
9135             return true;
9136         }
9137         
9138         this.markInvalid();
9139         return false;
9140     },
9141     
9142     
9143     /**
9144      * Validates a value according to the field's validation rules and marks the field as invalid
9145      * if the validation fails
9146      * @param {Mixed} value The value to validate
9147      * @return {Boolean} True if the value is valid, else false
9148      */
9149     validateValue : function(value)
9150     {
9151         if(this.getVisibilityEl().hasClass('hidden')){
9152             return true;
9153         }
9154         
9155         if(value.length < 1)  { // if it's blank
9156             if(this.allowBlank){
9157                 return true;
9158             }
9159             return false;
9160         }
9161         
9162         if(value.length < this.minLength){
9163             return false;
9164         }
9165         if(value.length > this.maxLength){
9166             return false;
9167         }
9168         if(this.vtype){
9169             var vt = Roo.form.VTypes;
9170             if(!vt[this.vtype](value, this)){
9171                 return false;
9172             }
9173         }
9174         if(typeof this.validator == "function"){
9175             var msg = this.validator(value);
9176             if(msg !== true){
9177                 return false;
9178             }
9179             if (typeof(msg) == 'string') {
9180                 this.invalidText = msg;
9181             }
9182         }
9183         
9184         if(this.regex && !this.regex.test(value)){
9185             return false;
9186         }
9187         
9188         return true;
9189     },
9190     
9191      // private
9192     fireKey : function(e){
9193         //Roo.log('field ' + e.getKey());
9194         if(e.isNavKeyPress()){
9195             this.fireEvent("specialkey", this, e);
9196         }
9197     },
9198     focus : function (selectText){
9199         if(this.rendered){
9200             this.inputEl().focus();
9201             if(selectText === true){
9202                 this.inputEl().dom.select();
9203             }
9204         }
9205         return this;
9206     } ,
9207     
9208     onFocus : function(){
9209         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9210            // this.el.addClass(this.focusClass);
9211         }
9212         if(!this.hasFocus){
9213             this.hasFocus = true;
9214             this.startValue = this.getValue();
9215             this.fireEvent("focus", this);
9216         }
9217     },
9218     
9219     beforeBlur : Roo.emptyFn,
9220
9221     
9222     // private
9223     onBlur : function(){
9224         this.beforeBlur();
9225         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9226             //this.el.removeClass(this.focusClass);
9227         }
9228         this.hasFocus = false;
9229         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9230             this.validate();
9231         }
9232         var v = this.getValue();
9233         if(String(v) !== String(this.startValue)){
9234             this.fireEvent('change', this, v, this.startValue);
9235         }
9236         this.fireEvent("blur", this);
9237     },
9238     
9239     /**
9240      * Resets the current field value to the originally loaded value and clears any validation messages
9241      */
9242     reset : function(){
9243         this.setValue(this.originalValue);
9244         this.validate();
9245     },
9246      /**
9247      * Returns the name of the field
9248      * @return {Mixed} name The name field
9249      */
9250     getName: function(){
9251         return this.name;
9252     },
9253      /**
9254      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9255      * @return {Mixed} value The field value
9256      */
9257     getValue : function(){
9258         
9259         var v = this.inputEl().getValue();
9260         
9261         return v;
9262     },
9263     /**
9264      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9265      * @return {Mixed} value The field value
9266      */
9267     getRawValue : function(){
9268         var v = this.inputEl().getValue();
9269         
9270         return v;
9271     },
9272     
9273     /**
9274      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9275      * @param {Mixed} value The value to set
9276      */
9277     setRawValue : function(v){
9278         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9279     },
9280     
9281     selectText : function(start, end){
9282         var v = this.getRawValue();
9283         if(v.length > 0){
9284             start = start === undefined ? 0 : start;
9285             end = end === undefined ? v.length : end;
9286             var d = this.inputEl().dom;
9287             if(d.setSelectionRange){
9288                 d.setSelectionRange(start, end);
9289             }else if(d.createTextRange){
9290                 var range = d.createTextRange();
9291                 range.moveStart("character", start);
9292                 range.moveEnd("character", v.length-end);
9293                 range.select();
9294             }
9295         }
9296     },
9297     
9298     /**
9299      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9300      * @param {Mixed} value The value to set
9301      */
9302     setValue : function(v){
9303         this.value = v;
9304         if(this.rendered){
9305             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9306             this.validate();
9307         }
9308     },
9309     
9310     /*
9311     processValue : function(value){
9312         if(this.stripCharsRe){
9313             var newValue = value.replace(this.stripCharsRe, '');
9314             if(newValue !== value){
9315                 this.setRawValue(newValue);
9316                 return newValue;
9317             }
9318         }
9319         return value;
9320     },
9321   */
9322     preFocus : function(){
9323         
9324         if(this.selectOnFocus){
9325             this.inputEl().dom.select();
9326         }
9327     },
9328     filterKeys : function(e){
9329         var k = e.getKey();
9330         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9331             return;
9332         }
9333         var c = e.getCharCode(), cc = String.fromCharCode(c);
9334         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9335             return;
9336         }
9337         if(!this.maskRe.test(cc)){
9338             e.stopEvent();
9339         }
9340     },
9341      /**
9342      * Clear any invalid styles/messages for this field
9343      */
9344     clearInvalid : function(){
9345         
9346         if(!this.el || this.preventMark){ // not rendered
9347             return;
9348         }
9349         
9350      
9351         this.el.removeClass(this.invalidClass);
9352         
9353         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9354             
9355             var feedback = this.el.select('.form-control-feedback', true).first();
9356             
9357             if(feedback){
9358                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9359             }
9360             
9361         }
9362         
9363         this.fireEvent('valid', this);
9364     },
9365     
9366      /**
9367      * Mark this field as valid
9368      */
9369     markValid : function()
9370     {
9371         if(!this.el  || this.preventMark){ // not rendered...
9372             return;
9373         }
9374         
9375         this.el.removeClass([this.invalidClass, this.validClass]);
9376         
9377         var feedback = this.el.select('.form-control-feedback', true).first();
9378             
9379         if(feedback){
9380             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9381         }
9382         
9383         if(this.indicator){
9384             this.indicator.removeClass('visible');
9385             this.indicator.addClass('invisible');
9386         }
9387         
9388         if(this.disabled){
9389             return;
9390         }
9391         
9392         if(this.allowBlank && !this.getRawValue().length){
9393             return;
9394         }
9395         
9396         this.el.addClass(this.validClass);
9397         
9398         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9399             
9400             var feedback = this.el.select('.form-control-feedback', true).first();
9401             
9402             if(feedback){
9403                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9404                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9405             }
9406             
9407         }
9408         
9409         this.fireEvent('valid', this);
9410     },
9411     
9412      /**
9413      * Mark this field as invalid
9414      * @param {String} msg The validation message
9415      */
9416     markInvalid : function(msg)
9417     {
9418         if(!this.el  || this.preventMark){ // not rendered
9419             return;
9420         }
9421         
9422         this.el.removeClass([this.invalidClass, this.validClass]);
9423         
9424         var feedback = this.el.select('.form-control-feedback', true).first();
9425             
9426         if(feedback){
9427             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9428         }
9429
9430         if(this.disabled){
9431             return;
9432         }
9433         
9434         if(this.allowBlank && !this.getRawValue().length){
9435             return;
9436         }
9437         
9438         if(this.indicator){
9439             this.indicator.removeClass('invisible');
9440             this.indicator.addClass('visible');
9441         }
9442         
9443         this.el.addClass(this.invalidClass);
9444         
9445         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9446             
9447             var feedback = this.el.select('.form-control-feedback', true).first();
9448             
9449             if(feedback){
9450                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9451                 
9452                 if(this.getValue().length || this.forceFeedback){
9453                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9454                 }
9455                 
9456             }
9457             
9458         }
9459         
9460         this.fireEvent('invalid', this, msg);
9461     },
9462     // private
9463     SafariOnKeyDown : function(event)
9464     {
9465         // this is a workaround for a password hang bug on chrome/ webkit.
9466         if (this.inputEl().dom.type != 'password') {
9467             return;
9468         }
9469         
9470         var isSelectAll = false;
9471         
9472         if(this.inputEl().dom.selectionEnd > 0){
9473             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9474         }
9475         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9476             event.preventDefault();
9477             this.setValue('');
9478             return;
9479         }
9480         
9481         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9482             
9483             event.preventDefault();
9484             // this is very hacky as keydown always get's upper case.
9485             //
9486             var cc = String.fromCharCode(event.getCharCode());
9487             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9488             
9489         }
9490     },
9491     adjustWidth : function(tag, w){
9492         tag = tag.toLowerCase();
9493         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9494             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9495                 if(tag == 'input'){
9496                     return w + 2;
9497                 }
9498                 if(tag == 'textarea'){
9499                     return w-2;
9500                 }
9501             }else if(Roo.isOpera){
9502                 if(tag == 'input'){
9503                     return w + 2;
9504                 }
9505                 if(tag == 'textarea'){
9506                     return w-2;
9507                 }
9508             }
9509         }
9510         return w;
9511     },
9512     
9513     setFieldLabel : function(v)
9514     {
9515         if(!this.rendered){
9516             return;
9517         }
9518         
9519         if(this.indicator){
9520             var ar = this.el.select('label > span',true);
9521             
9522             if (ar.elements.length) {
9523                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9524                 this.fieldLabel = v;
9525                 return;
9526             }
9527             
9528             var br = this.el.select('label',true);
9529             
9530             if(br.elements.length) {
9531                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9532                 this.fieldLabel = v;
9533                 return;
9534             }
9535             
9536             Roo.log('Cannot Found any of label > span || label in input');
9537             return;
9538         }
9539         
9540         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9541         this.fieldLabel = v;
9542         
9543         
9544     }
9545 });
9546
9547  
9548 /*
9549  * - LGPL
9550  *
9551  * Input
9552  * 
9553  */
9554
9555 /**
9556  * @class Roo.bootstrap.TextArea
9557  * @extends Roo.bootstrap.Input
9558  * Bootstrap TextArea class
9559  * @cfg {Number} cols Specifies the visible width of a text area
9560  * @cfg {Number} rows Specifies the visible number of lines in a text area
9561  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9562  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9563  * @cfg {string} html text
9564  * 
9565  * @constructor
9566  * Create a new TextArea
9567  * @param {Object} config The config object
9568  */
9569
9570 Roo.bootstrap.TextArea = function(config){
9571     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9572    
9573 };
9574
9575 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9576      
9577     cols : false,
9578     rows : 5,
9579     readOnly : false,
9580     warp : 'soft',
9581     resize : false,
9582     value: false,
9583     html: false,
9584     
9585     getAutoCreate : function(){
9586         
9587         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9588         
9589         var id = Roo.id();
9590         
9591         var cfg = {};
9592         
9593         if(this.inputType != 'hidden'){
9594             cfg.cls = 'form-group' //input-group
9595         }
9596         
9597         var input =  {
9598             tag: 'textarea',
9599             id : id,
9600             warp : this.warp,
9601             rows : this.rows,
9602             value : this.value || '',
9603             html: this.html || '',
9604             cls : 'form-control',
9605             placeholder : this.placeholder || '' 
9606             
9607         };
9608         
9609         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9610             input.maxLength = this.maxLength;
9611         }
9612         
9613         if(this.resize){
9614             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9615         }
9616         
9617         if(this.cols){
9618             input.cols = this.cols;
9619         }
9620         
9621         if (this.readOnly) {
9622             input.readonly = true;
9623         }
9624         
9625         if (this.name) {
9626             input.name = this.name;
9627         }
9628         
9629         if (this.size) {
9630             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9631         }
9632         
9633         var settings=this;
9634         ['xs','sm','md','lg'].map(function(size){
9635             if (settings[size]) {
9636                 cfg.cls += ' col-' + size + '-' + settings[size];
9637             }
9638         });
9639         
9640         var inputblock = input;
9641         
9642         if(this.hasFeedback && !this.allowBlank){
9643             
9644             var feedback = {
9645                 tag: 'span',
9646                 cls: 'glyphicon form-control-feedback'
9647             };
9648
9649             inputblock = {
9650                 cls : 'has-feedback',
9651                 cn :  [
9652                     input,
9653                     feedback
9654                 ] 
9655             };  
9656         }
9657         
9658         
9659         if (this.before || this.after) {
9660             
9661             inputblock = {
9662                 cls : 'input-group',
9663                 cn :  [] 
9664             };
9665             if (this.before) {
9666                 inputblock.cn.push({
9667                     tag :'span',
9668                     cls : 'input-group-addon',
9669                     html : this.before
9670                 });
9671             }
9672             
9673             inputblock.cn.push(input);
9674             
9675             if(this.hasFeedback && !this.allowBlank){
9676                 inputblock.cls += ' has-feedback';
9677                 inputblock.cn.push(feedback);
9678             }
9679             
9680             if (this.after) {
9681                 inputblock.cn.push({
9682                     tag :'span',
9683                     cls : 'input-group-addon',
9684                     html : this.after
9685                 });
9686             }
9687             
9688         }
9689         
9690         if (align ==='left' && this.fieldLabel.length) {
9691             cfg.cn = [
9692                 {
9693                     tag: 'label',
9694                     'for' :  id,
9695                     cls : 'control-label',
9696                     html : this.fieldLabel
9697                 },
9698                 {
9699                     cls : "",
9700                     cn: [
9701                         inputblock
9702                     ]
9703                 }
9704
9705             ];
9706             
9707             if(this.labelWidth > 12){
9708                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9709             }
9710
9711             if(this.labelWidth < 13 && this.labelmd == 0){
9712                 this.labelmd = this.labelWidth;
9713             }
9714
9715             if(this.labellg > 0){
9716                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9717                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9718             }
9719
9720             if(this.labelmd > 0){
9721                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9722                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9723             }
9724
9725             if(this.labelsm > 0){
9726                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9727                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9728             }
9729
9730             if(this.labelxs > 0){
9731                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9732                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9733             }
9734             
9735         } else if ( this.fieldLabel.length) {
9736             cfg.cn = [
9737
9738                {
9739                    tag: 'label',
9740                    //cls : 'input-group-addon',
9741                    html : this.fieldLabel
9742
9743                },
9744
9745                inputblock
9746
9747            ];
9748
9749         } else {
9750
9751             cfg.cn = [
9752
9753                 inputblock
9754
9755             ];
9756                 
9757         }
9758         
9759         if (this.disabled) {
9760             input.disabled=true;
9761         }
9762         
9763         return cfg;
9764         
9765     },
9766     /**
9767      * return the real textarea element.
9768      */
9769     inputEl: function ()
9770     {
9771         return this.el.select('textarea.form-control',true).first();
9772     },
9773     
9774     /**
9775      * Clear any invalid styles/messages for this field
9776      */
9777     clearInvalid : function()
9778     {
9779         
9780         if(!this.el || this.preventMark){ // not rendered
9781             return;
9782         }
9783         
9784         var label = this.el.select('label', true).first();
9785         var icon = this.el.select('i.fa-star', true).first();
9786         
9787         if(label && icon){
9788             icon.remove();
9789         }
9790         
9791         this.el.removeClass(this.invalidClass);
9792         
9793         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9794             
9795             var feedback = this.el.select('.form-control-feedback', true).first();
9796             
9797             if(feedback){
9798                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9799             }
9800             
9801         }
9802         
9803         this.fireEvent('valid', this);
9804     },
9805     
9806      /**
9807      * Mark this field as valid
9808      */
9809     markValid : function()
9810     {
9811         if(!this.el  || this.preventMark){ // not rendered
9812             return;
9813         }
9814         
9815         this.el.removeClass([this.invalidClass, this.validClass]);
9816         
9817         var feedback = this.el.select('.form-control-feedback', true).first();
9818             
9819         if(feedback){
9820             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9821         }
9822
9823         if(this.disabled || this.allowBlank){
9824             return;
9825         }
9826         
9827         var label = this.el.select('label', true).first();
9828         var icon = this.el.select('i.fa-star', true).first();
9829         
9830         if(label && icon){
9831             icon.remove();
9832         }
9833         
9834         this.el.addClass(this.validClass);
9835         
9836         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9837             
9838             var feedback = this.el.select('.form-control-feedback', true).first();
9839             
9840             if(feedback){
9841                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9842                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9843             }
9844             
9845         }
9846         
9847         this.fireEvent('valid', this);
9848     },
9849     
9850      /**
9851      * Mark this field as invalid
9852      * @param {String} msg The validation message
9853      */
9854     markInvalid : function(msg)
9855     {
9856         if(!this.el  || this.preventMark){ // not rendered
9857             return;
9858         }
9859         
9860         this.el.removeClass([this.invalidClass, this.validClass]);
9861         
9862         var feedback = this.el.select('.form-control-feedback', true).first();
9863             
9864         if(feedback){
9865             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9866         }
9867
9868         if(this.disabled || this.allowBlank){
9869             return;
9870         }
9871         
9872         var label = this.el.select('label', true).first();
9873         var icon = this.el.select('i.fa-star', true).first();
9874         
9875         if(!this.getValue().length && label && !icon){
9876             this.el.createChild({
9877                 tag : 'i',
9878                 cls : 'text-danger fa fa-lg fa-star',
9879                 tooltip : 'This field is required',
9880                 style : 'margin-right:5px;'
9881             }, label, true);
9882         }
9883
9884         this.el.addClass(this.invalidClass);
9885         
9886         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9887             
9888             var feedback = this.el.select('.form-control-feedback', true).first();
9889             
9890             if(feedback){
9891                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9892                 
9893                 if(this.getValue().length || this.forceFeedback){
9894                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9895                 }
9896                 
9897             }
9898             
9899         }
9900         
9901         this.fireEvent('invalid', this, msg);
9902     }
9903 });
9904
9905  
9906 /*
9907  * - LGPL
9908  *
9909  * trigger field - base class for combo..
9910  * 
9911  */
9912  
9913 /**
9914  * @class Roo.bootstrap.TriggerField
9915  * @extends Roo.bootstrap.Input
9916  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9917  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9918  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9919  * for which you can provide a custom implementation.  For example:
9920  * <pre><code>
9921 var trigger = new Roo.bootstrap.TriggerField();
9922 trigger.onTriggerClick = myTriggerFn;
9923 trigger.applyTo('my-field');
9924 </code></pre>
9925  *
9926  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9927  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9928  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9929  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9930  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9931
9932  * @constructor
9933  * Create a new TriggerField.
9934  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9935  * to the base TextField)
9936  */
9937 Roo.bootstrap.TriggerField = function(config){
9938     this.mimicing = false;
9939     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9940 };
9941
9942 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9943     /**
9944      * @cfg {String} triggerClass A CSS class to apply to the trigger
9945      */
9946      /**
9947      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9948      */
9949     hideTrigger:false,
9950
9951     /**
9952      * @cfg {Boolean} removable (true|false) special filter default false
9953      */
9954     removable : false,
9955     
9956     /** @cfg {Boolean} grow @hide */
9957     /** @cfg {Number} growMin @hide */
9958     /** @cfg {Number} growMax @hide */
9959
9960     /**
9961      * @hide 
9962      * @method
9963      */
9964     autoSize: Roo.emptyFn,
9965     // private
9966     monitorTab : true,
9967     // private
9968     deferHeight : true,
9969
9970     
9971     actionMode : 'wrap',
9972     
9973     caret : false,
9974     
9975     
9976     getAutoCreate : function(){
9977        
9978         var align = this.labelAlign || this.parentLabelAlign();
9979         
9980         var id = Roo.id();
9981         
9982         var cfg = {
9983             cls: 'form-group' //input-group
9984         };
9985         
9986         
9987         var input =  {
9988             tag: 'input',
9989             id : id,
9990             type : this.inputType,
9991             cls : 'form-control',
9992             autocomplete: 'new-password',
9993             placeholder : this.placeholder || '' 
9994             
9995         };
9996         if (this.name) {
9997             input.name = this.name;
9998         }
9999         if (this.size) {
10000             input.cls += ' input-' + this.size;
10001         }
10002         
10003         if (this.disabled) {
10004             input.disabled=true;
10005         }
10006         
10007         var inputblock = input;
10008         
10009         if(this.hasFeedback && !this.allowBlank){
10010             
10011             var feedback = {
10012                 tag: 'span',
10013                 cls: 'glyphicon form-control-feedback'
10014             };
10015             
10016             if(this.removable && !this.editable && !this.tickable){
10017                 inputblock = {
10018                     cls : 'has-feedback',
10019                     cn :  [
10020                         inputblock,
10021                         {
10022                             tag: 'button',
10023                             html : 'x',
10024                             cls : 'roo-combo-removable-btn close'
10025                         },
10026                         feedback
10027                     ] 
10028                 };
10029             } else {
10030                 inputblock = {
10031                     cls : 'has-feedback',
10032                     cn :  [
10033                         inputblock,
10034                         feedback
10035                     ] 
10036                 };
10037             }
10038
10039         } else {
10040             if(this.removable && !this.editable && !this.tickable){
10041                 inputblock = {
10042                     cls : 'roo-removable',
10043                     cn :  [
10044                         inputblock,
10045                         {
10046                             tag: 'button',
10047                             html : 'x',
10048                             cls : 'roo-combo-removable-btn close'
10049                         }
10050                     ] 
10051                 };
10052             }
10053         }
10054         
10055         if (this.before || this.after) {
10056             
10057             inputblock = {
10058                 cls : 'input-group',
10059                 cn :  [] 
10060             };
10061             if (this.before) {
10062                 inputblock.cn.push({
10063                     tag :'span',
10064                     cls : 'input-group-addon',
10065                     html : this.before
10066                 });
10067             }
10068             
10069             inputblock.cn.push(input);
10070             
10071             if(this.hasFeedback && !this.allowBlank){
10072                 inputblock.cls += ' has-feedback';
10073                 inputblock.cn.push(feedback);
10074             }
10075             
10076             if (this.after) {
10077                 inputblock.cn.push({
10078                     tag :'span',
10079                     cls : 'input-group-addon',
10080                     html : this.after
10081                 });
10082             }
10083             
10084         };
10085         
10086         var box = {
10087             tag: 'div',
10088             cn: [
10089                 {
10090                     tag: 'input',
10091                     type : 'hidden',
10092                     cls: 'form-hidden-field'
10093                 },
10094                 inputblock
10095             ]
10096             
10097         };
10098         
10099         if(this.multiple){
10100             box = {
10101                 tag: 'div',
10102                 cn: [
10103                     {
10104                         tag: 'input',
10105                         type : 'hidden',
10106                         cls: 'form-hidden-field'
10107                     },
10108                     {
10109                         tag: 'ul',
10110                         cls: 'roo-select2-choices',
10111                         cn:[
10112                             {
10113                                 tag: 'li',
10114                                 cls: 'roo-select2-search-field',
10115                                 cn: [
10116
10117                                     inputblock
10118                                 ]
10119                             }
10120                         ]
10121                     }
10122                 ]
10123             }
10124         };
10125         
10126         var combobox = {
10127             cls: 'roo-select2-container input-group',
10128             cn: [
10129                 box
10130 //                {
10131 //                    tag: 'ul',
10132 //                    cls: 'typeahead typeahead-long dropdown-menu',
10133 //                    style: 'display:none'
10134 //                }
10135             ]
10136         };
10137         
10138         if(!this.multiple && this.showToggleBtn){
10139             
10140             var caret = {
10141                         tag: 'span',
10142                         cls: 'caret'
10143              };
10144             if (this.caret != false) {
10145                 caret = {
10146                      tag: 'i',
10147                      cls: 'fa fa-' + this.caret
10148                 };
10149                 
10150             }
10151             
10152             combobox.cn.push({
10153                 tag :'span',
10154                 cls : 'input-group-addon btn dropdown-toggle',
10155                 cn : [
10156                     caret,
10157                     {
10158                         tag: 'span',
10159                         cls: 'combobox-clear',
10160                         cn  : [
10161                             {
10162                                 tag : 'i',
10163                                 cls: 'icon-remove'
10164                             }
10165                         ]
10166                     }
10167                 ]
10168
10169             })
10170         }
10171         
10172         if(this.multiple){
10173             combobox.cls += ' roo-select2-container-multi';
10174         }
10175         
10176         if (align ==='left' && this.fieldLabel.length) {
10177             
10178             cfg.cls += ' roo-form-group-label-left';
10179
10180             cfg.cn = [
10181                 {
10182                     tag : 'i',
10183                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10184                     tooltip : 'This field is required'
10185                 },
10186                 {
10187                     tag: 'label',
10188                     'for' :  id,
10189                     cls : 'control-label',
10190                     html : this.fieldLabel
10191
10192                 },
10193                 {
10194                     cls : "", 
10195                     cn: [
10196                         combobox
10197                     ]
10198                 }
10199
10200             ];
10201             
10202             var labelCfg = cfg.cn[1];
10203             var contentCfg = cfg.cn[2];
10204             
10205             if(this.indicatorpos == 'right'){
10206                 cfg.cn = [
10207                     {
10208                         tag: 'label',
10209                         'for' :  id,
10210                         cls : 'control-label',
10211                         cn : [
10212                             {
10213                                 tag : 'span',
10214                                 html : this.fieldLabel
10215                             },
10216                             {
10217                                 tag : 'i',
10218                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10219                                 tooltip : 'This field is required'
10220                             }
10221                         ]
10222                     },
10223                     {
10224                         cls : "", 
10225                         cn: [
10226                             combobox
10227                         ]
10228                     }
10229
10230                 ];
10231                 
10232                 labelCfg = cfg.cn[0];
10233                 contentCfg = cfg.cn[1];
10234             }
10235             
10236             if(this.labelWidth > 12){
10237                 labelCfg.style = "width: " + this.labelWidth + 'px';
10238             }
10239             
10240             if(this.labelWidth < 13 && this.labelmd == 0){
10241                 this.labelmd = this.labelWidth;
10242             }
10243             
10244             if(this.labellg > 0){
10245                 labelCfg.cls += ' col-lg-' + this.labellg;
10246                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10247             }
10248             
10249             if(this.labelmd > 0){
10250                 labelCfg.cls += ' col-md-' + this.labelmd;
10251                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10252             }
10253             
10254             if(this.labelsm > 0){
10255                 labelCfg.cls += ' col-sm-' + this.labelsm;
10256                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10257             }
10258             
10259             if(this.labelxs > 0){
10260                 labelCfg.cls += ' col-xs-' + this.labelxs;
10261                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10262             }
10263             
10264         } else if ( this.fieldLabel.length) {
10265 //                Roo.log(" label");
10266             cfg.cn = [
10267                 {
10268                    tag : 'i',
10269                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10270                    tooltip : 'This field is required'
10271                },
10272                {
10273                    tag: 'label',
10274                    //cls : 'input-group-addon',
10275                    html : this.fieldLabel
10276
10277                },
10278
10279                combobox
10280
10281             ];
10282             
10283             if(this.indicatorpos == 'right'){
10284                 
10285                 cfg.cn = [
10286                     {
10287                        tag: 'label',
10288                        cn : [
10289                            {
10290                                tag : 'span',
10291                                html : this.fieldLabel
10292                            },
10293                            {
10294                               tag : 'i',
10295                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10296                               tooltip : 'This field is required'
10297                            }
10298                        ]
10299
10300                     },
10301                     combobox
10302
10303                 ];
10304
10305             }
10306
10307         } else {
10308             
10309 //                Roo.log(" no label && no align");
10310                 cfg = combobox
10311                      
10312                 
10313         }
10314         
10315         var settings=this;
10316         ['xs','sm','md','lg'].map(function(size){
10317             if (settings[size]) {
10318                 cfg.cls += ' col-' + size + '-' + settings[size];
10319             }
10320         });
10321         
10322         return cfg;
10323         
10324     },
10325     
10326     
10327     
10328     // private
10329     onResize : function(w, h){
10330 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10331 //        if(typeof w == 'number'){
10332 //            var x = w - this.trigger.getWidth();
10333 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10334 //            this.trigger.setStyle('left', x+'px');
10335 //        }
10336     },
10337
10338     // private
10339     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10340
10341     // private
10342     getResizeEl : function(){
10343         return this.inputEl();
10344     },
10345
10346     // private
10347     getPositionEl : function(){
10348         return this.inputEl();
10349     },
10350
10351     // private
10352     alignErrorIcon : function(){
10353         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10354     },
10355
10356     // private
10357     initEvents : function(){
10358         
10359         this.createList();
10360         
10361         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10362         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10363         if(!this.multiple && this.showToggleBtn){
10364             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10365             if(this.hideTrigger){
10366                 this.trigger.setDisplayed(false);
10367             }
10368             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10369         }
10370         
10371         if(this.multiple){
10372             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10373         }
10374         
10375         if(this.removable && !this.editable && !this.tickable){
10376             var close = this.closeTriggerEl();
10377             
10378             if(close){
10379                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10380                 close.on('click', this.removeBtnClick, this, close);
10381             }
10382         }
10383         
10384         //this.trigger.addClassOnOver('x-form-trigger-over');
10385         //this.trigger.addClassOnClick('x-form-trigger-click');
10386         
10387         //if(!this.width){
10388         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10389         //}
10390     },
10391     
10392     closeTriggerEl : function()
10393     {
10394         var close = this.el.select('.roo-combo-removable-btn', true).first();
10395         return close ? close : false;
10396     },
10397     
10398     removeBtnClick : function(e, h, el)
10399     {
10400         e.preventDefault();
10401         
10402         if(this.fireEvent("remove", this) !== false){
10403             this.reset();
10404             this.fireEvent("afterremove", this)
10405         }
10406     },
10407     
10408     createList : function()
10409     {
10410         this.list = Roo.get(document.body).createChild({
10411             tag: 'ul',
10412             cls: 'typeahead typeahead-long dropdown-menu',
10413             style: 'display:none'
10414         });
10415         
10416         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10417         
10418     },
10419
10420     // private
10421     initTrigger : function(){
10422        
10423     },
10424
10425     // private
10426     onDestroy : function(){
10427         if(this.trigger){
10428             this.trigger.removeAllListeners();
10429           //  this.trigger.remove();
10430         }
10431         //if(this.wrap){
10432         //    this.wrap.remove();
10433         //}
10434         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10435     },
10436
10437     // private
10438     onFocus : function(){
10439         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10440         /*
10441         if(!this.mimicing){
10442             this.wrap.addClass('x-trigger-wrap-focus');
10443             this.mimicing = true;
10444             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10445             if(this.monitorTab){
10446                 this.el.on("keydown", this.checkTab, this);
10447             }
10448         }
10449         */
10450     },
10451
10452     // private
10453     checkTab : function(e){
10454         if(e.getKey() == e.TAB){
10455             this.triggerBlur();
10456         }
10457     },
10458
10459     // private
10460     onBlur : function(){
10461         // do nothing
10462     },
10463
10464     // private
10465     mimicBlur : function(e, t){
10466         /*
10467         if(!this.wrap.contains(t) && this.validateBlur()){
10468             this.triggerBlur();
10469         }
10470         */
10471     },
10472
10473     // private
10474     triggerBlur : function(){
10475         this.mimicing = false;
10476         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10477         if(this.monitorTab){
10478             this.el.un("keydown", this.checkTab, this);
10479         }
10480         //this.wrap.removeClass('x-trigger-wrap-focus');
10481         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10482     },
10483
10484     // private
10485     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10486     validateBlur : function(e, t){
10487         return true;
10488     },
10489
10490     // private
10491     onDisable : function(){
10492         this.inputEl().dom.disabled = true;
10493         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10494         //if(this.wrap){
10495         //    this.wrap.addClass('x-item-disabled');
10496         //}
10497     },
10498
10499     // private
10500     onEnable : function(){
10501         this.inputEl().dom.disabled = false;
10502         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10503         //if(this.wrap){
10504         //    this.el.removeClass('x-item-disabled');
10505         //}
10506     },
10507
10508     // private
10509     onShow : function(){
10510         var ae = this.getActionEl();
10511         
10512         if(ae){
10513             ae.dom.style.display = '';
10514             ae.dom.style.visibility = 'visible';
10515         }
10516     },
10517
10518     // private
10519     
10520     onHide : function(){
10521         var ae = this.getActionEl();
10522         ae.dom.style.display = 'none';
10523     },
10524
10525     /**
10526      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10527      * by an implementing function.
10528      * @method
10529      * @param {EventObject} e
10530      */
10531     onTriggerClick : Roo.emptyFn
10532 });
10533  /*
10534  * Based on:
10535  * Ext JS Library 1.1.1
10536  * Copyright(c) 2006-2007, Ext JS, LLC.
10537  *
10538  * Originally Released Under LGPL - original licence link has changed is not relivant.
10539  *
10540  * Fork - LGPL
10541  * <script type="text/javascript">
10542  */
10543
10544
10545 /**
10546  * @class Roo.data.SortTypes
10547  * @singleton
10548  * Defines the default sorting (casting?) comparison functions used when sorting data.
10549  */
10550 Roo.data.SortTypes = {
10551     /**
10552      * Default sort that does nothing
10553      * @param {Mixed} s The value being converted
10554      * @return {Mixed} The comparison value
10555      */
10556     none : function(s){
10557         return s;
10558     },
10559     
10560     /**
10561      * The regular expression used to strip tags
10562      * @type {RegExp}
10563      * @property
10564      */
10565     stripTagsRE : /<\/?[^>]+>/gi,
10566     
10567     /**
10568      * Strips all HTML tags to sort on text only
10569      * @param {Mixed} s The value being converted
10570      * @return {String} The comparison value
10571      */
10572     asText : function(s){
10573         return String(s).replace(this.stripTagsRE, "");
10574     },
10575     
10576     /**
10577      * Strips all HTML tags to sort on text only - Case insensitive
10578      * @param {Mixed} s The value being converted
10579      * @return {String} The comparison value
10580      */
10581     asUCText : function(s){
10582         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10583     },
10584     
10585     /**
10586      * Case insensitive string
10587      * @param {Mixed} s The value being converted
10588      * @return {String} The comparison value
10589      */
10590     asUCString : function(s) {
10591         return String(s).toUpperCase();
10592     },
10593     
10594     /**
10595      * Date sorting
10596      * @param {Mixed} s The value being converted
10597      * @return {Number} The comparison value
10598      */
10599     asDate : function(s) {
10600         if(!s){
10601             return 0;
10602         }
10603         if(s instanceof Date){
10604             return s.getTime();
10605         }
10606         return Date.parse(String(s));
10607     },
10608     
10609     /**
10610      * Float sorting
10611      * @param {Mixed} s The value being converted
10612      * @return {Float} The comparison value
10613      */
10614     asFloat : function(s) {
10615         var val = parseFloat(String(s).replace(/,/g, ""));
10616         if(isNaN(val)) {
10617             val = 0;
10618         }
10619         return val;
10620     },
10621     
10622     /**
10623      * Integer sorting
10624      * @param {Mixed} s The value being converted
10625      * @return {Number} The comparison value
10626      */
10627     asInt : function(s) {
10628         var val = parseInt(String(s).replace(/,/g, ""));
10629         if(isNaN(val)) {
10630             val = 0;
10631         }
10632         return val;
10633     }
10634 };/*
10635  * Based on:
10636  * Ext JS Library 1.1.1
10637  * Copyright(c) 2006-2007, Ext JS, LLC.
10638  *
10639  * Originally Released Under LGPL - original licence link has changed is not relivant.
10640  *
10641  * Fork - LGPL
10642  * <script type="text/javascript">
10643  */
10644
10645 /**
10646 * @class Roo.data.Record
10647  * Instances of this class encapsulate both record <em>definition</em> information, and record
10648  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10649  * to access Records cached in an {@link Roo.data.Store} object.<br>
10650  * <p>
10651  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10652  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10653  * objects.<br>
10654  * <p>
10655  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10656  * @constructor
10657  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10658  * {@link #create}. The parameters are the same.
10659  * @param {Array} data An associative Array of data values keyed by the field name.
10660  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10661  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10662  * not specified an integer id is generated.
10663  */
10664 Roo.data.Record = function(data, id){
10665     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10666     this.data = data;
10667 };
10668
10669 /**
10670  * Generate a constructor for a specific record layout.
10671  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10672  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10673  * Each field definition object may contain the following properties: <ul>
10674  * <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,
10675  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10676  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10677  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10678  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10679  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10680  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10681  * this may be omitted.</p></li>
10682  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10683  * <ul><li>auto (Default, implies no conversion)</li>
10684  * <li>string</li>
10685  * <li>int</li>
10686  * <li>float</li>
10687  * <li>boolean</li>
10688  * <li>date</li></ul></p></li>
10689  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10690  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10691  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10692  * by the Reader into an object that will be stored in the Record. It is passed the
10693  * following parameters:<ul>
10694  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10695  * </ul></p></li>
10696  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10697  * </ul>
10698  * <br>usage:<br><pre><code>
10699 var TopicRecord = Roo.data.Record.create(
10700     {name: 'title', mapping: 'topic_title'},
10701     {name: 'author', mapping: 'username'},
10702     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10703     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10704     {name: 'lastPoster', mapping: 'user2'},
10705     {name: 'excerpt', mapping: 'post_text'}
10706 );
10707
10708 var myNewRecord = new TopicRecord({
10709     title: 'Do my job please',
10710     author: 'noobie',
10711     totalPosts: 1,
10712     lastPost: new Date(),
10713     lastPoster: 'Animal',
10714     excerpt: 'No way dude!'
10715 });
10716 myStore.add(myNewRecord);
10717 </code></pre>
10718  * @method create
10719  * @static
10720  */
10721 Roo.data.Record.create = function(o){
10722     var f = function(){
10723         f.superclass.constructor.apply(this, arguments);
10724     };
10725     Roo.extend(f, Roo.data.Record);
10726     var p = f.prototype;
10727     p.fields = new Roo.util.MixedCollection(false, function(field){
10728         return field.name;
10729     });
10730     for(var i = 0, len = o.length; i < len; i++){
10731         p.fields.add(new Roo.data.Field(o[i]));
10732     }
10733     f.getField = function(name){
10734         return p.fields.get(name);  
10735     };
10736     return f;
10737 };
10738
10739 Roo.data.Record.AUTO_ID = 1000;
10740 Roo.data.Record.EDIT = 'edit';
10741 Roo.data.Record.REJECT = 'reject';
10742 Roo.data.Record.COMMIT = 'commit';
10743
10744 Roo.data.Record.prototype = {
10745     /**
10746      * Readonly flag - true if this record has been modified.
10747      * @type Boolean
10748      */
10749     dirty : false,
10750     editing : false,
10751     error: null,
10752     modified: null,
10753
10754     // private
10755     join : function(store){
10756         this.store = store;
10757     },
10758
10759     /**
10760      * Set the named field to the specified value.
10761      * @param {String} name The name of the field to set.
10762      * @param {Object} value The value to set the field to.
10763      */
10764     set : function(name, value){
10765         if(this.data[name] == value){
10766             return;
10767         }
10768         this.dirty = true;
10769         if(!this.modified){
10770             this.modified = {};
10771         }
10772         if(typeof this.modified[name] == 'undefined'){
10773             this.modified[name] = this.data[name];
10774         }
10775         this.data[name] = value;
10776         if(!this.editing && this.store){
10777             this.store.afterEdit(this);
10778         }       
10779     },
10780
10781     /**
10782      * Get the value of the named field.
10783      * @param {String} name The name of the field to get the value of.
10784      * @return {Object} The value of the field.
10785      */
10786     get : function(name){
10787         return this.data[name]; 
10788     },
10789
10790     // private
10791     beginEdit : function(){
10792         this.editing = true;
10793         this.modified = {}; 
10794     },
10795
10796     // private
10797     cancelEdit : function(){
10798         this.editing = false;
10799         delete this.modified;
10800     },
10801
10802     // private
10803     endEdit : function(){
10804         this.editing = false;
10805         if(this.dirty && this.store){
10806             this.store.afterEdit(this);
10807         }
10808     },
10809
10810     /**
10811      * Usually called by the {@link Roo.data.Store} which owns the Record.
10812      * Rejects all changes made to the Record since either creation, or the last commit operation.
10813      * Modified fields are reverted to their original values.
10814      * <p>
10815      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10816      * of reject operations.
10817      */
10818     reject : function(){
10819         var m = this.modified;
10820         for(var n in m){
10821             if(typeof m[n] != "function"){
10822                 this.data[n] = m[n];
10823             }
10824         }
10825         this.dirty = false;
10826         delete this.modified;
10827         this.editing = false;
10828         if(this.store){
10829             this.store.afterReject(this);
10830         }
10831     },
10832
10833     /**
10834      * Usually called by the {@link Roo.data.Store} which owns the Record.
10835      * Commits all changes made to the Record since either creation, or the last commit operation.
10836      * <p>
10837      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10838      * of commit operations.
10839      */
10840     commit : function(){
10841         this.dirty = false;
10842         delete this.modified;
10843         this.editing = false;
10844         if(this.store){
10845             this.store.afterCommit(this);
10846         }
10847     },
10848
10849     // private
10850     hasError : function(){
10851         return this.error != null;
10852     },
10853
10854     // private
10855     clearError : function(){
10856         this.error = null;
10857     },
10858
10859     /**
10860      * Creates a copy of this record.
10861      * @param {String} id (optional) A new record id if you don't want to use this record's id
10862      * @return {Record}
10863      */
10864     copy : function(newId) {
10865         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10866     }
10867 };/*
10868  * Based on:
10869  * Ext JS Library 1.1.1
10870  * Copyright(c) 2006-2007, Ext JS, LLC.
10871  *
10872  * Originally Released Under LGPL - original licence link has changed is not relivant.
10873  *
10874  * Fork - LGPL
10875  * <script type="text/javascript">
10876  */
10877
10878
10879
10880 /**
10881  * @class Roo.data.Store
10882  * @extends Roo.util.Observable
10883  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10884  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10885  * <p>
10886  * 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
10887  * has no knowledge of the format of the data returned by the Proxy.<br>
10888  * <p>
10889  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10890  * instances from the data object. These records are cached and made available through accessor functions.
10891  * @constructor
10892  * Creates a new Store.
10893  * @param {Object} config A config object containing the objects needed for the Store to access data,
10894  * and read the data into Records.
10895  */
10896 Roo.data.Store = function(config){
10897     this.data = new Roo.util.MixedCollection(false);
10898     this.data.getKey = function(o){
10899         return o.id;
10900     };
10901     this.baseParams = {};
10902     // private
10903     this.paramNames = {
10904         "start" : "start",
10905         "limit" : "limit",
10906         "sort" : "sort",
10907         "dir" : "dir",
10908         "multisort" : "_multisort"
10909     };
10910
10911     if(config && config.data){
10912         this.inlineData = config.data;
10913         delete config.data;
10914     }
10915
10916     Roo.apply(this, config);
10917     
10918     if(this.reader){ // reader passed
10919         this.reader = Roo.factory(this.reader, Roo.data);
10920         this.reader.xmodule = this.xmodule || false;
10921         if(!this.recordType){
10922             this.recordType = this.reader.recordType;
10923         }
10924         if(this.reader.onMetaChange){
10925             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10926         }
10927     }
10928
10929     if(this.recordType){
10930         this.fields = this.recordType.prototype.fields;
10931     }
10932     this.modified = [];
10933
10934     this.addEvents({
10935         /**
10936          * @event datachanged
10937          * Fires when the data cache has changed, and a widget which is using this Store
10938          * as a Record cache should refresh its view.
10939          * @param {Store} this
10940          */
10941         datachanged : true,
10942         /**
10943          * @event metachange
10944          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10945          * @param {Store} this
10946          * @param {Object} meta The JSON metadata
10947          */
10948         metachange : true,
10949         /**
10950          * @event add
10951          * Fires when Records have been added to the Store
10952          * @param {Store} this
10953          * @param {Roo.data.Record[]} records The array of Records added
10954          * @param {Number} index The index at which the record(s) were added
10955          */
10956         add : true,
10957         /**
10958          * @event remove
10959          * Fires when a Record has been removed from the Store
10960          * @param {Store} this
10961          * @param {Roo.data.Record} record The Record that was removed
10962          * @param {Number} index The index at which the record was removed
10963          */
10964         remove : true,
10965         /**
10966          * @event update
10967          * Fires when a Record has been updated
10968          * @param {Store} this
10969          * @param {Roo.data.Record} record The Record that was updated
10970          * @param {String} operation The update operation being performed.  Value may be one of:
10971          * <pre><code>
10972  Roo.data.Record.EDIT
10973  Roo.data.Record.REJECT
10974  Roo.data.Record.COMMIT
10975          * </code></pre>
10976          */
10977         update : true,
10978         /**
10979          * @event clear
10980          * Fires when the data cache has been cleared.
10981          * @param {Store} this
10982          */
10983         clear : true,
10984         /**
10985          * @event beforeload
10986          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10987          * the load action will be canceled.
10988          * @param {Store} this
10989          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10990          */
10991         beforeload : true,
10992         /**
10993          * @event beforeloadadd
10994          * Fires after a new set of Records has been loaded.
10995          * @param {Store} this
10996          * @param {Roo.data.Record[]} records The Records that were loaded
10997          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10998          */
10999         beforeloadadd : true,
11000         /**
11001          * @event load
11002          * Fires after a new set of Records has been loaded, before they are added to the store.
11003          * @param {Store} this
11004          * @param {Roo.data.Record[]} records The Records that were loaded
11005          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11006          * @params {Object} return from reader
11007          */
11008         load : true,
11009         /**
11010          * @event loadexception
11011          * Fires if an exception occurs in the Proxy during loading.
11012          * Called with the signature of the Proxy's "loadexception" event.
11013          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11014          * 
11015          * @param {Proxy} 
11016          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11017          * @param {Object} load options 
11018          * @param {Object} jsonData from your request (normally this contains the Exception)
11019          */
11020         loadexception : true
11021     });
11022     
11023     if(this.proxy){
11024         this.proxy = Roo.factory(this.proxy, Roo.data);
11025         this.proxy.xmodule = this.xmodule || false;
11026         this.relayEvents(this.proxy,  ["loadexception"]);
11027     }
11028     this.sortToggle = {};
11029     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11030
11031     Roo.data.Store.superclass.constructor.call(this);
11032
11033     if(this.inlineData){
11034         this.loadData(this.inlineData);
11035         delete this.inlineData;
11036     }
11037 };
11038
11039 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11040      /**
11041     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11042     * without a remote query - used by combo/forms at present.
11043     */
11044     
11045     /**
11046     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11047     */
11048     /**
11049     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11050     */
11051     /**
11052     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11053     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11054     */
11055     /**
11056     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11057     * on any HTTP request
11058     */
11059     /**
11060     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11061     */
11062     /**
11063     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11064     */
11065     multiSort: false,
11066     /**
11067     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11068     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11069     */
11070     remoteSort : false,
11071
11072     /**
11073     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11074      * loaded or when a record is removed. (defaults to false).
11075     */
11076     pruneModifiedRecords : false,
11077
11078     // private
11079     lastOptions : null,
11080
11081     /**
11082      * Add Records to the Store and fires the add event.
11083      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11084      */
11085     add : function(records){
11086         records = [].concat(records);
11087         for(var i = 0, len = records.length; i < len; i++){
11088             records[i].join(this);
11089         }
11090         var index = this.data.length;
11091         this.data.addAll(records);
11092         this.fireEvent("add", this, records, index);
11093     },
11094
11095     /**
11096      * Remove a Record from the Store and fires the remove event.
11097      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11098      */
11099     remove : function(record){
11100         var index = this.data.indexOf(record);
11101         this.data.removeAt(index);
11102         if(this.pruneModifiedRecords){
11103             this.modified.remove(record);
11104         }
11105         this.fireEvent("remove", this, record, index);
11106     },
11107
11108     /**
11109      * Remove all Records from the Store and fires the clear event.
11110      */
11111     removeAll : function(){
11112         this.data.clear();
11113         if(this.pruneModifiedRecords){
11114             this.modified = [];
11115         }
11116         this.fireEvent("clear", this);
11117     },
11118
11119     /**
11120      * Inserts Records to the Store at the given index and fires the add event.
11121      * @param {Number} index The start index at which to insert the passed Records.
11122      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11123      */
11124     insert : function(index, records){
11125         records = [].concat(records);
11126         for(var i = 0, len = records.length; i < len; i++){
11127             this.data.insert(index, records[i]);
11128             records[i].join(this);
11129         }
11130         this.fireEvent("add", this, records, index);
11131     },
11132
11133     /**
11134      * Get the index within the cache of the passed Record.
11135      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11136      * @return {Number} The index of the passed Record. Returns -1 if not found.
11137      */
11138     indexOf : function(record){
11139         return this.data.indexOf(record);
11140     },
11141
11142     /**
11143      * Get the index within the cache of the Record with the passed id.
11144      * @param {String} id The id of the Record to find.
11145      * @return {Number} The index of the Record. Returns -1 if not found.
11146      */
11147     indexOfId : function(id){
11148         return this.data.indexOfKey(id);
11149     },
11150
11151     /**
11152      * Get the Record with the specified id.
11153      * @param {String} id The id of the Record to find.
11154      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11155      */
11156     getById : function(id){
11157         return this.data.key(id);
11158     },
11159
11160     /**
11161      * Get the Record at the specified index.
11162      * @param {Number} index The index of the Record to find.
11163      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11164      */
11165     getAt : function(index){
11166         return this.data.itemAt(index);
11167     },
11168
11169     /**
11170      * Returns a range of Records between specified indices.
11171      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11172      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11173      * @return {Roo.data.Record[]} An array of Records
11174      */
11175     getRange : function(start, end){
11176         return this.data.getRange(start, end);
11177     },
11178
11179     // private
11180     storeOptions : function(o){
11181         o = Roo.apply({}, o);
11182         delete o.callback;
11183         delete o.scope;
11184         this.lastOptions = o;
11185     },
11186
11187     /**
11188      * Loads the Record cache from the configured Proxy using the configured Reader.
11189      * <p>
11190      * If using remote paging, then the first load call must specify the <em>start</em>
11191      * and <em>limit</em> properties in the options.params property to establish the initial
11192      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11193      * <p>
11194      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11195      * and this call will return before the new data has been loaded. Perform any post-processing
11196      * in a callback function, or in a "load" event handler.</strong>
11197      * <p>
11198      * @param {Object} options An object containing properties which control loading options:<ul>
11199      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11200      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11201      * passed the following arguments:<ul>
11202      * <li>r : Roo.data.Record[]</li>
11203      * <li>options: Options object from the load call</li>
11204      * <li>success: Boolean success indicator</li></ul></li>
11205      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11206      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11207      * </ul>
11208      */
11209     load : function(options){
11210         options = options || {};
11211         if(this.fireEvent("beforeload", this, options) !== false){
11212             this.storeOptions(options);
11213             var p = Roo.apply(options.params || {}, this.baseParams);
11214             // if meta was not loaded from remote source.. try requesting it.
11215             if (!this.reader.metaFromRemote) {
11216                 p._requestMeta = 1;
11217             }
11218             if(this.sortInfo && this.remoteSort){
11219                 var pn = this.paramNames;
11220                 p[pn["sort"]] = this.sortInfo.field;
11221                 p[pn["dir"]] = this.sortInfo.direction;
11222             }
11223             if (this.multiSort) {
11224                 var pn = this.paramNames;
11225                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11226             }
11227             
11228             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11229         }
11230     },
11231
11232     /**
11233      * Reloads the Record cache from the configured Proxy using the configured Reader and
11234      * the options from the last load operation performed.
11235      * @param {Object} options (optional) An object containing properties which may override the options
11236      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11237      * the most recently used options are reused).
11238      */
11239     reload : function(options){
11240         this.load(Roo.applyIf(options||{}, this.lastOptions));
11241     },
11242
11243     // private
11244     // Called as a callback by the Reader during a load operation.
11245     loadRecords : function(o, options, success){
11246         if(!o || success === false){
11247             if(success !== false){
11248                 this.fireEvent("load", this, [], options, o);
11249             }
11250             if(options.callback){
11251                 options.callback.call(options.scope || this, [], options, false);
11252             }
11253             return;
11254         }
11255         // if data returned failure - throw an exception.
11256         if (o.success === false) {
11257             // show a message if no listener is registered.
11258             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11259                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11260             }
11261             // loadmask wil be hooked into this..
11262             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11263             return;
11264         }
11265         var r = o.records, t = o.totalRecords || r.length;
11266         
11267         this.fireEvent("beforeloadadd", this, r, options, o);
11268         
11269         if(!options || options.add !== true){
11270             if(this.pruneModifiedRecords){
11271                 this.modified = [];
11272             }
11273             for(var i = 0, len = r.length; i < len; i++){
11274                 r[i].join(this);
11275             }
11276             if(this.snapshot){
11277                 this.data = this.snapshot;
11278                 delete this.snapshot;
11279             }
11280             this.data.clear();
11281             this.data.addAll(r);
11282             this.totalLength = t;
11283             this.applySort();
11284             this.fireEvent("datachanged", this);
11285         }else{
11286             this.totalLength = Math.max(t, this.data.length+r.length);
11287             this.add(r);
11288         }
11289         
11290         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11291                 
11292             var e = new Roo.data.Record({});
11293
11294             e.set(this.parent.displayField, this.parent.emptyTitle);
11295             e.set(this.parent.valueField, '');
11296
11297             this.insert(0, e);
11298         }
11299             
11300         this.fireEvent("load", this, r, options, o);
11301         if(options.callback){
11302             options.callback.call(options.scope || this, r, options, true);
11303         }
11304     },
11305
11306
11307     /**
11308      * Loads data from a passed data block. A Reader which understands the format of the data
11309      * must have been configured in the constructor.
11310      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11311      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11312      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11313      */
11314     loadData : function(o, append){
11315         var r = this.reader.readRecords(o);
11316         this.loadRecords(r, {add: append}, true);
11317     },
11318
11319     /**
11320      * Gets the number of cached records.
11321      * <p>
11322      * <em>If using paging, this may not be the total size of the dataset. If the data object
11323      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11324      * the data set size</em>
11325      */
11326     getCount : function(){
11327         return this.data.length || 0;
11328     },
11329
11330     /**
11331      * Gets the total number of records in the dataset as returned by the server.
11332      * <p>
11333      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11334      * the dataset size</em>
11335      */
11336     getTotalCount : function(){
11337         return this.totalLength || 0;
11338     },
11339
11340     /**
11341      * Returns the sort state of the Store as an object with two properties:
11342      * <pre><code>
11343  field {String} The name of the field by which the Records are sorted
11344  direction {String} The sort order, "ASC" or "DESC"
11345      * </code></pre>
11346      */
11347     getSortState : function(){
11348         return this.sortInfo;
11349     },
11350
11351     // private
11352     applySort : function(){
11353         if(this.sortInfo && !this.remoteSort){
11354             var s = this.sortInfo, f = s.field;
11355             var st = this.fields.get(f).sortType;
11356             var fn = function(r1, r2){
11357                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11358                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11359             };
11360             this.data.sort(s.direction, fn);
11361             if(this.snapshot && this.snapshot != this.data){
11362                 this.snapshot.sort(s.direction, fn);
11363             }
11364         }
11365     },
11366
11367     /**
11368      * Sets the default sort column and order to be used by the next load operation.
11369      * @param {String} fieldName The name of the field to sort by.
11370      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11371      */
11372     setDefaultSort : function(field, dir){
11373         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11374     },
11375
11376     /**
11377      * Sort the Records.
11378      * If remote sorting is used, the sort is performed on the server, and the cache is
11379      * reloaded. If local sorting is used, the cache is sorted internally.
11380      * @param {String} fieldName The name of the field to sort by.
11381      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11382      */
11383     sort : function(fieldName, dir){
11384         var f = this.fields.get(fieldName);
11385         if(!dir){
11386             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11387             
11388             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11389                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11390             }else{
11391                 dir = f.sortDir;
11392             }
11393         }
11394         this.sortToggle[f.name] = dir;
11395         this.sortInfo = {field: f.name, direction: dir};
11396         if(!this.remoteSort){
11397             this.applySort();
11398             this.fireEvent("datachanged", this);
11399         }else{
11400             this.load(this.lastOptions);
11401         }
11402     },
11403
11404     /**
11405      * Calls the specified function for each of the Records in the cache.
11406      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11407      * Returning <em>false</em> aborts and exits the iteration.
11408      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11409      */
11410     each : function(fn, scope){
11411         this.data.each(fn, scope);
11412     },
11413
11414     /**
11415      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11416      * (e.g., during paging).
11417      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11418      */
11419     getModifiedRecords : function(){
11420         return this.modified;
11421     },
11422
11423     // private
11424     createFilterFn : function(property, value, anyMatch){
11425         if(!value.exec){ // not a regex
11426             value = String(value);
11427             if(value.length == 0){
11428                 return false;
11429             }
11430             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11431         }
11432         return function(r){
11433             return value.test(r.data[property]);
11434         };
11435     },
11436
11437     /**
11438      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11439      * @param {String} property A field on your records
11440      * @param {Number} start The record index to start at (defaults to 0)
11441      * @param {Number} end The last record index to include (defaults to length - 1)
11442      * @return {Number} The sum
11443      */
11444     sum : function(property, start, end){
11445         var rs = this.data.items, v = 0;
11446         start = start || 0;
11447         end = (end || end === 0) ? end : rs.length-1;
11448
11449         for(var i = start; i <= end; i++){
11450             v += (rs[i].data[property] || 0);
11451         }
11452         return v;
11453     },
11454
11455     /**
11456      * Filter the records by a specified property.
11457      * @param {String} field A field on your records
11458      * @param {String/RegExp} value Either a string that the field
11459      * should start with or a RegExp to test against the field
11460      * @param {Boolean} anyMatch True to match any part not just the beginning
11461      */
11462     filter : function(property, value, anyMatch){
11463         var fn = this.createFilterFn(property, value, anyMatch);
11464         return fn ? this.filterBy(fn) : this.clearFilter();
11465     },
11466
11467     /**
11468      * Filter by a function. The specified function will be called with each
11469      * record in this data source. If the function returns true the record is included,
11470      * otherwise it is filtered.
11471      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11472      * @param {Object} scope (optional) The scope of the function (defaults to this)
11473      */
11474     filterBy : function(fn, scope){
11475         this.snapshot = this.snapshot || this.data;
11476         this.data = this.queryBy(fn, scope||this);
11477         this.fireEvent("datachanged", this);
11478     },
11479
11480     /**
11481      * Query the records by a specified property.
11482      * @param {String} field A field on your records
11483      * @param {String/RegExp} value Either a string that the field
11484      * should start with or a RegExp to test against the field
11485      * @param {Boolean} anyMatch True to match any part not just the beginning
11486      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11487      */
11488     query : function(property, value, anyMatch){
11489         var fn = this.createFilterFn(property, value, anyMatch);
11490         return fn ? this.queryBy(fn) : this.data.clone();
11491     },
11492
11493     /**
11494      * Query by a function. The specified function will be called with each
11495      * record in this data source. If the function returns true the record is included
11496      * in the results.
11497      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11498      * @param {Object} scope (optional) The scope of the function (defaults to this)
11499       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11500      **/
11501     queryBy : function(fn, scope){
11502         var data = this.snapshot || this.data;
11503         return data.filterBy(fn, scope||this);
11504     },
11505
11506     /**
11507      * Collects unique values for a particular dataIndex from this store.
11508      * @param {String} dataIndex The property to collect
11509      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11510      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11511      * @return {Array} An array of the unique values
11512      **/
11513     collect : function(dataIndex, allowNull, bypassFilter){
11514         var d = (bypassFilter === true && this.snapshot) ?
11515                 this.snapshot.items : this.data.items;
11516         var v, sv, r = [], l = {};
11517         for(var i = 0, len = d.length; i < len; i++){
11518             v = d[i].data[dataIndex];
11519             sv = String(v);
11520             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11521                 l[sv] = true;
11522                 r[r.length] = v;
11523             }
11524         }
11525         return r;
11526     },
11527
11528     /**
11529      * Revert to a view of the Record cache with no filtering applied.
11530      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11531      */
11532     clearFilter : function(suppressEvent){
11533         if(this.snapshot && this.snapshot != this.data){
11534             this.data = this.snapshot;
11535             delete this.snapshot;
11536             if(suppressEvent !== true){
11537                 this.fireEvent("datachanged", this);
11538             }
11539         }
11540     },
11541
11542     // private
11543     afterEdit : function(record){
11544         if(this.modified.indexOf(record) == -1){
11545             this.modified.push(record);
11546         }
11547         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11548     },
11549     
11550     // private
11551     afterReject : function(record){
11552         this.modified.remove(record);
11553         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11554     },
11555
11556     // private
11557     afterCommit : function(record){
11558         this.modified.remove(record);
11559         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11560     },
11561
11562     /**
11563      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11564      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11565      */
11566     commitChanges : function(){
11567         var m = this.modified.slice(0);
11568         this.modified = [];
11569         for(var i = 0, len = m.length; i < len; i++){
11570             m[i].commit();
11571         }
11572     },
11573
11574     /**
11575      * Cancel outstanding changes on all changed records.
11576      */
11577     rejectChanges : function(){
11578         var m = this.modified.slice(0);
11579         this.modified = [];
11580         for(var i = 0, len = m.length; i < len; i++){
11581             m[i].reject();
11582         }
11583     },
11584
11585     onMetaChange : function(meta, rtype, o){
11586         this.recordType = rtype;
11587         this.fields = rtype.prototype.fields;
11588         delete this.snapshot;
11589         this.sortInfo = meta.sortInfo || this.sortInfo;
11590         this.modified = [];
11591         this.fireEvent('metachange', this, this.reader.meta);
11592     },
11593     
11594     moveIndex : function(data, type)
11595     {
11596         var index = this.indexOf(data);
11597         
11598         var newIndex = index + type;
11599         
11600         this.remove(data);
11601         
11602         this.insert(newIndex, data);
11603         
11604     }
11605 });/*
11606  * Based on:
11607  * Ext JS Library 1.1.1
11608  * Copyright(c) 2006-2007, Ext JS, LLC.
11609  *
11610  * Originally Released Under LGPL - original licence link has changed is not relivant.
11611  *
11612  * Fork - LGPL
11613  * <script type="text/javascript">
11614  */
11615
11616 /**
11617  * @class Roo.data.SimpleStore
11618  * @extends Roo.data.Store
11619  * Small helper class to make creating Stores from Array data easier.
11620  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11621  * @cfg {Array} fields An array of field definition objects, or field name strings.
11622  * @cfg {Array} data The multi-dimensional array of data
11623  * @constructor
11624  * @param {Object} config
11625  */
11626 Roo.data.SimpleStore = function(config){
11627     Roo.data.SimpleStore.superclass.constructor.call(this, {
11628         isLocal : true,
11629         reader: new Roo.data.ArrayReader({
11630                 id: config.id
11631             },
11632             Roo.data.Record.create(config.fields)
11633         ),
11634         proxy : new Roo.data.MemoryProxy(config.data)
11635     });
11636     this.load();
11637 };
11638 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11639  * Based on:
11640  * Ext JS Library 1.1.1
11641  * Copyright(c) 2006-2007, Ext JS, LLC.
11642  *
11643  * Originally Released Under LGPL - original licence link has changed is not relivant.
11644  *
11645  * Fork - LGPL
11646  * <script type="text/javascript">
11647  */
11648
11649 /**
11650 /**
11651  * @extends Roo.data.Store
11652  * @class Roo.data.JsonStore
11653  * Small helper class to make creating Stores for JSON data easier. <br/>
11654 <pre><code>
11655 var store = new Roo.data.JsonStore({
11656     url: 'get-images.php',
11657     root: 'images',
11658     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11659 });
11660 </code></pre>
11661  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11662  * JsonReader and HttpProxy (unless inline data is provided).</b>
11663  * @cfg {Array} fields An array of field definition objects, or field name strings.
11664  * @constructor
11665  * @param {Object} config
11666  */
11667 Roo.data.JsonStore = function(c){
11668     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11669         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11670         reader: new Roo.data.JsonReader(c, c.fields)
11671     }));
11672 };
11673 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11674  * Based on:
11675  * Ext JS Library 1.1.1
11676  * Copyright(c) 2006-2007, Ext JS, LLC.
11677  *
11678  * Originally Released Under LGPL - original licence link has changed is not relivant.
11679  *
11680  * Fork - LGPL
11681  * <script type="text/javascript">
11682  */
11683
11684  
11685 Roo.data.Field = function(config){
11686     if(typeof config == "string"){
11687         config = {name: config};
11688     }
11689     Roo.apply(this, config);
11690     
11691     if(!this.type){
11692         this.type = "auto";
11693     }
11694     
11695     var st = Roo.data.SortTypes;
11696     // named sortTypes are supported, here we look them up
11697     if(typeof this.sortType == "string"){
11698         this.sortType = st[this.sortType];
11699     }
11700     
11701     // set default sortType for strings and dates
11702     if(!this.sortType){
11703         switch(this.type){
11704             case "string":
11705                 this.sortType = st.asUCString;
11706                 break;
11707             case "date":
11708                 this.sortType = st.asDate;
11709                 break;
11710             default:
11711                 this.sortType = st.none;
11712         }
11713     }
11714
11715     // define once
11716     var stripRe = /[\$,%]/g;
11717
11718     // prebuilt conversion function for this field, instead of
11719     // switching every time we're reading a value
11720     if(!this.convert){
11721         var cv, dateFormat = this.dateFormat;
11722         switch(this.type){
11723             case "":
11724             case "auto":
11725             case undefined:
11726                 cv = function(v){ return v; };
11727                 break;
11728             case "string":
11729                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11730                 break;
11731             case "int":
11732                 cv = function(v){
11733                     return v !== undefined && v !== null && v !== '' ?
11734                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11735                     };
11736                 break;
11737             case "float":
11738                 cv = function(v){
11739                     return v !== undefined && v !== null && v !== '' ?
11740                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11741                     };
11742                 break;
11743             case "bool":
11744             case "boolean":
11745                 cv = function(v){ return v === true || v === "true" || v == 1; };
11746                 break;
11747             case "date":
11748                 cv = function(v){
11749                     if(!v){
11750                         return '';
11751                     }
11752                     if(v instanceof Date){
11753                         return v;
11754                     }
11755                     if(dateFormat){
11756                         if(dateFormat == "timestamp"){
11757                             return new Date(v*1000);
11758                         }
11759                         return Date.parseDate(v, dateFormat);
11760                     }
11761                     var parsed = Date.parse(v);
11762                     return parsed ? new Date(parsed) : null;
11763                 };
11764              break;
11765             
11766         }
11767         this.convert = cv;
11768     }
11769 };
11770
11771 Roo.data.Field.prototype = {
11772     dateFormat: null,
11773     defaultValue: "",
11774     mapping: null,
11775     sortType : null,
11776     sortDir : "ASC"
11777 };/*
11778  * Based on:
11779  * Ext JS Library 1.1.1
11780  * Copyright(c) 2006-2007, Ext JS, LLC.
11781  *
11782  * Originally Released Under LGPL - original licence link has changed is not relivant.
11783  *
11784  * Fork - LGPL
11785  * <script type="text/javascript">
11786  */
11787  
11788 // Base class for reading structured data from a data source.  This class is intended to be
11789 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11790
11791 /**
11792  * @class Roo.data.DataReader
11793  * Base class for reading structured data from a data source.  This class is intended to be
11794  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11795  */
11796
11797 Roo.data.DataReader = function(meta, recordType){
11798     
11799     this.meta = meta;
11800     
11801     this.recordType = recordType instanceof Array ? 
11802         Roo.data.Record.create(recordType) : recordType;
11803 };
11804
11805 Roo.data.DataReader.prototype = {
11806      /**
11807      * Create an empty record
11808      * @param {Object} data (optional) - overlay some values
11809      * @return {Roo.data.Record} record created.
11810      */
11811     newRow :  function(d) {
11812         var da =  {};
11813         this.recordType.prototype.fields.each(function(c) {
11814             switch( c.type) {
11815                 case 'int' : da[c.name] = 0; break;
11816                 case 'date' : da[c.name] = new Date(); break;
11817                 case 'float' : da[c.name] = 0.0; break;
11818                 case 'boolean' : da[c.name] = false; break;
11819                 default : da[c.name] = ""; break;
11820             }
11821             
11822         });
11823         return new this.recordType(Roo.apply(da, d));
11824     }
11825     
11826 };/*
11827  * Based on:
11828  * Ext JS Library 1.1.1
11829  * Copyright(c) 2006-2007, Ext JS, LLC.
11830  *
11831  * Originally Released Under LGPL - original licence link has changed is not relivant.
11832  *
11833  * Fork - LGPL
11834  * <script type="text/javascript">
11835  */
11836
11837 /**
11838  * @class Roo.data.DataProxy
11839  * @extends Roo.data.Observable
11840  * This class is an abstract base class for implementations which provide retrieval of
11841  * unformatted data objects.<br>
11842  * <p>
11843  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11844  * (of the appropriate type which knows how to parse the data object) to provide a block of
11845  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11846  * <p>
11847  * Custom implementations must implement the load method as described in
11848  * {@link Roo.data.HttpProxy#load}.
11849  */
11850 Roo.data.DataProxy = function(){
11851     this.addEvents({
11852         /**
11853          * @event beforeload
11854          * Fires before a network request is made to retrieve a data object.
11855          * @param {Object} This DataProxy object.
11856          * @param {Object} params The params parameter to the load function.
11857          */
11858         beforeload : true,
11859         /**
11860          * @event load
11861          * Fires before the load method's callback is called.
11862          * @param {Object} This DataProxy object.
11863          * @param {Object} o The data object.
11864          * @param {Object} arg The callback argument object passed to the load function.
11865          */
11866         load : true,
11867         /**
11868          * @event loadexception
11869          * Fires if an Exception occurs during data retrieval.
11870          * @param {Object} This DataProxy object.
11871          * @param {Object} o The data object.
11872          * @param {Object} arg The callback argument object passed to the load function.
11873          * @param {Object} e The Exception.
11874          */
11875         loadexception : true
11876     });
11877     Roo.data.DataProxy.superclass.constructor.call(this);
11878 };
11879
11880 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11881
11882     /**
11883      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11884      */
11885 /*
11886  * Based on:
11887  * Ext JS Library 1.1.1
11888  * Copyright(c) 2006-2007, Ext JS, LLC.
11889  *
11890  * Originally Released Under LGPL - original licence link has changed is not relivant.
11891  *
11892  * Fork - LGPL
11893  * <script type="text/javascript">
11894  */
11895 /**
11896  * @class Roo.data.MemoryProxy
11897  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11898  * to the Reader when its load method is called.
11899  * @constructor
11900  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11901  */
11902 Roo.data.MemoryProxy = function(data){
11903     if (data.data) {
11904         data = data.data;
11905     }
11906     Roo.data.MemoryProxy.superclass.constructor.call(this);
11907     this.data = data;
11908 };
11909
11910 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11911     
11912     /**
11913      * Load data from the requested source (in this case an in-memory
11914      * data object passed to the constructor), read the data object into
11915      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11916      * process that block using the passed callback.
11917      * @param {Object} params This parameter is not used by the MemoryProxy class.
11918      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11919      * object into a block of Roo.data.Records.
11920      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11921      * The function must be passed <ul>
11922      * <li>The Record block object</li>
11923      * <li>The "arg" argument from the load function</li>
11924      * <li>A boolean success indicator</li>
11925      * </ul>
11926      * @param {Object} scope The scope in which to call the callback
11927      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11928      */
11929     load : function(params, reader, callback, scope, arg){
11930         params = params || {};
11931         var result;
11932         try {
11933             result = reader.readRecords(this.data);
11934         }catch(e){
11935             this.fireEvent("loadexception", this, arg, null, e);
11936             callback.call(scope, null, arg, false);
11937             return;
11938         }
11939         callback.call(scope, result, arg, true);
11940     },
11941     
11942     // private
11943     update : function(params, records){
11944         
11945     }
11946 });/*
11947  * Based on:
11948  * Ext JS Library 1.1.1
11949  * Copyright(c) 2006-2007, Ext JS, LLC.
11950  *
11951  * Originally Released Under LGPL - original licence link has changed is not relivant.
11952  *
11953  * Fork - LGPL
11954  * <script type="text/javascript">
11955  */
11956 /**
11957  * @class Roo.data.HttpProxy
11958  * @extends Roo.data.DataProxy
11959  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11960  * configured to reference a certain URL.<br><br>
11961  * <p>
11962  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11963  * from which the running page was served.<br><br>
11964  * <p>
11965  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11966  * <p>
11967  * Be aware that to enable the browser to parse an XML document, the server must set
11968  * the Content-Type header in the HTTP response to "text/xml".
11969  * @constructor
11970  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11971  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11972  * will be used to make the request.
11973  */
11974 Roo.data.HttpProxy = function(conn){
11975     Roo.data.HttpProxy.superclass.constructor.call(this);
11976     // is conn a conn config or a real conn?
11977     this.conn = conn;
11978     this.useAjax = !conn || !conn.events;
11979   
11980 };
11981
11982 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11983     // thse are take from connection...
11984     
11985     /**
11986      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11987      */
11988     /**
11989      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11990      * extra parameters to each request made by this object. (defaults to undefined)
11991      */
11992     /**
11993      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11994      *  to each request made by this object. (defaults to undefined)
11995      */
11996     /**
11997      * @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)
11998      */
11999     /**
12000      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12001      */
12002      /**
12003      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12004      * @type Boolean
12005      */
12006   
12007
12008     /**
12009      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12010      * @type Boolean
12011      */
12012     /**
12013      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12014      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12015      * a finer-grained basis than the DataProxy events.
12016      */
12017     getConnection : function(){
12018         return this.useAjax ? Roo.Ajax : this.conn;
12019     },
12020
12021     /**
12022      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12023      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12024      * process that block using the passed callback.
12025      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12026      * for the request to the remote server.
12027      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12028      * object into a block of Roo.data.Records.
12029      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12030      * The function must be passed <ul>
12031      * <li>The Record block object</li>
12032      * <li>The "arg" argument from the load function</li>
12033      * <li>A boolean success indicator</li>
12034      * </ul>
12035      * @param {Object} scope The scope in which to call the callback
12036      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12037      */
12038     load : function(params, reader, callback, scope, arg){
12039         if(this.fireEvent("beforeload", this, params) !== false){
12040             var  o = {
12041                 params : params || {},
12042                 request: {
12043                     callback : callback,
12044                     scope : scope,
12045                     arg : arg
12046                 },
12047                 reader: reader,
12048                 callback : this.loadResponse,
12049                 scope: this
12050             };
12051             if(this.useAjax){
12052                 Roo.applyIf(o, this.conn);
12053                 if(this.activeRequest){
12054                     Roo.Ajax.abort(this.activeRequest);
12055                 }
12056                 this.activeRequest = Roo.Ajax.request(o);
12057             }else{
12058                 this.conn.request(o);
12059             }
12060         }else{
12061             callback.call(scope||this, null, arg, false);
12062         }
12063     },
12064
12065     // private
12066     loadResponse : function(o, success, response){
12067         delete this.activeRequest;
12068         if(!success){
12069             this.fireEvent("loadexception", this, o, response);
12070             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12071             return;
12072         }
12073         var result;
12074         try {
12075             result = o.reader.read(response);
12076         }catch(e){
12077             this.fireEvent("loadexception", this, o, response, e);
12078             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12079             return;
12080         }
12081         
12082         this.fireEvent("load", this, o, o.request.arg);
12083         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12084     },
12085
12086     // private
12087     update : function(dataSet){
12088
12089     },
12090
12091     // private
12092     updateResponse : function(dataSet){
12093
12094     }
12095 });/*
12096  * Based on:
12097  * Ext JS Library 1.1.1
12098  * Copyright(c) 2006-2007, Ext JS, LLC.
12099  *
12100  * Originally Released Under LGPL - original licence link has changed is not relivant.
12101  *
12102  * Fork - LGPL
12103  * <script type="text/javascript">
12104  */
12105
12106 /**
12107  * @class Roo.data.ScriptTagProxy
12108  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12109  * other than the originating domain of the running page.<br><br>
12110  * <p>
12111  * <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
12112  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12113  * <p>
12114  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12115  * source code that is used as the source inside a &lt;script> tag.<br><br>
12116  * <p>
12117  * In order for the browser to process the returned data, the server must wrap the data object
12118  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12119  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12120  * depending on whether the callback name was passed:
12121  * <p>
12122  * <pre><code>
12123 boolean scriptTag = false;
12124 String cb = request.getParameter("callback");
12125 if (cb != null) {
12126     scriptTag = true;
12127     response.setContentType("text/javascript");
12128 } else {
12129     response.setContentType("application/x-json");
12130 }
12131 Writer out = response.getWriter();
12132 if (scriptTag) {
12133     out.write(cb + "(");
12134 }
12135 out.print(dataBlock.toJsonString());
12136 if (scriptTag) {
12137     out.write(");");
12138 }
12139 </pre></code>
12140  *
12141  * @constructor
12142  * @param {Object} config A configuration object.
12143  */
12144 Roo.data.ScriptTagProxy = function(config){
12145     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12146     Roo.apply(this, config);
12147     this.head = document.getElementsByTagName("head")[0];
12148 };
12149
12150 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12151
12152 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12153     /**
12154      * @cfg {String} url The URL from which to request the data object.
12155      */
12156     /**
12157      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12158      */
12159     timeout : 30000,
12160     /**
12161      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12162      * the server the name of the callback function set up by the load call to process the returned data object.
12163      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12164      * javascript output which calls this named function passing the data object as its only parameter.
12165      */
12166     callbackParam : "callback",
12167     /**
12168      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12169      * name to the request.
12170      */
12171     nocache : true,
12172
12173     /**
12174      * Load data from the configured URL, read the data object into
12175      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12176      * process that block using the passed callback.
12177      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12178      * for the request to the remote server.
12179      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12180      * object into a block of Roo.data.Records.
12181      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12182      * The function must be passed <ul>
12183      * <li>The Record block object</li>
12184      * <li>The "arg" argument from the load function</li>
12185      * <li>A boolean success indicator</li>
12186      * </ul>
12187      * @param {Object} scope The scope in which to call the callback
12188      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12189      */
12190     load : function(params, reader, callback, scope, arg){
12191         if(this.fireEvent("beforeload", this, params) !== false){
12192
12193             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12194
12195             var url = this.url;
12196             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12197             if(this.nocache){
12198                 url += "&_dc=" + (new Date().getTime());
12199             }
12200             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12201             var trans = {
12202                 id : transId,
12203                 cb : "stcCallback"+transId,
12204                 scriptId : "stcScript"+transId,
12205                 params : params,
12206                 arg : arg,
12207                 url : url,
12208                 callback : callback,
12209                 scope : scope,
12210                 reader : reader
12211             };
12212             var conn = this;
12213
12214             window[trans.cb] = function(o){
12215                 conn.handleResponse(o, trans);
12216             };
12217
12218             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12219
12220             if(this.autoAbort !== false){
12221                 this.abort();
12222             }
12223
12224             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12225
12226             var script = document.createElement("script");
12227             script.setAttribute("src", url);
12228             script.setAttribute("type", "text/javascript");
12229             script.setAttribute("id", trans.scriptId);
12230             this.head.appendChild(script);
12231
12232             this.trans = trans;
12233         }else{
12234             callback.call(scope||this, null, arg, false);
12235         }
12236     },
12237
12238     // private
12239     isLoading : function(){
12240         return this.trans ? true : false;
12241     },
12242
12243     /**
12244      * Abort the current server request.
12245      */
12246     abort : function(){
12247         if(this.isLoading()){
12248             this.destroyTrans(this.trans);
12249         }
12250     },
12251
12252     // private
12253     destroyTrans : function(trans, isLoaded){
12254         this.head.removeChild(document.getElementById(trans.scriptId));
12255         clearTimeout(trans.timeoutId);
12256         if(isLoaded){
12257             window[trans.cb] = undefined;
12258             try{
12259                 delete window[trans.cb];
12260             }catch(e){}
12261         }else{
12262             // if hasn't been loaded, wait for load to remove it to prevent script error
12263             window[trans.cb] = function(){
12264                 window[trans.cb] = undefined;
12265                 try{
12266                     delete window[trans.cb];
12267                 }catch(e){}
12268             };
12269         }
12270     },
12271
12272     // private
12273     handleResponse : function(o, trans){
12274         this.trans = false;
12275         this.destroyTrans(trans, true);
12276         var result;
12277         try {
12278             result = trans.reader.readRecords(o);
12279         }catch(e){
12280             this.fireEvent("loadexception", this, o, trans.arg, e);
12281             trans.callback.call(trans.scope||window, null, trans.arg, false);
12282             return;
12283         }
12284         this.fireEvent("load", this, o, trans.arg);
12285         trans.callback.call(trans.scope||window, result, trans.arg, true);
12286     },
12287
12288     // private
12289     handleFailure : function(trans){
12290         this.trans = false;
12291         this.destroyTrans(trans, false);
12292         this.fireEvent("loadexception", this, null, trans.arg);
12293         trans.callback.call(trans.scope||window, null, trans.arg, false);
12294     }
12295 });/*
12296  * Based on:
12297  * Ext JS Library 1.1.1
12298  * Copyright(c) 2006-2007, Ext JS, LLC.
12299  *
12300  * Originally Released Under LGPL - original licence link has changed is not relivant.
12301  *
12302  * Fork - LGPL
12303  * <script type="text/javascript">
12304  */
12305
12306 /**
12307  * @class Roo.data.JsonReader
12308  * @extends Roo.data.DataReader
12309  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12310  * based on mappings in a provided Roo.data.Record constructor.
12311  * 
12312  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12313  * in the reply previously. 
12314  * 
12315  * <p>
12316  * Example code:
12317  * <pre><code>
12318 var RecordDef = Roo.data.Record.create([
12319     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12320     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12321 ]);
12322 var myReader = new Roo.data.JsonReader({
12323     totalProperty: "results",    // The property which contains the total dataset size (optional)
12324     root: "rows",                // The property which contains an Array of row objects
12325     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12326 }, RecordDef);
12327 </code></pre>
12328  * <p>
12329  * This would consume a JSON file like this:
12330  * <pre><code>
12331 { 'results': 2, 'rows': [
12332     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12333     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12334 }
12335 </code></pre>
12336  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12337  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12338  * paged from the remote server.
12339  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12340  * @cfg {String} root name of the property which contains the Array of row objects.
12341  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12342  * @cfg {Array} fields Array of field definition objects
12343  * @constructor
12344  * Create a new JsonReader
12345  * @param {Object} meta Metadata configuration options
12346  * @param {Object} recordType Either an Array of field definition objects,
12347  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12348  */
12349 Roo.data.JsonReader = function(meta, recordType){
12350     
12351     meta = meta || {};
12352     // set some defaults:
12353     Roo.applyIf(meta, {
12354         totalProperty: 'total',
12355         successProperty : 'success',
12356         root : 'data',
12357         id : 'id'
12358     });
12359     
12360     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12361 };
12362 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12363     
12364     /**
12365      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12366      * Used by Store query builder to append _requestMeta to params.
12367      * 
12368      */
12369     metaFromRemote : false,
12370     /**
12371      * This method is only used by a DataProxy which has retrieved data from a remote server.
12372      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12373      * @return {Object} data A data block which is used by an Roo.data.Store object as
12374      * a cache of Roo.data.Records.
12375      */
12376     read : function(response){
12377         var json = response.responseText;
12378        
12379         var o = /* eval:var:o */ eval("("+json+")");
12380         if(!o) {
12381             throw {message: "JsonReader.read: Json object not found"};
12382         }
12383         
12384         if(o.metaData){
12385             
12386             delete this.ef;
12387             this.metaFromRemote = true;
12388             this.meta = o.metaData;
12389             this.recordType = Roo.data.Record.create(o.metaData.fields);
12390             this.onMetaChange(this.meta, this.recordType, o);
12391         }
12392         return this.readRecords(o);
12393     },
12394
12395     // private function a store will implement
12396     onMetaChange : function(meta, recordType, o){
12397
12398     },
12399
12400     /**
12401          * @ignore
12402          */
12403     simpleAccess: function(obj, subsc) {
12404         return obj[subsc];
12405     },
12406
12407         /**
12408          * @ignore
12409          */
12410     getJsonAccessor: function(){
12411         var re = /[\[\.]/;
12412         return function(expr) {
12413             try {
12414                 return(re.test(expr))
12415                     ? new Function("obj", "return obj." + expr)
12416                     : function(obj){
12417                         return obj[expr];
12418                     };
12419             } catch(e){}
12420             return Roo.emptyFn;
12421         };
12422     }(),
12423
12424     /**
12425      * Create a data block containing Roo.data.Records from an XML document.
12426      * @param {Object} o An object which contains an Array of row objects in the property specified
12427      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12428      * which contains the total size of the dataset.
12429      * @return {Object} data A data block which is used by an Roo.data.Store object as
12430      * a cache of Roo.data.Records.
12431      */
12432     readRecords : function(o){
12433         /**
12434          * After any data loads, the raw JSON data is available for further custom processing.
12435          * @type Object
12436          */
12437         this.o = o;
12438         var s = this.meta, Record = this.recordType,
12439             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12440
12441 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12442         if (!this.ef) {
12443             if(s.totalProperty) {
12444                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12445                 }
12446                 if(s.successProperty) {
12447                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12448                 }
12449                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12450                 if (s.id) {
12451                         var g = this.getJsonAccessor(s.id);
12452                         this.getId = function(rec) {
12453                                 var r = g(rec);  
12454                                 return (r === undefined || r === "") ? null : r;
12455                         };
12456                 } else {
12457                         this.getId = function(){return null;};
12458                 }
12459             this.ef = [];
12460             for(var jj = 0; jj < fl; jj++){
12461                 f = fi[jj];
12462                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12463                 this.ef[jj] = this.getJsonAccessor(map);
12464             }
12465         }
12466
12467         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12468         if(s.totalProperty){
12469             var vt = parseInt(this.getTotal(o), 10);
12470             if(!isNaN(vt)){
12471                 totalRecords = vt;
12472             }
12473         }
12474         if(s.successProperty){
12475             var vs = this.getSuccess(o);
12476             if(vs === false || vs === 'false'){
12477                 success = false;
12478             }
12479         }
12480         var records = [];
12481         for(var i = 0; i < c; i++){
12482                 var n = root[i];
12483             var values = {};
12484             var id = this.getId(n);
12485             for(var j = 0; j < fl; j++){
12486                 f = fi[j];
12487             var v = this.ef[j](n);
12488             if (!f.convert) {
12489                 Roo.log('missing convert for ' + f.name);
12490                 Roo.log(f);
12491                 continue;
12492             }
12493             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12494             }
12495             var record = new Record(values, id);
12496             record.json = n;
12497             records[i] = record;
12498         }
12499         return {
12500             raw : o,
12501             success : success,
12502             records : records,
12503             totalRecords : totalRecords
12504         };
12505     }
12506 });/*
12507  * Based on:
12508  * Ext JS Library 1.1.1
12509  * Copyright(c) 2006-2007, Ext JS, LLC.
12510  *
12511  * Originally Released Under LGPL - original licence link has changed is not relivant.
12512  *
12513  * Fork - LGPL
12514  * <script type="text/javascript">
12515  */
12516
12517 /**
12518  * @class Roo.data.ArrayReader
12519  * @extends Roo.data.DataReader
12520  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12521  * Each element of that Array represents a row of data fields. The
12522  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12523  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12524  * <p>
12525  * Example code:.
12526  * <pre><code>
12527 var RecordDef = Roo.data.Record.create([
12528     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12529     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12530 ]);
12531 var myReader = new Roo.data.ArrayReader({
12532     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12533 }, RecordDef);
12534 </code></pre>
12535  * <p>
12536  * This would consume an Array like this:
12537  * <pre><code>
12538 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12539   </code></pre>
12540  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12541  * @constructor
12542  * Create a new JsonReader
12543  * @param {Object} meta Metadata configuration options.
12544  * @param {Object} recordType Either an Array of field definition objects
12545  * as specified to {@link Roo.data.Record#create},
12546  * or an {@link Roo.data.Record} object
12547  * created using {@link Roo.data.Record#create}.
12548  */
12549 Roo.data.ArrayReader = function(meta, recordType){
12550     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12551 };
12552
12553 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12554     /**
12555      * Create a data block containing Roo.data.Records from an XML document.
12556      * @param {Object} o An Array of row objects which represents the dataset.
12557      * @return {Object} data A data block which is used by an Roo.data.Store object as
12558      * a cache of Roo.data.Records.
12559      */
12560     readRecords : function(o){
12561         var sid = this.meta ? this.meta.id : null;
12562         var recordType = this.recordType, fields = recordType.prototype.fields;
12563         var records = [];
12564         var root = o;
12565             for(var i = 0; i < root.length; i++){
12566                     var n = root[i];
12567                 var values = {};
12568                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12569                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12570                 var f = fields.items[j];
12571                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12572                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12573                 v = f.convert(v);
12574                 values[f.name] = v;
12575             }
12576                 var record = new recordType(values, id);
12577                 record.json = n;
12578                 records[records.length] = record;
12579             }
12580             return {
12581                 records : records,
12582                 totalRecords : records.length
12583             };
12584     }
12585 });/*
12586  * - LGPL
12587  * * 
12588  */
12589
12590 /**
12591  * @class Roo.bootstrap.ComboBox
12592  * @extends Roo.bootstrap.TriggerField
12593  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12594  * @cfg {Boolean} append (true|false) default false
12595  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12596  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12597  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12598  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12599  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12600  * @cfg {Boolean} animate default true
12601  * @cfg {Boolean} emptyResultText only for touch device
12602  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12603  * @cfg {String} emptyTitle default ''
12604  * @constructor
12605  * Create a new ComboBox.
12606  * @param {Object} config Configuration options
12607  */
12608 Roo.bootstrap.ComboBox = function(config){
12609     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12610     this.addEvents({
12611         /**
12612          * @event expand
12613          * Fires when the dropdown list is expanded
12614         * @param {Roo.bootstrap.ComboBox} combo This combo box
12615         */
12616         'expand' : true,
12617         /**
12618          * @event collapse
12619          * Fires when the dropdown list is collapsed
12620         * @param {Roo.bootstrap.ComboBox} combo This combo box
12621         */
12622         'collapse' : true,
12623         /**
12624          * @event beforeselect
12625          * Fires before a list item is selected. Return false to cancel the selection.
12626         * @param {Roo.bootstrap.ComboBox} combo This combo box
12627         * @param {Roo.data.Record} record The data record returned from the underlying store
12628         * @param {Number} index The index of the selected item in the dropdown list
12629         */
12630         'beforeselect' : true,
12631         /**
12632          * @event select
12633          * Fires when a list item is selected
12634         * @param {Roo.bootstrap.ComboBox} combo This combo box
12635         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12636         * @param {Number} index The index of the selected item in the dropdown list
12637         */
12638         'select' : true,
12639         /**
12640          * @event beforequery
12641          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12642          * The event object passed has these properties:
12643         * @param {Roo.bootstrap.ComboBox} combo This combo box
12644         * @param {String} query The query
12645         * @param {Boolean} forceAll true to force "all" query
12646         * @param {Boolean} cancel true to cancel the query
12647         * @param {Object} e The query event object
12648         */
12649         'beforequery': true,
12650          /**
12651          * @event add
12652          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12653         * @param {Roo.bootstrap.ComboBox} combo This combo box
12654         */
12655         'add' : true,
12656         /**
12657          * @event edit
12658          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12659         * @param {Roo.bootstrap.ComboBox} combo This combo box
12660         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12661         */
12662         'edit' : true,
12663         /**
12664          * @event remove
12665          * Fires when the remove value from the combobox array
12666         * @param {Roo.bootstrap.ComboBox} combo This combo box
12667         */
12668         'remove' : true,
12669         /**
12670          * @event afterremove
12671          * Fires when the remove value from the combobox array
12672         * @param {Roo.bootstrap.ComboBox} combo This combo box
12673         */
12674         'afterremove' : true,
12675         /**
12676          * @event specialfilter
12677          * Fires when specialfilter
12678             * @param {Roo.bootstrap.ComboBox} combo This combo box
12679             */
12680         'specialfilter' : true,
12681         /**
12682          * @event tick
12683          * Fires when tick the element
12684             * @param {Roo.bootstrap.ComboBox} combo This combo box
12685             */
12686         'tick' : true,
12687         /**
12688          * @event touchviewdisplay
12689          * Fires when touch view require special display (default is using displayField)
12690             * @param {Roo.bootstrap.ComboBox} combo This combo box
12691             * @param {Object} cfg set html .
12692             */
12693         'touchviewdisplay' : true
12694         
12695     });
12696     
12697     this.item = [];
12698     this.tickItems = [];
12699     
12700     this.selectedIndex = -1;
12701     if(this.mode == 'local'){
12702         if(config.queryDelay === undefined){
12703             this.queryDelay = 10;
12704         }
12705         if(config.minChars === undefined){
12706             this.minChars = 0;
12707         }
12708     }
12709 };
12710
12711 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12712      
12713     /**
12714      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12715      * rendering into an Roo.Editor, defaults to false)
12716      */
12717     /**
12718      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12719      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12720      */
12721     /**
12722      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12723      */
12724     /**
12725      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12726      * the dropdown list (defaults to undefined, with no header element)
12727      */
12728
12729      /**
12730      * @cfg {String/Roo.Template} tpl The template to use to render the output
12731      */
12732      
12733      /**
12734      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12735      */
12736     listWidth: undefined,
12737     /**
12738      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12739      * mode = 'remote' or 'text' if mode = 'local')
12740      */
12741     displayField: undefined,
12742     
12743     /**
12744      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12745      * mode = 'remote' or 'value' if mode = 'local'). 
12746      * Note: use of a valueField requires the user make a selection
12747      * in order for a value to be mapped.
12748      */
12749     valueField: undefined,
12750     /**
12751      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12752      */
12753     modalTitle : '',
12754     
12755     /**
12756      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12757      * field's data value (defaults to the underlying DOM element's name)
12758      */
12759     hiddenName: undefined,
12760     /**
12761      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12762      */
12763     listClass: '',
12764     /**
12765      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12766      */
12767     selectedClass: 'active',
12768     
12769     /**
12770      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12771      */
12772     shadow:'sides',
12773     /**
12774      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12775      * anchor positions (defaults to 'tl-bl')
12776      */
12777     listAlign: 'tl-bl?',
12778     /**
12779      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12780      */
12781     maxHeight: 300,
12782     /**
12783      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12784      * query specified by the allQuery config option (defaults to 'query')
12785      */
12786     triggerAction: 'query',
12787     /**
12788      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12789      * (defaults to 4, does not apply if editable = false)
12790      */
12791     minChars : 4,
12792     /**
12793      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12794      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12795      */
12796     typeAhead: false,
12797     /**
12798      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12799      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12800      */
12801     queryDelay: 500,
12802     /**
12803      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12804      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12805      */
12806     pageSize: 0,
12807     /**
12808      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12809      * when editable = true (defaults to false)
12810      */
12811     selectOnFocus:false,
12812     /**
12813      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12814      */
12815     queryParam: 'query',
12816     /**
12817      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12818      * when mode = 'remote' (defaults to 'Loading...')
12819      */
12820     loadingText: 'Loading...',
12821     /**
12822      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12823      */
12824     resizable: false,
12825     /**
12826      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12827      */
12828     handleHeight : 8,
12829     /**
12830      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12831      * traditional select (defaults to true)
12832      */
12833     editable: true,
12834     /**
12835      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12836      */
12837     allQuery: '',
12838     /**
12839      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12840      */
12841     mode: 'remote',
12842     /**
12843      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12844      * listWidth has a higher value)
12845      */
12846     minListWidth : 70,
12847     /**
12848      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12849      * allow the user to set arbitrary text into the field (defaults to false)
12850      */
12851     forceSelection:false,
12852     /**
12853      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12854      * if typeAhead = true (defaults to 250)
12855      */
12856     typeAheadDelay : 250,
12857     /**
12858      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12859      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12860      */
12861     valueNotFoundText : undefined,
12862     /**
12863      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12864      */
12865     blockFocus : false,
12866     
12867     /**
12868      * @cfg {Boolean} disableClear Disable showing of clear button.
12869      */
12870     disableClear : false,
12871     /**
12872      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12873      */
12874     alwaysQuery : false,
12875     
12876     /**
12877      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12878      */
12879     multiple : false,
12880     
12881     /**
12882      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12883      */
12884     invalidClass : "has-warning",
12885     
12886     /**
12887      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12888      */
12889     validClass : "has-success",
12890     
12891     /**
12892      * @cfg {Boolean} specialFilter (true|false) special filter default false
12893      */
12894     specialFilter : false,
12895     
12896     /**
12897      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12898      */
12899     mobileTouchView : true,
12900     
12901     /**
12902      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12903      */
12904     useNativeIOS : false,
12905     
12906     ios_options : false,
12907     
12908     //private
12909     addicon : false,
12910     editicon: false,
12911     
12912     page: 0,
12913     hasQuery: false,
12914     append: false,
12915     loadNext: false,
12916     autoFocus : true,
12917     tickable : false,
12918     btnPosition : 'right',
12919     triggerList : true,
12920     showToggleBtn : true,
12921     animate : true,
12922     emptyResultText: 'Empty',
12923     triggerText : 'Select',
12924     emptyTitle : '',
12925     
12926     // element that contains real text value.. (when hidden is used..)
12927     
12928     getAutoCreate : function()
12929     {   
12930         var cfg = false;
12931         //render
12932         /*
12933          * Render classic select for iso
12934          */
12935         
12936         if(Roo.isIOS && this.useNativeIOS){
12937             cfg = this.getAutoCreateNativeIOS();
12938             return cfg;
12939         }
12940         
12941         /*
12942          * Touch Devices
12943          */
12944         
12945         if(Roo.isTouch && this.mobileTouchView){
12946             cfg = this.getAutoCreateTouchView();
12947             return cfg;;
12948         }
12949         
12950         /*
12951          *  Normal ComboBox
12952          */
12953         if(!this.tickable){
12954             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12955             return cfg;
12956         }
12957         
12958         /*
12959          *  ComboBox with tickable selections
12960          */
12961              
12962         var align = this.labelAlign || this.parentLabelAlign();
12963         
12964         cfg = {
12965             cls : 'form-group roo-combobox-tickable' //input-group
12966         };
12967         
12968         var btn_text_select = '';
12969         var btn_text_done = '';
12970         var btn_text_cancel = '';
12971         
12972         if (this.btn_text_show) {
12973             btn_text_select = 'Select';
12974             btn_text_done = 'Done';
12975             btn_text_cancel = 'Cancel'; 
12976         }
12977         
12978         var buttons = {
12979             tag : 'div',
12980             cls : 'tickable-buttons',
12981             cn : [
12982                 {
12983                     tag : 'button',
12984                     type : 'button',
12985                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12986                     //html : this.triggerText
12987                     html: btn_text_select
12988                 },
12989                 {
12990                     tag : 'button',
12991                     type : 'button',
12992                     name : 'ok',
12993                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12994                     //html : 'Done'
12995                     html: btn_text_done
12996                 },
12997                 {
12998                     tag : 'button',
12999                     type : 'button',
13000                     name : 'cancel',
13001                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13002                     //html : 'Cancel'
13003                     html: btn_text_cancel
13004                 }
13005             ]
13006         };
13007         
13008         if(this.editable){
13009             buttons.cn.unshift({
13010                 tag: 'input',
13011                 cls: 'roo-select2-search-field-input'
13012             });
13013         }
13014         
13015         var _this = this;
13016         
13017         Roo.each(buttons.cn, function(c){
13018             if (_this.size) {
13019                 c.cls += ' btn-' + _this.size;
13020             }
13021
13022             if (_this.disabled) {
13023                 c.disabled = true;
13024             }
13025         });
13026         
13027         var box = {
13028             tag: 'div',
13029             cn: [
13030                 {
13031                     tag: 'input',
13032                     type : 'hidden',
13033                     cls: 'form-hidden-field'
13034                 },
13035                 {
13036                     tag: 'ul',
13037                     cls: 'roo-select2-choices',
13038                     cn:[
13039                         {
13040                             tag: 'li',
13041                             cls: 'roo-select2-search-field',
13042                             cn: [
13043                                 buttons
13044                             ]
13045                         }
13046                     ]
13047                 }
13048             ]
13049         };
13050         
13051         var combobox = {
13052             cls: 'roo-select2-container input-group roo-select2-container-multi',
13053             cn: [
13054                 box
13055 //                {
13056 //                    tag: 'ul',
13057 //                    cls: 'typeahead typeahead-long dropdown-menu',
13058 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13059 //                }
13060             ]
13061         };
13062         
13063         if(this.hasFeedback && !this.allowBlank){
13064             
13065             var feedback = {
13066                 tag: 'span',
13067                 cls: 'glyphicon form-control-feedback'
13068             };
13069
13070             combobox.cn.push(feedback);
13071         }
13072         
13073         
13074         if (align ==='left' && this.fieldLabel.length) {
13075             
13076             cfg.cls += ' roo-form-group-label-left';
13077             
13078             cfg.cn = [
13079                 {
13080                     tag : 'i',
13081                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13082                     tooltip : 'This field is required'
13083                 },
13084                 {
13085                     tag: 'label',
13086                     'for' :  id,
13087                     cls : 'control-label',
13088                     html : this.fieldLabel
13089
13090                 },
13091                 {
13092                     cls : "", 
13093                     cn: [
13094                         combobox
13095                     ]
13096                 }
13097
13098             ];
13099             
13100             var labelCfg = cfg.cn[1];
13101             var contentCfg = cfg.cn[2];
13102             
13103
13104             if(this.indicatorpos == 'right'){
13105                 
13106                 cfg.cn = [
13107                     {
13108                         tag: 'label',
13109                         'for' :  id,
13110                         cls : 'control-label',
13111                         cn : [
13112                             {
13113                                 tag : 'span',
13114                                 html : this.fieldLabel
13115                             },
13116                             {
13117                                 tag : 'i',
13118                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13119                                 tooltip : 'This field is required'
13120                             }
13121                         ]
13122                     },
13123                     {
13124                         cls : "",
13125                         cn: [
13126                             combobox
13127                         ]
13128                     }
13129
13130                 ];
13131                 
13132                 
13133                 
13134                 labelCfg = cfg.cn[0];
13135                 contentCfg = cfg.cn[1];
13136             
13137             }
13138             
13139             if(this.labelWidth > 12){
13140                 labelCfg.style = "width: " + this.labelWidth + 'px';
13141             }
13142             
13143             if(this.labelWidth < 13 && this.labelmd == 0){
13144                 this.labelmd = this.labelWidth;
13145             }
13146             
13147             if(this.labellg > 0){
13148                 labelCfg.cls += ' col-lg-' + this.labellg;
13149                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13150             }
13151             
13152             if(this.labelmd > 0){
13153                 labelCfg.cls += ' col-md-' + this.labelmd;
13154                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13155             }
13156             
13157             if(this.labelsm > 0){
13158                 labelCfg.cls += ' col-sm-' + this.labelsm;
13159                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13160             }
13161             
13162             if(this.labelxs > 0){
13163                 labelCfg.cls += ' col-xs-' + this.labelxs;
13164                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13165             }
13166                 
13167                 
13168         } else if ( this.fieldLabel.length) {
13169 //                Roo.log(" label");
13170                  cfg.cn = [
13171                     {
13172                         tag : 'i',
13173                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13174                         tooltip : 'This field is required'
13175                     },
13176                     {
13177                         tag: 'label',
13178                         //cls : 'input-group-addon',
13179                         html : this.fieldLabel
13180                     },
13181                     combobox
13182                 ];
13183                 
13184                 if(this.indicatorpos == 'right'){
13185                     cfg.cn = [
13186                         {
13187                             tag: 'label',
13188                             //cls : 'input-group-addon',
13189                             html : this.fieldLabel
13190                         },
13191                         {
13192                             tag : 'i',
13193                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13194                             tooltip : 'This field is required'
13195                         },
13196                         combobox
13197                     ];
13198                     
13199                 }
13200
13201         } else {
13202             
13203 //                Roo.log(" no label && no align");
13204                 cfg = combobox
13205                      
13206                 
13207         }
13208          
13209         var settings=this;
13210         ['xs','sm','md','lg'].map(function(size){
13211             if (settings[size]) {
13212                 cfg.cls += ' col-' + size + '-' + settings[size];
13213             }
13214         });
13215         
13216         return cfg;
13217         
13218     },
13219     
13220     _initEventsCalled : false,
13221     
13222     // private
13223     initEvents: function()
13224     {   
13225         if (this._initEventsCalled) { // as we call render... prevent looping...
13226             return;
13227         }
13228         this._initEventsCalled = true;
13229         
13230         if (!this.store) {
13231             throw "can not find store for combo";
13232         }
13233         
13234         this.indicator = this.indicatorEl();
13235         
13236         this.store = Roo.factory(this.store, Roo.data);
13237         this.store.parent = this;
13238         
13239         // if we are building from html. then this element is so complex, that we can not really
13240         // use the rendered HTML.
13241         // so we have to trash and replace the previous code.
13242         if (Roo.XComponent.build_from_html) {
13243             // remove this element....
13244             var e = this.el.dom, k=0;
13245             while (e ) { e = e.previousSibling;  ++k;}
13246
13247             this.el.remove();
13248             
13249             this.el=false;
13250             this.rendered = false;
13251             
13252             this.render(this.parent().getChildContainer(true), k);
13253         }
13254         
13255         if(Roo.isIOS && this.useNativeIOS){
13256             this.initIOSView();
13257             return;
13258         }
13259         
13260         /*
13261          * Touch Devices
13262          */
13263         
13264         if(Roo.isTouch && this.mobileTouchView){
13265             this.initTouchView();
13266             return;
13267         }
13268         
13269         if(this.tickable){
13270             this.initTickableEvents();
13271             return;
13272         }
13273         
13274         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13275         
13276         if(this.hiddenName){
13277             
13278             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13279             
13280             this.hiddenField.dom.value =
13281                 this.hiddenValue !== undefined ? this.hiddenValue :
13282                 this.value !== undefined ? this.value : '';
13283
13284             // prevent input submission
13285             this.el.dom.removeAttribute('name');
13286             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13287              
13288              
13289         }
13290         //if(Roo.isGecko){
13291         //    this.el.dom.setAttribute('autocomplete', 'off');
13292         //}
13293         
13294         var cls = 'x-combo-list';
13295         
13296         //this.list = new Roo.Layer({
13297         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13298         //});
13299         
13300         var _this = this;
13301         
13302         (function(){
13303             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13304             _this.list.setWidth(lw);
13305         }).defer(100);
13306         
13307         this.list.on('mouseover', this.onViewOver, this);
13308         this.list.on('mousemove', this.onViewMove, this);
13309         this.list.on('scroll', this.onViewScroll, this);
13310         
13311         /*
13312         this.list.swallowEvent('mousewheel');
13313         this.assetHeight = 0;
13314
13315         if(this.title){
13316             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13317             this.assetHeight += this.header.getHeight();
13318         }
13319
13320         this.innerList = this.list.createChild({cls:cls+'-inner'});
13321         this.innerList.on('mouseover', this.onViewOver, this);
13322         this.innerList.on('mousemove', this.onViewMove, this);
13323         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13324         
13325         if(this.allowBlank && !this.pageSize && !this.disableClear){
13326             this.footer = this.list.createChild({cls:cls+'-ft'});
13327             this.pageTb = new Roo.Toolbar(this.footer);
13328            
13329         }
13330         if(this.pageSize){
13331             this.footer = this.list.createChild({cls:cls+'-ft'});
13332             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13333                     {pageSize: this.pageSize});
13334             
13335         }
13336         
13337         if (this.pageTb && this.allowBlank && !this.disableClear) {
13338             var _this = this;
13339             this.pageTb.add(new Roo.Toolbar.Fill(), {
13340                 cls: 'x-btn-icon x-btn-clear',
13341                 text: '&#160;',
13342                 handler: function()
13343                 {
13344                     _this.collapse();
13345                     _this.clearValue();
13346                     _this.onSelect(false, -1);
13347                 }
13348             });
13349         }
13350         if (this.footer) {
13351             this.assetHeight += this.footer.getHeight();
13352         }
13353         */
13354             
13355         if(!this.tpl){
13356             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13357         }
13358
13359         this.view = new Roo.View(this.list, this.tpl, {
13360             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13361         });
13362         //this.view.wrapEl.setDisplayed(false);
13363         this.view.on('click', this.onViewClick, this);
13364         
13365         
13366         this.store.on('beforeload', this.onBeforeLoad, this);
13367         this.store.on('load', this.onLoad, this);
13368         this.store.on('loadexception', this.onLoadException, this);
13369         /*
13370         if(this.resizable){
13371             this.resizer = new Roo.Resizable(this.list,  {
13372                pinned:true, handles:'se'
13373             });
13374             this.resizer.on('resize', function(r, w, h){
13375                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13376                 this.listWidth = w;
13377                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13378                 this.restrictHeight();
13379             }, this);
13380             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13381         }
13382         */
13383         if(!this.editable){
13384             this.editable = true;
13385             this.setEditable(false);
13386         }
13387         
13388         /*
13389         
13390         if (typeof(this.events.add.listeners) != 'undefined') {
13391             
13392             this.addicon = this.wrap.createChild(
13393                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13394        
13395             this.addicon.on('click', function(e) {
13396                 this.fireEvent('add', this);
13397             }, this);
13398         }
13399         if (typeof(this.events.edit.listeners) != 'undefined') {
13400             
13401             this.editicon = this.wrap.createChild(
13402                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13403             if (this.addicon) {
13404                 this.editicon.setStyle('margin-left', '40px');
13405             }
13406             this.editicon.on('click', function(e) {
13407                 
13408                 // we fire even  if inothing is selected..
13409                 this.fireEvent('edit', this, this.lastData );
13410                 
13411             }, this);
13412         }
13413         */
13414         
13415         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13416             "up" : function(e){
13417                 this.inKeyMode = true;
13418                 this.selectPrev();
13419             },
13420
13421             "down" : function(e){
13422                 if(!this.isExpanded()){
13423                     this.onTriggerClick();
13424                 }else{
13425                     this.inKeyMode = true;
13426                     this.selectNext();
13427                 }
13428             },
13429
13430             "enter" : function(e){
13431 //                this.onViewClick();
13432                 //return true;
13433                 this.collapse();
13434                 
13435                 if(this.fireEvent("specialkey", this, e)){
13436                     this.onViewClick(false);
13437                 }
13438                 
13439                 return true;
13440             },
13441
13442             "esc" : function(e){
13443                 this.collapse();
13444             },
13445
13446             "tab" : function(e){
13447                 this.collapse();
13448                 
13449                 if(this.fireEvent("specialkey", this, e)){
13450                     this.onViewClick(false);
13451                 }
13452                 
13453                 return true;
13454             },
13455
13456             scope : this,
13457
13458             doRelay : function(foo, bar, hname){
13459                 if(hname == 'down' || this.scope.isExpanded()){
13460                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13461                 }
13462                 return true;
13463             },
13464
13465             forceKeyDown: true
13466         });
13467         
13468         
13469         this.queryDelay = Math.max(this.queryDelay || 10,
13470                 this.mode == 'local' ? 10 : 250);
13471         
13472         
13473         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13474         
13475         if(this.typeAhead){
13476             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13477         }
13478         if(this.editable !== false){
13479             this.inputEl().on("keyup", this.onKeyUp, this);
13480         }
13481         if(this.forceSelection){
13482             this.inputEl().on('blur', this.doForce, this);
13483         }
13484         
13485         if(this.multiple){
13486             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13487             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13488         }
13489     },
13490     
13491     initTickableEvents: function()
13492     {   
13493         this.createList();
13494         
13495         if(this.hiddenName){
13496             
13497             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13498             
13499             this.hiddenField.dom.value =
13500                 this.hiddenValue !== undefined ? this.hiddenValue :
13501                 this.value !== undefined ? this.value : '';
13502
13503             // prevent input submission
13504             this.el.dom.removeAttribute('name');
13505             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13506              
13507              
13508         }
13509         
13510 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13511         
13512         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13513         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13514         if(this.triggerList){
13515             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13516         }
13517          
13518         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13519         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13520         
13521         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13522         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13523         
13524         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13525         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13526         
13527         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13528         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13529         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13530         
13531         this.okBtn.hide();
13532         this.cancelBtn.hide();
13533         
13534         var _this = this;
13535         
13536         (function(){
13537             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13538             _this.list.setWidth(lw);
13539         }).defer(100);
13540         
13541         this.list.on('mouseover', this.onViewOver, this);
13542         this.list.on('mousemove', this.onViewMove, this);
13543         
13544         this.list.on('scroll', this.onViewScroll, this);
13545         
13546         if(!this.tpl){
13547             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>';
13548         }
13549
13550         this.view = new Roo.View(this.list, this.tpl, {
13551             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13552         });
13553         
13554         //this.view.wrapEl.setDisplayed(false);
13555         this.view.on('click', this.onViewClick, this);
13556         
13557         
13558         
13559         this.store.on('beforeload', this.onBeforeLoad, this);
13560         this.store.on('load', this.onLoad, this);
13561         this.store.on('loadexception', this.onLoadException, this);
13562         
13563         if(this.editable){
13564             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13565                 "up" : function(e){
13566                     this.inKeyMode = true;
13567                     this.selectPrev();
13568                 },
13569
13570                 "down" : function(e){
13571                     this.inKeyMode = true;
13572                     this.selectNext();
13573                 },
13574
13575                 "enter" : function(e){
13576                     if(this.fireEvent("specialkey", this, e)){
13577                         this.onViewClick(false);
13578                     }
13579                     
13580                     return true;
13581                 },
13582
13583                 "esc" : function(e){
13584                     this.onTickableFooterButtonClick(e, false, false);
13585                 },
13586
13587                 "tab" : function(e){
13588                     this.fireEvent("specialkey", this, e);
13589                     
13590                     this.onTickableFooterButtonClick(e, false, false);
13591                     
13592                     return true;
13593                 },
13594
13595                 scope : this,
13596
13597                 doRelay : function(e, fn, key){
13598                     if(this.scope.isExpanded()){
13599                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13600                     }
13601                     return true;
13602                 },
13603
13604                 forceKeyDown: true
13605             });
13606         }
13607         
13608         this.queryDelay = Math.max(this.queryDelay || 10,
13609                 this.mode == 'local' ? 10 : 250);
13610         
13611         
13612         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13613         
13614         if(this.typeAhead){
13615             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13616         }
13617         
13618         if(this.editable !== false){
13619             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13620         }
13621         
13622         this.indicator = this.indicatorEl();
13623         
13624         if(this.indicator){
13625             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13626             this.indicator.hide();
13627         }
13628         
13629     },
13630
13631     onDestroy : function(){
13632         if(this.view){
13633             this.view.setStore(null);
13634             this.view.el.removeAllListeners();
13635             this.view.el.remove();
13636             this.view.purgeListeners();
13637         }
13638         if(this.list){
13639             this.list.dom.innerHTML  = '';
13640         }
13641         
13642         if(this.store){
13643             this.store.un('beforeload', this.onBeforeLoad, this);
13644             this.store.un('load', this.onLoad, this);
13645             this.store.un('loadexception', this.onLoadException, this);
13646         }
13647         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13648     },
13649
13650     // private
13651     fireKey : function(e){
13652         if(e.isNavKeyPress() && !this.list.isVisible()){
13653             this.fireEvent("specialkey", this, e);
13654         }
13655     },
13656
13657     // private
13658     onResize: function(w, h){
13659 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13660 //        
13661 //        if(typeof w != 'number'){
13662 //            // we do not handle it!?!?
13663 //            return;
13664 //        }
13665 //        var tw = this.trigger.getWidth();
13666 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13667 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13668 //        var x = w - tw;
13669 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13670 //            
13671 //        //this.trigger.setStyle('left', x+'px');
13672 //        
13673 //        if(this.list && this.listWidth === undefined){
13674 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13675 //            this.list.setWidth(lw);
13676 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13677 //        }
13678         
13679     
13680         
13681     },
13682
13683     /**
13684      * Allow or prevent the user from directly editing the field text.  If false is passed,
13685      * the user will only be able to select from the items defined in the dropdown list.  This method
13686      * is the runtime equivalent of setting the 'editable' config option at config time.
13687      * @param {Boolean} value True to allow the user to directly edit the field text
13688      */
13689     setEditable : function(value){
13690         if(value == this.editable){
13691             return;
13692         }
13693         this.editable = value;
13694         if(!value){
13695             this.inputEl().dom.setAttribute('readOnly', true);
13696             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13697             this.inputEl().addClass('x-combo-noedit');
13698         }else{
13699             this.inputEl().dom.setAttribute('readOnly', false);
13700             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13701             this.inputEl().removeClass('x-combo-noedit');
13702         }
13703     },
13704
13705     // private
13706     
13707     onBeforeLoad : function(combo,opts){
13708         if(!this.hasFocus){
13709             return;
13710         }
13711          if (!opts.add) {
13712             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13713          }
13714         this.restrictHeight();
13715         this.selectedIndex = -1;
13716     },
13717
13718     // private
13719     onLoad : function(){
13720         
13721         this.hasQuery = false;
13722         
13723         if(!this.hasFocus){
13724             return;
13725         }
13726         
13727         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13728             this.loading.hide();
13729         }
13730         
13731         if(this.store.getCount() > 0){
13732             
13733             this.expand();
13734             this.restrictHeight();
13735             if(this.lastQuery == this.allQuery){
13736                 if(this.editable && !this.tickable){
13737                     this.inputEl().dom.select();
13738                 }
13739                 
13740                 if(
13741                     !this.selectByValue(this.value, true) &&
13742                     this.autoFocus && 
13743                     (
13744                         !this.store.lastOptions ||
13745                         typeof(this.store.lastOptions.add) == 'undefined' || 
13746                         this.store.lastOptions.add != true
13747                     )
13748                 ){
13749                     this.select(0, true);
13750                 }
13751             }else{
13752                 if(this.autoFocus){
13753                     this.selectNext();
13754                 }
13755                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13756                     this.taTask.delay(this.typeAheadDelay);
13757                 }
13758             }
13759         }else{
13760             this.onEmptyResults();
13761         }
13762         
13763         //this.el.focus();
13764     },
13765     // private
13766     onLoadException : function()
13767     {
13768         this.hasQuery = false;
13769         
13770         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13771             this.loading.hide();
13772         }
13773         
13774         if(this.tickable && this.editable){
13775             return;
13776         }
13777         
13778         this.collapse();
13779         // only causes errors at present
13780         //Roo.log(this.store.reader.jsonData);
13781         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13782             // fixme
13783             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13784         //}
13785         
13786         
13787     },
13788     // private
13789     onTypeAhead : function(){
13790         if(this.store.getCount() > 0){
13791             var r = this.store.getAt(0);
13792             var newValue = r.data[this.displayField];
13793             var len = newValue.length;
13794             var selStart = this.getRawValue().length;
13795             
13796             if(selStart != len){
13797                 this.setRawValue(newValue);
13798                 this.selectText(selStart, newValue.length);
13799             }
13800         }
13801     },
13802
13803     // private
13804     onSelect : function(record, index){
13805         
13806         if(this.fireEvent('beforeselect', this, record, index) !== false){
13807         
13808             this.setFromData(index > -1 ? record.data : false);
13809             
13810             this.collapse();
13811             this.fireEvent('select', this, record, index);
13812         }
13813     },
13814
13815     /**
13816      * Returns the currently selected field value or empty string if no value is set.
13817      * @return {String} value The selected value
13818      */
13819     getValue : function()
13820     {
13821         if(Roo.isIOS && this.useNativeIOS){
13822             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13823         }
13824         
13825         if(this.multiple){
13826             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13827         }
13828         
13829         if(this.valueField){
13830             return typeof this.value != 'undefined' ? this.value : '';
13831         }else{
13832             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13833         }
13834     },
13835     
13836     getRawValue : function()
13837     {
13838         if(Roo.isIOS && this.useNativeIOS){
13839             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13840         }
13841         
13842         var v = this.inputEl().getValue();
13843         
13844         return v;
13845     },
13846
13847     /**
13848      * Clears any text/value currently set in the field
13849      */
13850     clearValue : function(){
13851         
13852         if(this.hiddenField){
13853             this.hiddenField.dom.value = '';
13854         }
13855         this.value = '';
13856         this.setRawValue('');
13857         this.lastSelectionText = '';
13858         this.lastData = false;
13859         
13860         var close = this.closeTriggerEl();
13861         
13862         if(close){
13863             close.hide();
13864         }
13865         
13866         this.validate();
13867         
13868     },
13869
13870     /**
13871      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13872      * will be displayed in the field.  If the value does not match the data value of an existing item,
13873      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13874      * Otherwise the field will be blank (although the value will still be set).
13875      * @param {String} value The value to match
13876      */
13877     setValue : function(v)
13878     {
13879         if(Roo.isIOS && this.useNativeIOS){
13880             this.setIOSValue(v);
13881             return;
13882         }
13883         
13884         if(this.multiple){
13885             this.syncValue();
13886             return;
13887         }
13888         
13889         var text = v;
13890         if(this.valueField){
13891             var r = this.findRecord(this.valueField, v);
13892             if(r){
13893                 text = r.data[this.displayField];
13894             }else if(this.valueNotFoundText !== undefined){
13895                 text = this.valueNotFoundText;
13896             }
13897         }
13898         this.lastSelectionText = text;
13899         if(this.hiddenField){
13900             this.hiddenField.dom.value = v;
13901         }
13902         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13903         this.value = v;
13904         
13905         var close = this.closeTriggerEl();
13906         
13907         if(close){
13908             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13909         }
13910         
13911         this.validate();
13912     },
13913     /**
13914      * @property {Object} the last set data for the element
13915      */
13916     
13917     lastData : false,
13918     /**
13919      * Sets the value of the field based on a object which is related to the record format for the store.
13920      * @param {Object} value the value to set as. or false on reset?
13921      */
13922     setFromData : function(o){
13923         
13924         if(this.multiple){
13925             this.addItem(o);
13926             return;
13927         }
13928             
13929         var dv = ''; // display value
13930         var vv = ''; // value value..
13931         this.lastData = o;
13932         if (this.displayField) {
13933             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13934         } else {
13935             // this is an error condition!!!
13936             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13937         }
13938         
13939         if(this.valueField){
13940             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13941         }
13942         
13943         var close = this.closeTriggerEl();
13944         
13945         if(close){
13946             if(dv.length || vv * 1 > 0){
13947                 close.show() ;
13948                 this.blockFocus=true;
13949             } else {
13950                 close.hide();
13951             }             
13952         }
13953         
13954         if(this.hiddenField){
13955             this.hiddenField.dom.value = vv;
13956             
13957             this.lastSelectionText = dv;
13958             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13959             this.value = vv;
13960             return;
13961         }
13962         // no hidden field.. - we store the value in 'value', but still display
13963         // display field!!!!
13964         this.lastSelectionText = dv;
13965         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13966         this.value = vv;
13967         
13968         
13969         
13970     },
13971     // private
13972     reset : function(){
13973         // overridden so that last data is reset..
13974         
13975         if(this.multiple){
13976             this.clearItem();
13977             return;
13978         }
13979         
13980         this.setValue(this.originalValue);
13981         //this.clearInvalid();
13982         this.lastData = false;
13983         if (this.view) {
13984             this.view.clearSelections();
13985         }
13986         
13987         this.validate();
13988     },
13989     // private
13990     findRecord : function(prop, value){
13991         var record;
13992         if(this.store.getCount() > 0){
13993             this.store.each(function(r){
13994                 if(r.data[prop] == value){
13995                     record = r;
13996                     return false;
13997                 }
13998                 return true;
13999             });
14000         }
14001         return record;
14002     },
14003     
14004     getName: function()
14005     {
14006         // returns hidden if it's set..
14007         if (!this.rendered) {return ''};
14008         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14009         
14010     },
14011     // private
14012     onViewMove : function(e, t){
14013         this.inKeyMode = false;
14014     },
14015
14016     // private
14017     onViewOver : function(e, t){
14018         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14019             return;
14020         }
14021         var item = this.view.findItemFromChild(t);
14022         
14023         if(item){
14024             var index = this.view.indexOf(item);
14025             this.select(index, false);
14026         }
14027     },
14028
14029     // private
14030     onViewClick : function(view, doFocus, el, e)
14031     {
14032         var index = this.view.getSelectedIndexes()[0];
14033         
14034         var r = this.store.getAt(index);
14035         
14036         if(this.tickable){
14037             
14038             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14039                 return;
14040             }
14041             
14042             var rm = false;
14043             var _this = this;
14044             
14045             Roo.each(this.tickItems, function(v,k){
14046                 
14047                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14048                     Roo.log(v);
14049                     _this.tickItems.splice(k, 1);
14050                     
14051                     if(typeof(e) == 'undefined' && view == false){
14052                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14053                     }
14054                     
14055                     rm = true;
14056                     return;
14057                 }
14058             });
14059             
14060             if(rm){
14061                 return;
14062             }
14063             
14064             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14065                 this.tickItems.push(r.data);
14066             }
14067             
14068             if(typeof(e) == 'undefined' && view == false){
14069                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14070             }
14071                     
14072             return;
14073         }
14074         
14075         if(r){
14076             this.onSelect(r, index);
14077         }
14078         if(doFocus !== false && !this.blockFocus){
14079             this.inputEl().focus();
14080         }
14081     },
14082
14083     // private
14084     restrictHeight : function(){
14085         //this.innerList.dom.style.height = '';
14086         //var inner = this.innerList.dom;
14087         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14088         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14089         //this.list.beginUpdate();
14090         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14091         this.list.alignTo(this.inputEl(), this.listAlign);
14092         this.list.alignTo(this.inputEl(), this.listAlign);
14093         //this.list.endUpdate();
14094     },
14095
14096     // private
14097     onEmptyResults : function(){
14098         
14099         if(this.tickable && this.editable){
14100             this.hasFocus = false;
14101             this.restrictHeight();
14102             return;
14103         }
14104         
14105         this.collapse();
14106     },
14107
14108     /**
14109      * Returns true if the dropdown list is expanded, else false.
14110      */
14111     isExpanded : function(){
14112         return this.list.isVisible();
14113     },
14114
14115     /**
14116      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14117      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14118      * @param {String} value The data value of the item to select
14119      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14120      * selected item if it is not currently in view (defaults to true)
14121      * @return {Boolean} True if the value matched an item in the list, else false
14122      */
14123     selectByValue : function(v, scrollIntoView){
14124         if(v !== undefined && v !== null){
14125             var r = this.findRecord(this.valueField || this.displayField, v);
14126             if(r){
14127                 this.select(this.store.indexOf(r), scrollIntoView);
14128                 return true;
14129             }
14130         }
14131         return false;
14132     },
14133
14134     /**
14135      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14136      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14137      * @param {Number} index The zero-based index of the list item to select
14138      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14139      * selected item if it is not currently in view (defaults to true)
14140      */
14141     select : function(index, scrollIntoView){
14142         this.selectedIndex = index;
14143         this.view.select(index);
14144         if(scrollIntoView !== false){
14145             var el = this.view.getNode(index);
14146             /*
14147              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14148              */
14149             if(el){
14150                 this.list.scrollChildIntoView(el, false);
14151             }
14152         }
14153     },
14154
14155     // private
14156     selectNext : function(){
14157         var ct = this.store.getCount();
14158         if(ct > 0){
14159             if(this.selectedIndex == -1){
14160                 this.select(0);
14161             }else if(this.selectedIndex < ct-1){
14162                 this.select(this.selectedIndex+1);
14163             }
14164         }
14165     },
14166
14167     // private
14168     selectPrev : function(){
14169         var ct = this.store.getCount();
14170         if(ct > 0){
14171             if(this.selectedIndex == -1){
14172                 this.select(0);
14173             }else if(this.selectedIndex != 0){
14174                 this.select(this.selectedIndex-1);
14175             }
14176         }
14177     },
14178
14179     // private
14180     onKeyUp : function(e){
14181         if(this.editable !== false && !e.isSpecialKey()){
14182             this.lastKey = e.getKey();
14183             this.dqTask.delay(this.queryDelay);
14184         }
14185     },
14186
14187     // private
14188     validateBlur : function(){
14189         return !this.list || !this.list.isVisible();   
14190     },
14191
14192     // private
14193     initQuery : function(){
14194         
14195         var v = this.getRawValue();
14196         
14197         if(this.tickable && this.editable){
14198             v = this.tickableInputEl().getValue();
14199         }
14200         
14201         this.doQuery(v);
14202     },
14203
14204     // private
14205     doForce : function(){
14206         if(this.inputEl().dom.value.length > 0){
14207             this.inputEl().dom.value =
14208                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14209              
14210         }
14211     },
14212
14213     /**
14214      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14215      * query allowing the query action to be canceled if needed.
14216      * @param {String} query The SQL query to execute
14217      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14218      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14219      * saved in the current store (defaults to false)
14220      */
14221     doQuery : function(q, forceAll){
14222         
14223         if(q === undefined || q === null){
14224             q = '';
14225         }
14226         var qe = {
14227             query: q,
14228             forceAll: forceAll,
14229             combo: this,
14230             cancel:false
14231         };
14232         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14233             return false;
14234         }
14235         q = qe.query;
14236         
14237         forceAll = qe.forceAll;
14238         if(forceAll === true || (q.length >= this.minChars)){
14239             
14240             this.hasQuery = true;
14241             
14242             if(this.lastQuery != q || this.alwaysQuery){
14243                 this.lastQuery = q;
14244                 if(this.mode == 'local'){
14245                     this.selectedIndex = -1;
14246                     if(forceAll){
14247                         this.store.clearFilter();
14248                     }else{
14249                         
14250                         if(this.specialFilter){
14251                             this.fireEvent('specialfilter', this);
14252                             this.onLoad();
14253                             return;
14254                         }
14255                         
14256                         this.store.filter(this.displayField, q);
14257                     }
14258                     
14259                     this.store.fireEvent("datachanged", this.store);
14260                     
14261                     this.onLoad();
14262                     
14263                     
14264                 }else{
14265                     
14266                     this.store.baseParams[this.queryParam] = q;
14267                     
14268                     var options = {params : this.getParams(q)};
14269                     
14270                     if(this.loadNext){
14271                         options.add = true;
14272                         options.params.start = this.page * this.pageSize;
14273                     }
14274                     
14275                     this.store.load(options);
14276                     
14277                     /*
14278                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14279                      *  we should expand the list on onLoad
14280                      *  so command out it
14281                      */
14282 //                    this.expand();
14283                 }
14284             }else{
14285                 this.selectedIndex = -1;
14286                 this.onLoad();   
14287             }
14288         }
14289         
14290         this.loadNext = false;
14291     },
14292     
14293     // private
14294     getParams : function(q){
14295         var p = {};
14296         //p[this.queryParam] = q;
14297         
14298         if(this.pageSize){
14299             p.start = 0;
14300             p.limit = this.pageSize;
14301         }
14302         return p;
14303     },
14304
14305     /**
14306      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14307      */
14308     collapse : function(){
14309         if(!this.isExpanded()){
14310             return;
14311         }
14312         
14313         this.list.hide();
14314         
14315         this.hasFocus = false;
14316         
14317         if(this.tickable){
14318             this.okBtn.hide();
14319             this.cancelBtn.hide();
14320             this.trigger.show();
14321             
14322             if(this.editable){
14323                 this.tickableInputEl().dom.value = '';
14324                 this.tickableInputEl().blur();
14325             }
14326             
14327         }
14328         
14329         Roo.get(document).un('mousedown', this.collapseIf, this);
14330         Roo.get(document).un('mousewheel', this.collapseIf, this);
14331         if (!this.editable) {
14332             Roo.get(document).un('keydown', this.listKeyPress, this);
14333         }
14334         this.fireEvent('collapse', this);
14335         
14336         this.validate();
14337     },
14338
14339     // private
14340     collapseIf : function(e){
14341         var in_combo  = e.within(this.el);
14342         var in_list =  e.within(this.list);
14343         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14344         
14345         if (in_combo || in_list || is_list) {
14346             //e.stopPropagation();
14347             return;
14348         }
14349         
14350         if(this.tickable){
14351             this.onTickableFooterButtonClick(e, false, false);
14352         }
14353
14354         this.collapse();
14355         
14356     },
14357
14358     /**
14359      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14360      */
14361     expand : function(){
14362        
14363         if(this.isExpanded() || !this.hasFocus){
14364             return;
14365         }
14366         
14367         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14368         this.list.setWidth(lw);
14369         
14370         Roo.log('expand');
14371         
14372         this.list.show();
14373         
14374         this.restrictHeight();
14375         
14376         if(this.tickable){
14377             
14378             this.tickItems = Roo.apply([], this.item);
14379             
14380             this.okBtn.show();
14381             this.cancelBtn.show();
14382             this.trigger.hide();
14383             
14384             if(this.editable){
14385                 this.tickableInputEl().focus();
14386             }
14387             
14388         }
14389         
14390         Roo.get(document).on('mousedown', this.collapseIf, this);
14391         Roo.get(document).on('mousewheel', this.collapseIf, this);
14392         if (!this.editable) {
14393             Roo.get(document).on('keydown', this.listKeyPress, this);
14394         }
14395         
14396         this.fireEvent('expand', this);
14397     },
14398
14399     // private
14400     // Implements the default empty TriggerField.onTriggerClick function
14401     onTriggerClick : function(e)
14402     {
14403         Roo.log('trigger click');
14404         
14405         if(this.disabled || !this.triggerList){
14406             return;
14407         }
14408         
14409         this.page = 0;
14410         this.loadNext = false;
14411         
14412         if(this.isExpanded()){
14413             this.collapse();
14414             if (!this.blockFocus) {
14415                 this.inputEl().focus();
14416             }
14417             
14418         }else {
14419             this.hasFocus = true;
14420             if(this.triggerAction == 'all') {
14421                 this.doQuery(this.allQuery, true);
14422             } else {
14423                 this.doQuery(this.getRawValue());
14424             }
14425             if (!this.blockFocus) {
14426                 this.inputEl().focus();
14427             }
14428         }
14429     },
14430     
14431     onTickableTriggerClick : function(e)
14432     {
14433         if(this.disabled){
14434             return;
14435         }
14436         
14437         this.page = 0;
14438         this.loadNext = false;
14439         this.hasFocus = true;
14440         
14441         if(this.triggerAction == 'all') {
14442             this.doQuery(this.allQuery, true);
14443         } else {
14444             this.doQuery(this.getRawValue());
14445         }
14446     },
14447     
14448     onSearchFieldClick : function(e)
14449     {
14450         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14451             this.onTickableFooterButtonClick(e, false, false);
14452             return;
14453         }
14454         
14455         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14456             return;
14457         }
14458         
14459         this.page = 0;
14460         this.loadNext = false;
14461         this.hasFocus = true;
14462         
14463         if(this.triggerAction == 'all') {
14464             this.doQuery(this.allQuery, true);
14465         } else {
14466             this.doQuery(this.getRawValue());
14467         }
14468     },
14469     
14470     listKeyPress : function(e)
14471     {
14472         //Roo.log('listkeypress');
14473         // scroll to first matching element based on key pres..
14474         if (e.isSpecialKey()) {
14475             return false;
14476         }
14477         var k = String.fromCharCode(e.getKey()).toUpperCase();
14478         //Roo.log(k);
14479         var match  = false;
14480         var csel = this.view.getSelectedNodes();
14481         var cselitem = false;
14482         if (csel.length) {
14483             var ix = this.view.indexOf(csel[0]);
14484             cselitem  = this.store.getAt(ix);
14485             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14486                 cselitem = false;
14487             }
14488             
14489         }
14490         
14491         this.store.each(function(v) { 
14492             if (cselitem) {
14493                 // start at existing selection.
14494                 if (cselitem.id == v.id) {
14495                     cselitem = false;
14496                 }
14497                 return true;
14498             }
14499                 
14500             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14501                 match = this.store.indexOf(v);
14502                 return false;
14503             }
14504             return true;
14505         }, this);
14506         
14507         if (match === false) {
14508             return true; // no more action?
14509         }
14510         // scroll to?
14511         this.view.select(match);
14512         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14513         sn.scrollIntoView(sn.dom.parentNode, false);
14514     },
14515     
14516     onViewScroll : function(e, t){
14517         
14518         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){
14519             return;
14520         }
14521         
14522         this.hasQuery = true;
14523         
14524         this.loading = this.list.select('.loading', true).first();
14525         
14526         if(this.loading === null){
14527             this.list.createChild({
14528                 tag: 'div',
14529                 cls: 'loading roo-select2-more-results roo-select2-active',
14530                 html: 'Loading more results...'
14531             });
14532             
14533             this.loading = this.list.select('.loading', true).first();
14534             
14535             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14536             
14537             this.loading.hide();
14538         }
14539         
14540         this.loading.show();
14541         
14542         var _combo = this;
14543         
14544         this.page++;
14545         this.loadNext = true;
14546         
14547         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14548         
14549         return;
14550     },
14551     
14552     addItem : function(o)
14553     {   
14554         var dv = ''; // display value
14555         
14556         if (this.displayField) {
14557             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14558         } else {
14559             // this is an error condition!!!
14560             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14561         }
14562         
14563         if(!dv.length){
14564             return;
14565         }
14566         
14567         var choice = this.choices.createChild({
14568             tag: 'li',
14569             cls: 'roo-select2-search-choice',
14570             cn: [
14571                 {
14572                     tag: 'div',
14573                     html: dv
14574                 },
14575                 {
14576                     tag: 'a',
14577                     href: '#',
14578                     cls: 'roo-select2-search-choice-close fa fa-times',
14579                     tabindex: '-1'
14580                 }
14581             ]
14582             
14583         }, this.searchField);
14584         
14585         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14586         
14587         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14588         
14589         this.item.push(o);
14590         
14591         this.lastData = o;
14592         
14593         this.syncValue();
14594         
14595         this.inputEl().dom.value = '';
14596         
14597         this.validate();
14598     },
14599     
14600     onRemoveItem : function(e, _self, o)
14601     {
14602         e.preventDefault();
14603         
14604         this.lastItem = Roo.apply([], this.item);
14605         
14606         var index = this.item.indexOf(o.data) * 1;
14607         
14608         if( index < 0){
14609             Roo.log('not this item?!');
14610             return;
14611         }
14612         
14613         this.item.splice(index, 1);
14614         o.item.remove();
14615         
14616         this.syncValue();
14617         
14618         this.fireEvent('remove', this, e);
14619         
14620         this.validate();
14621         
14622     },
14623     
14624     syncValue : function()
14625     {
14626         if(!this.item.length){
14627             this.clearValue();
14628             return;
14629         }
14630             
14631         var value = [];
14632         var _this = this;
14633         Roo.each(this.item, function(i){
14634             if(_this.valueField){
14635                 value.push(i[_this.valueField]);
14636                 return;
14637             }
14638
14639             value.push(i);
14640         });
14641
14642         this.value = value.join(',');
14643
14644         if(this.hiddenField){
14645             this.hiddenField.dom.value = this.value;
14646         }
14647         
14648         this.store.fireEvent("datachanged", this.store);
14649         
14650         this.validate();
14651     },
14652     
14653     clearItem : function()
14654     {
14655         if(!this.multiple){
14656             return;
14657         }
14658         
14659         this.item = [];
14660         
14661         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14662            c.remove();
14663         });
14664         
14665         this.syncValue();
14666         
14667         this.validate();
14668         
14669         if(this.tickable && !Roo.isTouch){
14670             this.view.refresh();
14671         }
14672     },
14673     
14674     inputEl: function ()
14675     {
14676         if(Roo.isIOS && this.useNativeIOS){
14677             return this.el.select('select.roo-ios-select', true).first();
14678         }
14679         
14680         if(Roo.isTouch && this.mobileTouchView){
14681             return this.el.select('input.form-control',true).first();
14682         }
14683         
14684         if(this.tickable){
14685             return this.searchField;
14686         }
14687         
14688         return this.el.select('input.form-control',true).first();
14689     },
14690     
14691     onTickableFooterButtonClick : function(e, btn, el)
14692     {
14693         e.preventDefault();
14694         
14695         this.lastItem = Roo.apply([], this.item);
14696         
14697         if(btn && btn.name == 'cancel'){
14698             this.tickItems = Roo.apply([], this.item);
14699             this.collapse();
14700             return;
14701         }
14702         
14703         this.clearItem();
14704         
14705         var _this = this;
14706         
14707         Roo.each(this.tickItems, function(o){
14708             _this.addItem(o);
14709         });
14710         
14711         this.collapse();
14712         
14713     },
14714     
14715     validate : function()
14716     {
14717         if(this.getVisibilityEl().hasClass('hidden')){
14718             return true;
14719         }
14720         
14721         var v = this.getRawValue();
14722         
14723         if(this.multiple){
14724             v = this.getValue();
14725         }
14726         
14727         if(this.disabled || this.allowBlank || v.length){
14728             this.markValid();
14729             return true;
14730         }
14731         
14732         this.markInvalid();
14733         return false;
14734     },
14735     
14736     tickableInputEl : function()
14737     {
14738         if(!this.tickable || !this.editable){
14739             return this.inputEl();
14740         }
14741         
14742         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14743     },
14744     
14745     
14746     getAutoCreateTouchView : function()
14747     {
14748         var id = Roo.id();
14749         
14750         var cfg = {
14751             cls: 'form-group' //input-group
14752         };
14753         
14754         var input =  {
14755             tag: 'input',
14756             id : id,
14757             type : this.inputType,
14758             cls : 'form-control x-combo-noedit',
14759             autocomplete: 'new-password',
14760             placeholder : this.placeholder || '',
14761             readonly : true
14762         };
14763         
14764         if (this.name) {
14765             input.name = this.name;
14766         }
14767         
14768         if (this.size) {
14769             input.cls += ' input-' + this.size;
14770         }
14771         
14772         if (this.disabled) {
14773             input.disabled = true;
14774         }
14775         
14776         var inputblock = {
14777             cls : '',
14778             cn : [
14779                 input
14780             ]
14781         };
14782         
14783         if(this.before){
14784             inputblock.cls += ' input-group';
14785             
14786             inputblock.cn.unshift({
14787                 tag :'span',
14788                 cls : 'input-group-addon',
14789                 html : this.before
14790             });
14791         }
14792         
14793         if(this.removable && !this.multiple){
14794             inputblock.cls += ' roo-removable';
14795             
14796             inputblock.cn.push({
14797                 tag: 'button',
14798                 html : 'x',
14799                 cls : 'roo-combo-removable-btn close'
14800             });
14801         }
14802
14803         if(this.hasFeedback && !this.allowBlank){
14804             
14805             inputblock.cls += ' has-feedback';
14806             
14807             inputblock.cn.push({
14808                 tag: 'span',
14809                 cls: 'glyphicon form-control-feedback'
14810             });
14811             
14812         }
14813         
14814         if (this.after) {
14815             
14816             inputblock.cls += (this.before) ? '' : ' input-group';
14817             
14818             inputblock.cn.push({
14819                 tag :'span',
14820                 cls : 'input-group-addon',
14821                 html : this.after
14822             });
14823         }
14824
14825         var box = {
14826             tag: 'div',
14827             cn: [
14828                 {
14829                     tag: 'input',
14830                     type : 'hidden',
14831                     cls: 'form-hidden-field'
14832                 },
14833                 inputblock
14834             ]
14835             
14836         };
14837         
14838         if(this.multiple){
14839             box = {
14840                 tag: 'div',
14841                 cn: [
14842                     {
14843                         tag: 'input',
14844                         type : 'hidden',
14845                         cls: 'form-hidden-field'
14846                     },
14847                     {
14848                         tag: 'ul',
14849                         cls: 'roo-select2-choices',
14850                         cn:[
14851                             {
14852                                 tag: 'li',
14853                                 cls: 'roo-select2-search-field',
14854                                 cn: [
14855
14856                                     inputblock
14857                                 ]
14858                             }
14859                         ]
14860                     }
14861                 ]
14862             }
14863         };
14864         
14865         var combobox = {
14866             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14867             cn: [
14868                 box
14869             ]
14870         };
14871         
14872         if(!this.multiple && this.showToggleBtn){
14873             
14874             var caret = {
14875                         tag: 'span',
14876                         cls: 'caret'
14877             };
14878             
14879             if (this.caret != false) {
14880                 caret = {
14881                      tag: 'i',
14882                      cls: 'fa fa-' + this.caret
14883                 };
14884                 
14885             }
14886             
14887             combobox.cn.push({
14888                 tag :'span',
14889                 cls : 'input-group-addon btn dropdown-toggle',
14890                 cn : [
14891                     caret,
14892                     {
14893                         tag: 'span',
14894                         cls: 'combobox-clear',
14895                         cn  : [
14896                             {
14897                                 tag : 'i',
14898                                 cls: 'icon-remove'
14899                             }
14900                         ]
14901                     }
14902                 ]
14903
14904             })
14905         }
14906         
14907         if(this.multiple){
14908             combobox.cls += ' roo-select2-container-multi';
14909         }
14910         
14911         var align = this.labelAlign || this.parentLabelAlign();
14912         
14913         if (align ==='left' && this.fieldLabel.length) {
14914
14915             cfg.cn = [
14916                 {
14917                    tag : 'i',
14918                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14919                    tooltip : 'This field is required'
14920                 },
14921                 {
14922                     tag: 'label',
14923                     cls : 'control-label',
14924                     html : this.fieldLabel
14925
14926                 },
14927                 {
14928                     cls : '', 
14929                     cn: [
14930                         combobox
14931                     ]
14932                 }
14933             ];
14934             
14935             var labelCfg = cfg.cn[1];
14936             var contentCfg = cfg.cn[2];
14937             
14938
14939             if(this.indicatorpos == 'right'){
14940                 cfg.cn = [
14941                     {
14942                         tag: 'label',
14943                         'for' :  id,
14944                         cls : 'control-label',
14945                         cn : [
14946                             {
14947                                 tag : 'span',
14948                                 html : this.fieldLabel
14949                             },
14950                             {
14951                                 tag : 'i',
14952                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14953                                 tooltip : 'This field is required'
14954                             }
14955                         ]
14956                     },
14957                     {
14958                         cls : "",
14959                         cn: [
14960                             combobox
14961                         ]
14962                     }
14963
14964                 ];
14965                 
14966                 labelCfg = cfg.cn[0];
14967                 contentCfg = cfg.cn[1];
14968             }
14969             
14970            
14971             
14972             if(this.labelWidth > 12){
14973                 labelCfg.style = "width: " + this.labelWidth + 'px';
14974             }
14975             
14976             if(this.labelWidth < 13 && this.labelmd == 0){
14977                 this.labelmd = this.labelWidth;
14978             }
14979             
14980             if(this.labellg > 0){
14981                 labelCfg.cls += ' col-lg-' + this.labellg;
14982                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14983             }
14984             
14985             if(this.labelmd > 0){
14986                 labelCfg.cls += ' col-md-' + this.labelmd;
14987                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14988             }
14989             
14990             if(this.labelsm > 0){
14991                 labelCfg.cls += ' col-sm-' + this.labelsm;
14992                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14993             }
14994             
14995             if(this.labelxs > 0){
14996                 labelCfg.cls += ' col-xs-' + this.labelxs;
14997                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14998             }
14999                 
15000                 
15001         } else if ( this.fieldLabel.length) {
15002             cfg.cn = [
15003                 {
15004                    tag : 'i',
15005                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15006                    tooltip : 'This field is required'
15007                 },
15008                 {
15009                     tag: 'label',
15010                     cls : 'control-label',
15011                     html : this.fieldLabel
15012
15013                 },
15014                 {
15015                     cls : '', 
15016                     cn: [
15017                         combobox
15018                     ]
15019                 }
15020             ];
15021             
15022             if(this.indicatorpos == 'right'){
15023                 cfg.cn = [
15024                     {
15025                         tag: 'label',
15026                         cls : 'control-label',
15027                         html : this.fieldLabel,
15028                         cn : [
15029                             {
15030                                tag : 'i',
15031                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15032                                tooltip : 'This field is required'
15033                             }
15034                         ]
15035                     },
15036                     {
15037                         cls : '', 
15038                         cn: [
15039                             combobox
15040                         ]
15041                     }
15042                 ];
15043             }
15044         } else {
15045             cfg.cn = combobox;    
15046         }
15047         
15048         
15049         var settings = this;
15050         
15051         ['xs','sm','md','lg'].map(function(size){
15052             if (settings[size]) {
15053                 cfg.cls += ' col-' + size + '-' + settings[size];
15054             }
15055         });
15056         
15057         return cfg;
15058     },
15059     
15060     initTouchView : function()
15061     {
15062         this.renderTouchView();
15063         
15064         this.touchViewEl.on('scroll', function(){
15065             this.el.dom.scrollTop = 0;
15066         }, this);
15067         
15068         this.originalValue = this.getValue();
15069         
15070         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15071         
15072         this.inputEl().on("click", this.showTouchView, this);
15073         if (this.triggerEl) {
15074             this.triggerEl.on("click", this.showTouchView, this);
15075         }
15076         
15077         
15078         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15079         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15080         
15081         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15082         
15083         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15084         this.store.on('load', this.onTouchViewLoad, this);
15085         this.store.on('loadexception', this.onTouchViewLoadException, this);
15086         
15087         if(this.hiddenName){
15088             
15089             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15090             
15091             this.hiddenField.dom.value =
15092                 this.hiddenValue !== undefined ? this.hiddenValue :
15093                 this.value !== undefined ? this.value : '';
15094         
15095             this.el.dom.removeAttribute('name');
15096             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15097         }
15098         
15099         if(this.multiple){
15100             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15101             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15102         }
15103         
15104         if(this.removable && !this.multiple){
15105             var close = this.closeTriggerEl();
15106             if(close){
15107                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15108                 close.on('click', this.removeBtnClick, this, close);
15109             }
15110         }
15111         /*
15112          * fix the bug in Safari iOS8
15113          */
15114         this.inputEl().on("focus", function(e){
15115             document.activeElement.blur();
15116         }, this);
15117         
15118         return;
15119         
15120         
15121     },
15122     
15123     renderTouchView : function()
15124     {
15125         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15126         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15127         
15128         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15129         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15130         
15131         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15132         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15133         this.touchViewBodyEl.setStyle('overflow', 'auto');
15134         
15135         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15136         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15137         
15138         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15139         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15140         
15141     },
15142     
15143     showTouchView : function()
15144     {
15145         if(this.disabled){
15146             return;
15147         }
15148         
15149         this.touchViewHeaderEl.hide();
15150
15151         if(this.modalTitle.length){
15152             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15153             this.touchViewHeaderEl.show();
15154         }
15155
15156         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15157         this.touchViewEl.show();
15158
15159         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15160         
15161         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15162         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15163
15164         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15165
15166         if(this.modalTitle.length){
15167             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15168         }
15169         
15170         this.touchViewBodyEl.setHeight(bodyHeight);
15171
15172         if(this.animate){
15173             var _this = this;
15174             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15175         }else{
15176             this.touchViewEl.addClass('in');
15177         }
15178
15179         this.doTouchViewQuery();
15180         
15181     },
15182     
15183     hideTouchView : function()
15184     {
15185         this.touchViewEl.removeClass('in');
15186
15187         if(this.animate){
15188             var _this = this;
15189             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15190         }else{
15191             this.touchViewEl.setStyle('display', 'none');
15192         }
15193         
15194     },
15195     
15196     setTouchViewValue : function()
15197     {
15198         if(this.multiple){
15199             this.clearItem();
15200         
15201             var _this = this;
15202
15203             Roo.each(this.tickItems, function(o){
15204                 this.addItem(o);
15205             }, this);
15206         }
15207         
15208         this.hideTouchView();
15209     },
15210     
15211     doTouchViewQuery : function()
15212     {
15213         var qe = {
15214             query: '',
15215             forceAll: true,
15216             combo: this,
15217             cancel:false
15218         };
15219         
15220         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15221             return false;
15222         }
15223         
15224         if(!this.alwaysQuery || this.mode == 'local'){
15225             this.onTouchViewLoad();
15226             return;
15227         }
15228         
15229         this.store.load();
15230     },
15231     
15232     onTouchViewBeforeLoad : function(combo,opts)
15233     {
15234         return;
15235     },
15236
15237     // private
15238     onTouchViewLoad : function()
15239     {
15240         if(this.store.getCount() < 1){
15241             this.onTouchViewEmptyResults();
15242             return;
15243         }
15244         
15245         this.clearTouchView();
15246         
15247         var rawValue = this.getRawValue();
15248         
15249         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15250         
15251         this.tickItems = [];
15252         
15253         this.store.data.each(function(d, rowIndex){
15254             var row = this.touchViewListGroup.createChild(template);
15255             
15256             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15257                 row.addClass(d.data.cls);
15258             }
15259             
15260             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15261                 var cfg = {
15262                     data : d.data,
15263                     html : d.data[this.displayField]
15264                 };
15265                 
15266                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15267                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15268                 }
15269             }
15270             row.removeClass('selected');
15271             if(!this.multiple && this.valueField &&
15272                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15273             {
15274                 // radio buttons..
15275                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15276                 row.addClass('selected');
15277             }
15278             
15279             if(this.multiple && this.valueField &&
15280                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15281             {
15282                 
15283                 // checkboxes...
15284                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15285                 this.tickItems.push(d.data);
15286             }
15287             
15288             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15289             
15290         }, this);
15291         
15292         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15293         
15294         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15295
15296         if(this.modalTitle.length){
15297             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15298         }
15299
15300         var listHeight = this.touchViewListGroup.getHeight();
15301         
15302         var _this = this;
15303         
15304         if(firstChecked && listHeight > bodyHeight){
15305             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15306         }
15307         
15308     },
15309     
15310     onTouchViewLoadException : function()
15311     {
15312         this.hideTouchView();
15313     },
15314     
15315     onTouchViewEmptyResults : function()
15316     {
15317         this.clearTouchView();
15318         
15319         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15320         
15321         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15322         
15323     },
15324     
15325     clearTouchView : function()
15326     {
15327         this.touchViewListGroup.dom.innerHTML = '';
15328     },
15329     
15330     onTouchViewClick : function(e, el, o)
15331     {
15332         e.preventDefault();
15333         
15334         var row = o.row;
15335         var rowIndex = o.rowIndex;
15336         
15337         var r = this.store.getAt(rowIndex);
15338         
15339         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15340             
15341             if(!this.multiple){
15342                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15343                     c.dom.removeAttribute('checked');
15344                 }, this);
15345
15346                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15347
15348                 this.setFromData(r.data);
15349
15350                 var close = this.closeTriggerEl();
15351
15352                 if(close){
15353                     close.show();
15354                 }
15355
15356                 this.hideTouchView();
15357
15358                 this.fireEvent('select', this, r, rowIndex);
15359
15360                 return;
15361             }
15362
15363             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15364                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15365                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15366                 return;
15367             }
15368
15369             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15370             this.addItem(r.data);
15371             this.tickItems.push(r.data);
15372         }
15373     },
15374     
15375     getAutoCreateNativeIOS : function()
15376     {
15377         var cfg = {
15378             cls: 'form-group' //input-group,
15379         };
15380         
15381         var combobox =  {
15382             tag: 'select',
15383             cls : 'roo-ios-select'
15384         };
15385         
15386         if (this.name) {
15387             combobox.name = this.name;
15388         }
15389         
15390         if (this.disabled) {
15391             combobox.disabled = true;
15392         }
15393         
15394         var settings = this;
15395         
15396         ['xs','sm','md','lg'].map(function(size){
15397             if (settings[size]) {
15398                 cfg.cls += ' col-' + size + '-' + settings[size];
15399             }
15400         });
15401         
15402         cfg.cn = combobox;
15403         
15404         return cfg;
15405         
15406     },
15407     
15408     initIOSView : function()
15409     {
15410         this.store.on('load', this.onIOSViewLoad, this);
15411         
15412         return;
15413     },
15414     
15415     onIOSViewLoad : function()
15416     {
15417         if(this.store.getCount() < 1){
15418             return;
15419         }
15420         
15421         this.clearIOSView();
15422         
15423         if(this.allowBlank) {
15424             
15425             var default_text = '-- SELECT --';
15426             
15427             if(this.placeholder.length){
15428                 default_text = this.placeholder;
15429             }
15430             
15431             if(this.emptyTitle.length){
15432                 default_text += ' - ' + this.emptyTitle + ' -';
15433             }
15434             
15435             var opt = this.inputEl().createChild({
15436                 tag: 'option',
15437                 value : 0,
15438                 html : default_text
15439             });
15440             
15441             var o = {};
15442             o[this.valueField] = 0;
15443             o[this.displayField] = default_text;
15444             
15445             this.ios_options.push({
15446                 data : o,
15447                 el : opt
15448             });
15449             
15450         }
15451         
15452         this.store.data.each(function(d, rowIndex){
15453             
15454             var html = '';
15455             
15456             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15457                 html = d.data[this.displayField];
15458             }
15459             
15460             var value = '';
15461             
15462             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15463                 value = d.data[this.valueField];
15464             }
15465             
15466             var option = {
15467                 tag: 'option',
15468                 value : value,
15469                 html : html
15470             };
15471             
15472             if(this.value == d.data[this.valueField]){
15473                 option['selected'] = true;
15474             }
15475             
15476             var opt = this.inputEl().createChild(option);
15477             
15478             this.ios_options.push({
15479                 data : d.data,
15480                 el : opt
15481             });
15482             
15483         }, this);
15484         
15485         this.inputEl().on('change', function(){
15486            this.fireEvent('select', this);
15487         }, this);
15488         
15489     },
15490     
15491     clearIOSView: function()
15492     {
15493         this.inputEl().dom.innerHTML = '';
15494         
15495         this.ios_options = [];
15496     },
15497     
15498     setIOSValue: function(v)
15499     {
15500         this.value = v;
15501         
15502         if(!this.ios_options){
15503             return;
15504         }
15505         
15506         Roo.each(this.ios_options, function(opts){
15507            
15508            opts.el.dom.removeAttribute('selected');
15509            
15510            if(opts.data[this.valueField] != v){
15511                return;
15512            }
15513            
15514            opts.el.dom.setAttribute('selected', true);
15515            
15516         }, this);
15517     }
15518
15519     /** 
15520     * @cfg {Boolean} grow 
15521     * @hide 
15522     */
15523     /** 
15524     * @cfg {Number} growMin 
15525     * @hide 
15526     */
15527     /** 
15528     * @cfg {Number} growMax 
15529     * @hide 
15530     */
15531     /**
15532      * @hide
15533      * @method autoSize
15534      */
15535 });
15536
15537 Roo.apply(Roo.bootstrap.ComboBox,  {
15538     
15539     header : {
15540         tag: 'div',
15541         cls: 'modal-header',
15542         cn: [
15543             {
15544                 tag: 'h4',
15545                 cls: 'modal-title'
15546             }
15547         ]
15548     },
15549     
15550     body : {
15551         tag: 'div',
15552         cls: 'modal-body',
15553         cn: [
15554             {
15555                 tag: 'ul',
15556                 cls: 'list-group'
15557             }
15558         ]
15559     },
15560     
15561     listItemRadio : {
15562         tag: 'li',
15563         cls: 'list-group-item',
15564         cn: [
15565             {
15566                 tag: 'span',
15567                 cls: 'roo-combobox-list-group-item-value'
15568             },
15569             {
15570                 tag: 'div',
15571                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15572                 cn: [
15573                     {
15574                         tag: 'input',
15575                         type: 'radio'
15576                     },
15577                     {
15578                         tag: 'label'
15579                     }
15580                 ]
15581             }
15582         ]
15583     },
15584     
15585     listItemCheckbox : {
15586         tag: 'li',
15587         cls: 'list-group-item',
15588         cn: [
15589             {
15590                 tag: 'span',
15591                 cls: 'roo-combobox-list-group-item-value'
15592             },
15593             {
15594                 tag: 'div',
15595                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15596                 cn: [
15597                     {
15598                         tag: 'input',
15599                         type: 'checkbox'
15600                     },
15601                     {
15602                         tag: 'label'
15603                     }
15604                 ]
15605             }
15606         ]
15607     },
15608     
15609     emptyResult : {
15610         tag: 'div',
15611         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15612     },
15613     
15614     footer : {
15615         tag: 'div',
15616         cls: 'modal-footer',
15617         cn: [
15618             {
15619                 tag: 'div',
15620                 cls: 'row',
15621                 cn: [
15622                     {
15623                         tag: 'div',
15624                         cls: 'col-xs-6 text-left',
15625                         cn: {
15626                             tag: 'button',
15627                             cls: 'btn btn-danger roo-touch-view-cancel',
15628                             html: 'Cancel'
15629                         }
15630                     },
15631                     {
15632                         tag: 'div',
15633                         cls: 'col-xs-6 text-right',
15634                         cn: {
15635                             tag: 'button',
15636                             cls: 'btn btn-success roo-touch-view-ok',
15637                             html: 'OK'
15638                         }
15639                     }
15640                 ]
15641             }
15642         ]
15643         
15644     }
15645 });
15646
15647 Roo.apply(Roo.bootstrap.ComboBox,  {
15648     
15649     touchViewTemplate : {
15650         tag: 'div',
15651         cls: 'modal fade roo-combobox-touch-view',
15652         cn: [
15653             {
15654                 tag: 'div',
15655                 cls: 'modal-dialog',
15656                 style : 'position:fixed', // we have to fix position....
15657                 cn: [
15658                     {
15659                         tag: 'div',
15660                         cls: 'modal-content',
15661                         cn: [
15662                             Roo.bootstrap.ComboBox.header,
15663                             Roo.bootstrap.ComboBox.body,
15664                             Roo.bootstrap.ComboBox.footer
15665                         ]
15666                     }
15667                 ]
15668             }
15669         ]
15670     }
15671 });/*
15672  * Based on:
15673  * Ext JS Library 1.1.1
15674  * Copyright(c) 2006-2007, Ext JS, LLC.
15675  *
15676  * Originally Released Under LGPL - original licence link has changed is not relivant.
15677  *
15678  * Fork - LGPL
15679  * <script type="text/javascript">
15680  */
15681
15682 /**
15683  * @class Roo.View
15684  * @extends Roo.util.Observable
15685  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15686  * This class also supports single and multi selection modes. <br>
15687  * Create a data model bound view:
15688  <pre><code>
15689  var store = new Roo.data.Store(...);
15690
15691  var view = new Roo.View({
15692     el : "my-element",
15693     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15694  
15695     singleSelect: true,
15696     selectedClass: "ydataview-selected",
15697     store: store
15698  });
15699
15700  // listen for node click?
15701  view.on("click", function(vw, index, node, e){
15702  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15703  });
15704
15705  // load XML data
15706  dataModel.load("foobar.xml");
15707  </code></pre>
15708  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15709  * <br><br>
15710  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15711  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15712  * 
15713  * Note: old style constructor is still suported (container, template, config)
15714  * 
15715  * @constructor
15716  * Create a new View
15717  * @param {Object} config The config object
15718  * 
15719  */
15720 Roo.View = function(config, depreciated_tpl, depreciated_config){
15721     
15722     this.parent = false;
15723     
15724     if (typeof(depreciated_tpl) == 'undefined') {
15725         // new way.. - universal constructor.
15726         Roo.apply(this, config);
15727         this.el  = Roo.get(this.el);
15728     } else {
15729         // old format..
15730         this.el  = Roo.get(config);
15731         this.tpl = depreciated_tpl;
15732         Roo.apply(this, depreciated_config);
15733     }
15734     this.wrapEl  = this.el.wrap().wrap();
15735     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15736     
15737     
15738     if(typeof(this.tpl) == "string"){
15739         this.tpl = new Roo.Template(this.tpl);
15740     } else {
15741         // support xtype ctors..
15742         this.tpl = new Roo.factory(this.tpl, Roo);
15743     }
15744     
15745     
15746     this.tpl.compile();
15747     
15748     /** @private */
15749     this.addEvents({
15750         /**
15751          * @event beforeclick
15752          * Fires before a click is processed. Returns false to cancel the default action.
15753          * @param {Roo.View} this
15754          * @param {Number} index The index of the target node
15755          * @param {HTMLElement} node The target node
15756          * @param {Roo.EventObject} e The raw event object
15757          */
15758             "beforeclick" : true,
15759         /**
15760          * @event click
15761          * Fires when a template node is clicked.
15762          * @param {Roo.View} this
15763          * @param {Number} index The index of the target node
15764          * @param {HTMLElement} node The target node
15765          * @param {Roo.EventObject} e The raw event object
15766          */
15767             "click" : true,
15768         /**
15769          * @event dblclick
15770          * Fires when a template node is double clicked.
15771          * @param {Roo.View} this
15772          * @param {Number} index The index of the target node
15773          * @param {HTMLElement} node The target node
15774          * @param {Roo.EventObject} e The raw event object
15775          */
15776             "dblclick" : true,
15777         /**
15778          * @event contextmenu
15779          * Fires when a template node is right clicked.
15780          * @param {Roo.View} this
15781          * @param {Number} index The index of the target node
15782          * @param {HTMLElement} node The target node
15783          * @param {Roo.EventObject} e The raw event object
15784          */
15785             "contextmenu" : true,
15786         /**
15787          * @event selectionchange
15788          * Fires when the selected nodes change.
15789          * @param {Roo.View} this
15790          * @param {Array} selections Array of the selected nodes
15791          */
15792             "selectionchange" : true,
15793     
15794         /**
15795          * @event beforeselect
15796          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15797          * @param {Roo.View} this
15798          * @param {HTMLElement} node The node to be selected
15799          * @param {Array} selections Array of currently selected nodes
15800          */
15801             "beforeselect" : true,
15802         /**
15803          * @event preparedata
15804          * Fires on every row to render, to allow you to change the data.
15805          * @param {Roo.View} this
15806          * @param {Object} data to be rendered (change this)
15807          */
15808           "preparedata" : true
15809           
15810           
15811         });
15812
15813
15814
15815     this.el.on({
15816         "click": this.onClick,
15817         "dblclick": this.onDblClick,
15818         "contextmenu": this.onContextMenu,
15819         scope:this
15820     });
15821
15822     this.selections = [];
15823     this.nodes = [];
15824     this.cmp = new Roo.CompositeElementLite([]);
15825     if(this.store){
15826         this.store = Roo.factory(this.store, Roo.data);
15827         this.setStore(this.store, true);
15828     }
15829     
15830     if ( this.footer && this.footer.xtype) {
15831            
15832          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15833         
15834         this.footer.dataSource = this.store;
15835         this.footer.container = fctr;
15836         this.footer = Roo.factory(this.footer, Roo);
15837         fctr.insertFirst(this.el);
15838         
15839         // this is a bit insane - as the paging toolbar seems to detach the el..
15840 //        dom.parentNode.parentNode.parentNode
15841          // they get detached?
15842     }
15843     
15844     
15845     Roo.View.superclass.constructor.call(this);
15846     
15847     
15848 };
15849
15850 Roo.extend(Roo.View, Roo.util.Observable, {
15851     
15852      /**
15853      * @cfg {Roo.data.Store} store Data store to load data from.
15854      */
15855     store : false,
15856     
15857     /**
15858      * @cfg {String|Roo.Element} el The container element.
15859      */
15860     el : '',
15861     
15862     /**
15863      * @cfg {String|Roo.Template} tpl The template used by this View 
15864      */
15865     tpl : false,
15866     /**
15867      * @cfg {String} dataName the named area of the template to use as the data area
15868      *                          Works with domtemplates roo-name="name"
15869      */
15870     dataName: false,
15871     /**
15872      * @cfg {String} selectedClass The css class to add to selected nodes
15873      */
15874     selectedClass : "x-view-selected",
15875      /**
15876      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15877      */
15878     emptyText : "",
15879     
15880     /**
15881      * @cfg {String} text to display on mask (default Loading)
15882      */
15883     mask : false,
15884     /**
15885      * @cfg {Boolean} multiSelect Allow multiple selection
15886      */
15887     multiSelect : false,
15888     /**
15889      * @cfg {Boolean} singleSelect Allow single selection
15890      */
15891     singleSelect:  false,
15892     
15893     /**
15894      * @cfg {Boolean} toggleSelect - selecting 
15895      */
15896     toggleSelect : false,
15897     
15898     /**
15899      * @cfg {Boolean} tickable - selecting 
15900      */
15901     tickable : false,
15902     
15903     /**
15904      * Returns the element this view is bound to.
15905      * @return {Roo.Element}
15906      */
15907     getEl : function(){
15908         return this.wrapEl;
15909     },
15910     
15911     
15912
15913     /**
15914      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15915      */
15916     refresh : function(){
15917         //Roo.log('refresh');
15918         var t = this.tpl;
15919         
15920         // if we are using something like 'domtemplate', then
15921         // the what gets used is:
15922         // t.applySubtemplate(NAME, data, wrapping data..)
15923         // the outer template then get' applied with
15924         //     the store 'extra data'
15925         // and the body get's added to the
15926         //      roo-name="data" node?
15927         //      <span class='roo-tpl-{name}'></span> ?????
15928         
15929         
15930         
15931         this.clearSelections();
15932         this.el.update("");
15933         var html = [];
15934         var records = this.store.getRange();
15935         if(records.length < 1) {
15936             
15937             // is this valid??  = should it render a template??
15938             
15939             this.el.update(this.emptyText);
15940             return;
15941         }
15942         var el = this.el;
15943         if (this.dataName) {
15944             this.el.update(t.apply(this.store.meta)); //????
15945             el = this.el.child('.roo-tpl-' + this.dataName);
15946         }
15947         
15948         for(var i = 0, len = records.length; i < len; i++){
15949             var data = this.prepareData(records[i].data, i, records[i]);
15950             this.fireEvent("preparedata", this, data, i, records[i]);
15951             
15952             var d = Roo.apply({}, data);
15953             
15954             if(this.tickable){
15955                 Roo.apply(d, {'roo-id' : Roo.id()});
15956                 
15957                 var _this = this;
15958             
15959                 Roo.each(this.parent.item, function(item){
15960                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15961                         return;
15962                     }
15963                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15964                 });
15965             }
15966             
15967             html[html.length] = Roo.util.Format.trim(
15968                 this.dataName ?
15969                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15970                     t.apply(d)
15971             );
15972         }
15973         
15974         
15975         
15976         el.update(html.join(""));
15977         this.nodes = el.dom.childNodes;
15978         this.updateIndexes(0);
15979     },
15980     
15981
15982     /**
15983      * Function to override to reformat the data that is sent to
15984      * the template for each node.
15985      * DEPRICATED - use the preparedata event handler.
15986      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15987      * a JSON object for an UpdateManager bound view).
15988      */
15989     prepareData : function(data, index, record)
15990     {
15991         this.fireEvent("preparedata", this, data, index, record);
15992         return data;
15993     },
15994
15995     onUpdate : function(ds, record){
15996         // Roo.log('on update');   
15997         this.clearSelections();
15998         var index = this.store.indexOf(record);
15999         var n = this.nodes[index];
16000         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16001         n.parentNode.removeChild(n);
16002         this.updateIndexes(index, index);
16003     },
16004
16005     
16006     
16007 // --------- FIXME     
16008     onAdd : function(ds, records, index)
16009     {
16010         //Roo.log(['on Add', ds, records, index] );        
16011         this.clearSelections();
16012         if(this.nodes.length == 0){
16013             this.refresh();
16014             return;
16015         }
16016         var n = this.nodes[index];
16017         for(var i = 0, len = records.length; i < len; i++){
16018             var d = this.prepareData(records[i].data, i, records[i]);
16019             if(n){
16020                 this.tpl.insertBefore(n, d);
16021             }else{
16022                 
16023                 this.tpl.append(this.el, d);
16024             }
16025         }
16026         this.updateIndexes(index);
16027     },
16028
16029     onRemove : function(ds, record, index){
16030        // Roo.log('onRemove');
16031         this.clearSelections();
16032         var el = this.dataName  ?
16033             this.el.child('.roo-tpl-' + this.dataName) :
16034             this.el; 
16035         
16036         el.dom.removeChild(this.nodes[index]);
16037         this.updateIndexes(index);
16038     },
16039
16040     /**
16041      * Refresh an individual node.
16042      * @param {Number} index
16043      */
16044     refreshNode : function(index){
16045         this.onUpdate(this.store, this.store.getAt(index));
16046     },
16047
16048     updateIndexes : function(startIndex, endIndex){
16049         var ns = this.nodes;
16050         startIndex = startIndex || 0;
16051         endIndex = endIndex || ns.length - 1;
16052         for(var i = startIndex; i <= endIndex; i++){
16053             ns[i].nodeIndex = i;
16054         }
16055     },
16056
16057     /**
16058      * Changes the data store this view uses and refresh the view.
16059      * @param {Store} store
16060      */
16061     setStore : function(store, initial){
16062         if(!initial && this.store){
16063             this.store.un("datachanged", this.refresh);
16064             this.store.un("add", this.onAdd);
16065             this.store.un("remove", this.onRemove);
16066             this.store.un("update", this.onUpdate);
16067             this.store.un("clear", this.refresh);
16068             this.store.un("beforeload", this.onBeforeLoad);
16069             this.store.un("load", this.onLoad);
16070             this.store.un("loadexception", this.onLoad);
16071         }
16072         if(store){
16073           
16074             store.on("datachanged", this.refresh, this);
16075             store.on("add", this.onAdd, this);
16076             store.on("remove", this.onRemove, this);
16077             store.on("update", this.onUpdate, this);
16078             store.on("clear", this.refresh, this);
16079             store.on("beforeload", this.onBeforeLoad, this);
16080             store.on("load", this.onLoad, this);
16081             store.on("loadexception", this.onLoad, this);
16082         }
16083         
16084         if(store){
16085             this.refresh();
16086         }
16087     },
16088     /**
16089      * onbeforeLoad - masks the loading area.
16090      *
16091      */
16092     onBeforeLoad : function(store,opts)
16093     {
16094          //Roo.log('onBeforeLoad');   
16095         if (!opts.add) {
16096             this.el.update("");
16097         }
16098         this.el.mask(this.mask ? this.mask : "Loading" ); 
16099     },
16100     onLoad : function ()
16101     {
16102         this.el.unmask();
16103     },
16104     
16105
16106     /**
16107      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16108      * @param {HTMLElement} node
16109      * @return {HTMLElement} The template node
16110      */
16111     findItemFromChild : function(node){
16112         var el = this.dataName  ?
16113             this.el.child('.roo-tpl-' + this.dataName,true) :
16114             this.el.dom; 
16115         
16116         if(!node || node.parentNode == el){
16117                     return node;
16118             }
16119             var p = node.parentNode;
16120             while(p && p != el){
16121             if(p.parentNode == el){
16122                 return p;
16123             }
16124             p = p.parentNode;
16125         }
16126             return null;
16127     },
16128
16129     /** @ignore */
16130     onClick : function(e){
16131         var item = this.findItemFromChild(e.getTarget());
16132         if(item){
16133             var index = this.indexOf(item);
16134             if(this.onItemClick(item, index, e) !== false){
16135                 this.fireEvent("click", this, index, item, e);
16136             }
16137         }else{
16138             this.clearSelections();
16139         }
16140     },
16141
16142     /** @ignore */
16143     onContextMenu : function(e){
16144         var item = this.findItemFromChild(e.getTarget());
16145         if(item){
16146             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16147         }
16148     },
16149
16150     /** @ignore */
16151     onDblClick : function(e){
16152         var item = this.findItemFromChild(e.getTarget());
16153         if(item){
16154             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16155         }
16156     },
16157
16158     onItemClick : function(item, index, e)
16159     {
16160         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16161             return false;
16162         }
16163         if (this.toggleSelect) {
16164             var m = this.isSelected(item) ? 'unselect' : 'select';
16165             //Roo.log(m);
16166             var _t = this;
16167             _t[m](item, true, false);
16168             return true;
16169         }
16170         if(this.multiSelect || this.singleSelect){
16171             if(this.multiSelect && e.shiftKey && this.lastSelection){
16172                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16173             }else{
16174                 this.select(item, this.multiSelect && e.ctrlKey);
16175                 this.lastSelection = item;
16176             }
16177             
16178             if(!this.tickable){
16179                 e.preventDefault();
16180             }
16181             
16182         }
16183         return true;
16184     },
16185
16186     /**
16187      * Get the number of selected nodes.
16188      * @return {Number}
16189      */
16190     getSelectionCount : function(){
16191         return this.selections.length;
16192     },
16193
16194     /**
16195      * Get the currently selected nodes.
16196      * @return {Array} An array of HTMLElements
16197      */
16198     getSelectedNodes : function(){
16199         return this.selections;
16200     },
16201
16202     /**
16203      * Get the indexes of the selected nodes.
16204      * @return {Array}
16205      */
16206     getSelectedIndexes : function(){
16207         var indexes = [], s = this.selections;
16208         for(var i = 0, len = s.length; i < len; i++){
16209             indexes.push(s[i].nodeIndex);
16210         }
16211         return indexes;
16212     },
16213
16214     /**
16215      * Clear all selections
16216      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16217      */
16218     clearSelections : function(suppressEvent){
16219         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16220             this.cmp.elements = this.selections;
16221             this.cmp.removeClass(this.selectedClass);
16222             this.selections = [];
16223             if(!suppressEvent){
16224                 this.fireEvent("selectionchange", this, this.selections);
16225             }
16226         }
16227     },
16228
16229     /**
16230      * Returns true if the passed node is selected
16231      * @param {HTMLElement/Number} node The node or node index
16232      * @return {Boolean}
16233      */
16234     isSelected : function(node){
16235         var s = this.selections;
16236         if(s.length < 1){
16237             return false;
16238         }
16239         node = this.getNode(node);
16240         return s.indexOf(node) !== -1;
16241     },
16242
16243     /**
16244      * Selects nodes.
16245      * @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
16246      * @param {Boolean} keepExisting (optional) true to keep existing selections
16247      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16248      */
16249     select : function(nodeInfo, keepExisting, suppressEvent){
16250         if(nodeInfo instanceof Array){
16251             if(!keepExisting){
16252                 this.clearSelections(true);
16253             }
16254             for(var i = 0, len = nodeInfo.length; i < len; i++){
16255                 this.select(nodeInfo[i], true, true);
16256             }
16257             return;
16258         } 
16259         var node = this.getNode(nodeInfo);
16260         if(!node || this.isSelected(node)){
16261             return; // already selected.
16262         }
16263         if(!keepExisting){
16264             this.clearSelections(true);
16265         }
16266         
16267         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16268             Roo.fly(node).addClass(this.selectedClass);
16269             this.selections.push(node);
16270             if(!suppressEvent){
16271                 this.fireEvent("selectionchange", this, this.selections);
16272             }
16273         }
16274         
16275         
16276     },
16277       /**
16278      * Unselects nodes.
16279      * @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
16280      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16281      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16282      */
16283     unselect : function(nodeInfo, keepExisting, suppressEvent)
16284     {
16285         if(nodeInfo instanceof Array){
16286             Roo.each(this.selections, function(s) {
16287                 this.unselect(s, nodeInfo);
16288             }, this);
16289             return;
16290         }
16291         var node = this.getNode(nodeInfo);
16292         if(!node || !this.isSelected(node)){
16293             //Roo.log("not selected");
16294             return; // not selected.
16295         }
16296         // fireevent???
16297         var ns = [];
16298         Roo.each(this.selections, function(s) {
16299             if (s == node ) {
16300                 Roo.fly(node).removeClass(this.selectedClass);
16301
16302                 return;
16303             }
16304             ns.push(s);
16305         },this);
16306         
16307         this.selections= ns;
16308         this.fireEvent("selectionchange", this, this.selections);
16309     },
16310
16311     /**
16312      * Gets a template node.
16313      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16314      * @return {HTMLElement} The node or null if it wasn't found
16315      */
16316     getNode : function(nodeInfo){
16317         if(typeof nodeInfo == "string"){
16318             return document.getElementById(nodeInfo);
16319         }else if(typeof nodeInfo == "number"){
16320             return this.nodes[nodeInfo];
16321         }
16322         return nodeInfo;
16323     },
16324
16325     /**
16326      * Gets a range template nodes.
16327      * @param {Number} startIndex
16328      * @param {Number} endIndex
16329      * @return {Array} An array of nodes
16330      */
16331     getNodes : function(start, end){
16332         var ns = this.nodes;
16333         start = start || 0;
16334         end = typeof end == "undefined" ? ns.length - 1 : end;
16335         var nodes = [];
16336         if(start <= end){
16337             for(var i = start; i <= end; i++){
16338                 nodes.push(ns[i]);
16339             }
16340         } else{
16341             for(var i = start; i >= end; i--){
16342                 nodes.push(ns[i]);
16343             }
16344         }
16345         return nodes;
16346     },
16347
16348     /**
16349      * Finds the index of the passed node
16350      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16351      * @return {Number} The index of the node or -1
16352      */
16353     indexOf : function(node){
16354         node = this.getNode(node);
16355         if(typeof node.nodeIndex == "number"){
16356             return node.nodeIndex;
16357         }
16358         var ns = this.nodes;
16359         for(var i = 0, len = ns.length; i < len; i++){
16360             if(ns[i] == node){
16361                 return i;
16362             }
16363         }
16364         return -1;
16365     }
16366 });
16367 /*
16368  * - LGPL
16369  *
16370  * based on jquery fullcalendar
16371  * 
16372  */
16373
16374 Roo.bootstrap = Roo.bootstrap || {};
16375 /**
16376  * @class Roo.bootstrap.Calendar
16377  * @extends Roo.bootstrap.Component
16378  * Bootstrap Calendar class
16379  * @cfg {Boolean} loadMask (true|false) default false
16380  * @cfg {Object} header generate the user specific header of the calendar, default false
16381
16382  * @constructor
16383  * Create a new Container
16384  * @param {Object} config The config object
16385  */
16386
16387
16388
16389 Roo.bootstrap.Calendar = function(config){
16390     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16391      this.addEvents({
16392         /**
16393              * @event select
16394              * Fires when a date is selected
16395              * @param {DatePicker} this
16396              * @param {Date} date The selected date
16397              */
16398         'select': true,
16399         /**
16400              * @event monthchange
16401              * Fires when the displayed month changes 
16402              * @param {DatePicker} this
16403              * @param {Date} date The selected month
16404              */
16405         'monthchange': true,
16406         /**
16407              * @event evententer
16408              * Fires when mouse over an event
16409              * @param {Calendar} this
16410              * @param {event} Event
16411              */
16412         'evententer': true,
16413         /**
16414              * @event eventleave
16415              * Fires when the mouse leaves an
16416              * @param {Calendar} this
16417              * @param {event}
16418              */
16419         'eventleave': true,
16420         /**
16421              * @event eventclick
16422              * Fires when the mouse click an
16423              * @param {Calendar} this
16424              * @param {event}
16425              */
16426         'eventclick': true
16427         
16428     });
16429
16430 };
16431
16432 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16433     
16434      /**
16435      * @cfg {Number} startDay
16436      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16437      */
16438     startDay : 0,
16439     
16440     loadMask : false,
16441     
16442     header : false,
16443       
16444     getAutoCreate : function(){
16445         
16446         
16447         var fc_button = function(name, corner, style, content ) {
16448             return Roo.apply({},{
16449                 tag : 'span',
16450                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16451                          (corner.length ?
16452                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16453                             ''
16454                         ),
16455                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16456                 unselectable: 'on'
16457             });
16458         };
16459         
16460         var header = {};
16461         
16462         if(!this.header){
16463             header = {
16464                 tag : 'table',
16465                 cls : 'fc-header',
16466                 style : 'width:100%',
16467                 cn : [
16468                     {
16469                         tag: 'tr',
16470                         cn : [
16471                             {
16472                                 tag : 'td',
16473                                 cls : 'fc-header-left',
16474                                 cn : [
16475                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16476                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16477                                     { tag: 'span', cls: 'fc-header-space' },
16478                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16479
16480
16481                                 ]
16482                             },
16483
16484                             {
16485                                 tag : 'td',
16486                                 cls : 'fc-header-center',
16487                                 cn : [
16488                                     {
16489                                         tag: 'span',
16490                                         cls: 'fc-header-title',
16491                                         cn : {
16492                                             tag: 'H2',
16493                                             html : 'month / year'
16494                                         }
16495                                     }
16496
16497                                 ]
16498                             },
16499                             {
16500                                 tag : 'td',
16501                                 cls : 'fc-header-right',
16502                                 cn : [
16503                               /*      fc_button('month', 'left', '', 'month' ),
16504                                     fc_button('week', '', '', 'week' ),
16505                                     fc_button('day', 'right', '', 'day' )
16506                                 */    
16507
16508                                 ]
16509                             }
16510
16511                         ]
16512                     }
16513                 ]
16514             };
16515         }
16516         
16517         header = this.header;
16518         
16519        
16520         var cal_heads = function() {
16521             var ret = [];
16522             // fixme - handle this.
16523             
16524             for (var i =0; i < Date.dayNames.length; i++) {
16525                 var d = Date.dayNames[i];
16526                 ret.push({
16527                     tag: 'th',
16528                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16529                     html : d.substring(0,3)
16530                 });
16531                 
16532             }
16533             ret[0].cls += ' fc-first';
16534             ret[6].cls += ' fc-last';
16535             return ret;
16536         };
16537         var cal_cell = function(n) {
16538             return  {
16539                 tag: 'td',
16540                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16541                 cn : [
16542                     {
16543                         cn : [
16544                             {
16545                                 cls: 'fc-day-number',
16546                                 html: 'D'
16547                             },
16548                             {
16549                                 cls: 'fc-day-content',
16550                              
16551                                 cn : [
16552                                      {
16553                                         style: 'position: relative;' // height: 17px;
16554                                     }
16555                                 ]
16556                             }
16557                             
16558                             
16559                         ]
16560                     }
16561                 ]
16562                 
16563             }
16564         };
16565         var cal_rows = function() {
16566             
16567             var ret = [];
16568             for (var r = 0; r < 6; r++) {
16569                 var row= {
16570                     tag : 'tr',
16571                     cls : 'fc-week',
16572                     cn : []
16573                 };
16574                 
16575                 for (var i =0; i < Date.dayNames.length; i++) {
16576                     var d = Date.dayNames[i];
16577                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16578
16579                 }
16580                 row.cn[0].cls+=' fc-first';
16581                 row.cn[0].cn[0].style = 'min-height:90px';
16582                 row.cn[6].cls+=' fc-last';
16583                 ret.push(row);
16584                 
16585             }
16586             ret[0].cls += ' fc-first';
16587             ret[4].cls += ' fc-prev-last';
16588             ret[5].cls += ' fc-last';
16589             return ret;
16590             
16591         };
16592         
16593         var cal_table = {
16594             tag: 'table',
16595             cls: 'fc-border-separate',
16596             style : 'width:100%',
16597             cellspacing  : 0,
16598             cn : [
16599                 { 
16600                     tag: 'thead',
16601                     cn : [
16602                         { 
16603                             tag: 'tr',
16604                             cls : 'fc-first fc-last',
16605                             cn : cal_heads()
16606                         }
16607                     ]
16608                 },
16609                 { 
16610                     tag: 'tbody',
16611                     cn : cal_rows()
16612                 }
16613                   
16614             ]
16615         };
16616          
16617          var cfg = {
16618             cls : 'fc fc-ltr',
16619             cn : [
16620                 header,
16621                 {
16622                     cls : 'fc-content',
16623                     style : "position: relative;",
16624                     cn : [
16625                         {
16626                             cls : 'fc-view fc-view-month fc-grid',
16627                             style : 'position: relative',
16628                             unselectable : 'on',
16629                             cn : [
16630                                 {
16631                                     cls : 'fc-event-container',
16632                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16633                                 },
16634                                 cal_table
16635                             ]
16636                         }
16637                     ]
16638     
16639                 }
16640            ] 
16641             
16642         };
16643         
16644          
16645         
16646         return cfg;
16647     },
16648     
16649     
16650     initEvents : function()
16651     {
16652         if(!this.store){
16653             throw "can not find store for calendar";
16654         }
16655         
16656         var mark = {
16657             tag: "div",
16658             cls:"x-dlg-mask",
16659             style: "text-align:center",
16660             cn: [
16661                 {
16662                     tag: "div",
16663                     style: "background-color:white;width:50%;margin:250 auto",
16664                     cn: [
16665                         {
16666                             tag: "img",
16667                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16668                         },
16669                         {
16670                             tag: "span",
16671                             html: "Loading"
16672                         }
16673                         
16674                     ]
16675                 }
16676             ]
16677         };
16678         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16679         
16680         var size = this.el.select('.fc-content', true).first().getSize();
16681         this.maskEl.setSize(size.width, size.height);
16682         this.maskEl.enableDisplayMode("block");
16683         if(!this.loadMask){
16684             this.maskEl.hide();
16685         }
16686         
16687         this.store = Roo.factory(this.store, Roo.data);
16688         this.store.on('load', this.onLoad, this);
16689         this.store.on('beforeload', this.onBeforeLoad, this);
16690         
16691         this.resize();
16692         
16693         this.cells = this.el.select('.fc-day',true);
16694         //Roo.log(this.cells);
16695         this.textNodes = this.el.query('.fc-day-number');
16696         this.cells.addClassOnOver('fc-state-hover');
16697         
16698         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16699         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16700         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16701         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16702         
16703         this.on('monthchange', this.onMonthChange, this);
16704         
16705         this.update(new Date().clearTime());
16706     },
16707     
16708     resize : function() {
16709         var sz  = this.el.getSize();
16710         
16711         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16712         this.el.select('.fc-day-content div',true).setHeight(34);
16713     },
16714     
16715     
16716     // private
16717     showPrevMonth : function(e){
16718         this.update(this.activeDate.add("mo", -1));
16719     },
16720     showToday : function(e){
16721         this.update(new Date().clearTime());
16722     },
16723     // private
16724     showNextMonth : function(e){
16725         this.update(this.activeDate.add("mo", 1));
16726     },
16727
16728     // private
16729     showPrevYear : function(){
16730         this.update(this.activeDate.add("y", -1));
16731     },
16732
16733     // private
16734     showNextYear : function(){
16735         this.update(this.activeDate.add("y", 1));
16736     },
16737
16738     
16739    // private
16740     update : function(date)
16741     {
16742         var vd = this.activeDate;
16743         this.activeDate = date;
16744 //        if(vd && this.el){
16745 //            var t = date.getTime();
16746 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16747 //                Roo.log('using add remove');
16748 //                
16749 //                this.fireEvent('monthchange', this, date);
16750 //                
16751 //                this.cells.removeClass("fc-state-highlight");
16752 //                this.cells.each(function(c){
16753 //                   if(c.dateValue == t){
16754 //                       c.addClass("fc-state-highlight");
16755 //                       setTimeout(function(){
16756 //                            try{c.dom.firstChild.focus();}catch(e){}
16757 //                       }, 50);
16758 //                       return false;
16759 //                   }
16760 //                   return true;
16761 //                });
16762 //                return;
16763 //            }
16764 //        }
16765         
16766         var days = date.getDaysInMonth();
16767         
16768         var firstOfMonth = date.getFirstDateOfMonth();
16769         var startingPos = firstOfMonth.getDay()-this.startDay;
16770         
16771         if(startingPos < this.startDay){
16772             startingPos += 7;
16773         }
16774         
16775         var pm = date.add(Date.MONTH, -1);
16776         var prevStart = pm.getDaysInMonth()-startingPos;
16777 //        
16778         this.cells = this.el.select('.fc-day',true);
16779         this.textNodes = this.el.query('.fc-day-number');
16780         this.cells.addClassOnOver('fc-state-hover');
16781         
16782         var cells = this.cells.elements;
16783         var textEls = this.textNodes;
16784         
16785         Roo.each(cells, function(cell){
16786             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16787         });
16788         
16789         days += startingPos;
16790
16791         // convert everything to numbers so it's fast
16792         var day = 86400000;
16793         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16794         //Roo.log(d);
16795         //Roo.log(pm);
16796         //Roo.log(prevStart);
16797         
16798         var today = new Date().clearTime().getTime();
16799         var sel = date.clearTime().getTime();
16800         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16801         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16802         var ddMatch = this.disabledDatesRE;
16803         var ddText = this.disabledDatesText;
16804         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16805         var ddaysText = this.disabledDaysText;
16806         var format = this.format;
16807         
16808         var setCellClass = function(cal, cell){
16809             cell.row = 0;
16810             cell.events = [];
16811             cell.more = [];
16812             //Roo.log('set Cell Class');
16813             cell.title = "";
16814             var t = d.getTime();
16815             
16816             //Roo.log(d);
16817             
16818             cell.dateValue = t;
16819             if(t == today){
16820                 cell.className += " fc-today";
16821                 cell.className += " fc-state-highlight";
16822                 cell.title = cal.todayText;
16823             }
16824             if(t == sel){
16825                 // disable highlight in other month..
16826                 //cell.className += " fc-state-highlight";
16827                 
16828             }
16829             // disabling
16830             if(t < min) {
16831                 cell.className = " fc-state-disabled";
16832                 cell.title = cal.minText;
16833                 return;
16834             }
16835             if(t > max) {
16836                 cell.className = " fc-state-disabled";
16837                 cell.title = cal.maxText;
16838                 return;
16839             }
16840             if(ddays){
16841                 if(ddays.indexOf(d.getDay()) != -1){
16842                     cell.title = ddaysText;
16843                     cell.className = " fc-state-disabled";
16844                 }
16845             }
16846             if(ddMatch && format){
16847                 var fvalue = d.dateFormat(format);
16848                 if(ddMatch.test(fvalue)){
16849                     cell.title = ddText.replace("%0", fvalue);
16850                     cell.className = " fc-state-disabled";
16851                 }
16852             }
16853             
16854             if (!cell.initialClassName) {
16855                 cell.initialClassName = cell.dom.className;
16856             }
16857             
16858             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16859         };
16860
16861         var i = 0;
16862         
16863         for(; i < startingPos; i++) {
16864             textEls[i].innerHTML = (++prevStart);
16865             d.setDate(d.getDate()+1);
16866             
16867             cells[i].className = "fc-past fc-other-month";
16868             setCellClass(this, cells[i]);
16869         }
16870         
16871         var intDay = 0;
16872         
16873         for(; i < days; i++){
16874             intDay = i - startingPos + 1;
16875             textEls[i].innerHTML = (intDay);
16876             d.setDate(d.getDate()+1);
16877             
16878             cells[i].className = ''; // "x-date-active";
16879             setCellClass(this, cells[i]);
16880         }
16881         var extraDays = 0;
16882         
16883         for(; i < 42; i++) {
16884             textEls[i].innerHTML = (++extraDays);
16885             d.setDate(d.getDate()+1);
16886             
16887             cells[i].className = "fc-future fc-other-month";
16888             setCellClass(this, cells[i]);
16889         }
16890         
16891         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16892         
16893         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16894         
16895         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16896         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16897         
16898         if(totalRows != 6){
16899             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16900             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16901         }
16902         
16903         this.fireEvent('monthchange', this, date);
16904         
16905         
16906         /*
16907         if(!this.internalRender){
16908             var main = this.el.dom.firstChild;
16909             var w = main.offsetWidth;
16910             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16911             Roo.fly(main).setWidth(w);
16912             this.internalRender = true;
16913             // opera does not respect the auto grow header center column
16914             // then, after it gets a width opera refuses to recalculate
16915             // without a second pass
16916             if(Roo.isOpera && !this.secondPass){
16917                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16918                 this.secondPass = true;
16919                 this.update.defer(10, this, [date]);
16920             }
16921         }
16922         */
16923         
16924     },
16925     
16926     findCell : function(dt) {
16927         dt = dt.clearTime().getTime();
16928         var ret = false;
16929         this.cells.each(function(c){
16930             //Roo.log("check " +c.dateValue + '?=' + dt);
16931             if(c.dateValue == dt){
16932                 ret = c;
16933                 return false;
16934             }
16935             return true;
16936         });
16937         
16938         return ret;
16939     },
16940     
16941     findCells : function(ev) {
16942         var s = ev.start.clone().clearTime().getTime();
16943        // Roo.log(s);
16944         var e= ev.end.clone().clearTime().getTime();
16945        // Roo.log(e);
16946         var ret = [];
16947         this.cells.each(function(c){
16948              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16949             
16950             if(c.dateValue > e){
16951                 return ;
16952             }
16953             if(c.dateValue < s){
16954                 return ;
16955             }
16956             ret.push(c);
16957         });
16958         
16959         return ret;    
16960     },
16961     
16962 //    findBestRow: function(cells)
16963 //    {
16964 //        var ret = 0;
16965 //        
16966 //        for (var i =0 ; i < cells.length;i++) {
16967 //            ret  = Math.max(cells[i].rows || 0,ret);
16968 //        }
16969 //        return ret;
16970 //        
16971 //    },
16972     
16973     
16974     addItem : function(ev)
16975     {
16976         // look for vertical location slot in
16977         var cells = this.findCells(ev);
16978         
16979 //        ev.row = this.findBestRow(cells);
16980         
16981         // work out the location.
16982         
16983         var crow = false;
16984         var rows = [];
16985         for(var i =0; i < cells.length; i++) {
16986             
16987             cells[i].row = cells[0].row;
16988             
16989             if(i == 0){
16990                 cells[i].row = cells[i].row + 1;
16991             }
16992             
16993             if (!crow) {
16994                 crow = {
16995                     start : cells[i],
16996                     end :  cells[i]
16997                 };
16998                 continue;
16999             }
17000             if (crow.start.getY() == cells[i].getY()) {
17001                 // on same row.
17002                 crow.end = cells[i];
17003                 continue;
17004             }
17005             // different row.
17006             rows.push(crow);
17007             crow = {
17008                 start: cells[i],
17009                 end : cells[i]
17010             };
17011             
17012         }
17013         
17014         rows.push(crow);
17015         ev.els = [];
17016         ev.rows = rows;
17017         ev.cells = cells;
17018         
17019         cells[0].events.push(ev);
17020         
17021         this.calevents.push(ev);
17022     },
17023     
17024     clearEvents: function() {
17025         
17026         if(!this.calevents){
17027             return;
17028         }
17029         
17030         Roo.each(this.cells.elements, function(c){
17031             c.row = 0;
17032             c.events = [];
17033             c.more = [];
17034         });
17035         
17036         Roo.each(this.calevents, function(e) {
17037             Roo.each(e.els, function(el) {
17038                 el.un('mouseenter' ,this.onEventEnter, this);
17039                 el.un('mouseleave' ,this.onEventLeave, this);
17040                 el.remove();
17041             },this);
17042         },this);
17043         
17044         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17045             e.remove();
17046         });
17047         
17048     },
17049     
17050     renderEvents: function()
17051     {   
17052         var _this = this;
17053         
17054         this.cells.each(function(c) {
17055             
17056             if(c.row < 5){
17057                 return;
17058             }
17059             
17060             var ev = c.events;
17061             
17062             var r = 4;
17063             if(c.row != c.events.length){
17064                 r = 4 - (4 - (c.row - c.events.length));
17065             }
17066             
17067             c.events = ev.slice(0, r);
17068             c.more = ev.slice(r);
17069             
17070             if(c.more.length && c.more.length == 1){
17071                 c.events.push(c.more.pop());
17072             }
17073             
17074             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17075             
17076         });
17077             
17078         this.cells.each(function(c) {
17079             
17080             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17081             
17082             
17083             for (var e = 0; e < c.events.length; e++){
17084                 var ev = c.events[e];
17085                 var rows = ev.rows;
17086                 
17087                 for(var i = 0; i < rows.length; i++) {
17088                 
17089                     // how many rows should it span..
17090
17091                     var  cfg = {
17092                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17093                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17094
17095                         unselectable : "on",
17096                         cn : [
17097                             {
17098                                 cls: 'fc-event-inner',
17099                                 cn : [
17100     //                                {
17101     //                                  tag:'span',
17102     //                                  cls: 'fc-event-time',
17103     //                                  html : cells.length > 1 ? '' : ev.time
17104     //                                },
17105                                     {
17106                                       tag:'span',
17107                                       cls: 'fc-event-title',
17108                                       html : String.format('{0}', ev.title)
17109                                     }
17110
17111
17112                                 ]
17113                             },
17114                             {
17115                                 cls: 'ui-resizable-handle ui-resizable-e',
17116                                 html : '&nbsp;&nbsp;&nbsp'
17117                             }
17118
17119                         ]
17120                     };
17121
17122                     if (i == 0) {
17123                         cfg.cls += ' fc-event-start';
17124                     }
17125                     if ((i+1) == rows.length) {
17126                         cfg.cls += ' fc-event-end';
17127                     }
17128
17129                     var ctr = _this.el.select('.fc-event-container',true).first();
17130                     var cg = ctr.createChild(cfg);
17131
17132                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17133                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17134
17135                     var r = (c.more.length) ? 1 : 0;
17136                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17137                     cg.setWidth(ebox.right - sbox.x -2);
17138
17139                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17140                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17141                     cg.on('click', _this.onEventClick, _this, ev);
17142
17143                     ev.els.push(cg);
17144                     
17145                 }
17146                 
17147             }
17148             
17149             
17150             if(c.more.length){
17151                 var  cfg = {
17152                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17153                     style : 'position: absolute',
17154                     unselectable : "on",
17155                     cn : [
17156                         {
17157                             cls: 'fc-event-inner',
17158                             cn : [
17159                                 {
17160                                   tag:'span',
17161                                   cls: 'fc-event-title',
17162                                   html : 'More'
17163                                 }
17164
17165
17166                             ]
17167                         },
17168                         {
17169                             cls: 'ui-resizable-handle ui-resizable-e',
17170                             html : '&nbsp;&nbsp;&nbsp'
17171                         }
17172
17173                     ]
17174                 };
17175
17176                 var ctr = _this.el.select('.fc-event-container',true).first();
17177                 var cg = ctr.createChild(cfg);
17178
17179                 var sbox = c.select('.fc-day-content',true).first().getBox();
17180                 var ebox = c.select('.fc-day-content',true).first().getBox();
17181                 //Roo.log(cg);
17182                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17183                 cg.setWidth(ebox.right - sbox.x -2);
17184
17185                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17186                 
17187             }
17188             
17189         });
17190         
17191         
17192         
17193     },
17194     
17195     onEventEnter: function (e, el,event,d) {
17196         this.fireEvent('evententer', this, el, event);
17197     },
17198     
17199     onEventLeave: function (e, el,event,d) {
17200         this.fireEvent('eventleave', this, el, event);
17201     },
17202     
17203     onEventClick: function (e, el,event,d) {
17204         this.fireEvent('eventclick', this, el, event);
17205     },
17206     
17207     onMonthChange: function () {
17208         this.store.load();
17209     },
17210     
17211     onMoreEventClick: function(e, el, more)
17212     {
17213         var _this = this;
17214         
17215         this.calpopover.placement = 'right';
17216         this.calpopover.setTitle('More');
17217         
17218         this.calpopover.setContent('');
17219         
17220         var ctr = this.calpopover.el.select('.popover-content', true).first();
17221         
17222         Roo.each(more, function(m){
17223             var cfg = {
17224                 cls : 'fc-event-hori fc-event-draggable',
17225                 html : m.title
17226             };
17227             var cg = ctr.createChild(cfg);
17228             
17229             cg.on('click', _this.onEventClick, _this, m);
17230         });
17231         
17232         this.calpopover.show(el);
17233         
17234         
17235     },
17236     
17237     onLoad: function () 
17238     {   
17239         this.calevents = [];
17240         var cal = this;
17241         
17242         if(this.store.getCount() > 0){
17243             this.store.data.each(function(d){
17244                cal.addItem({
17245                     id : d.data.id,
17246                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17247                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17248                     time : d.data.start_time,
17249                     title : d.data.title,
17250                     description : d.data.description,
17251                     venue : d.data.venue
17252                 });
17253             });
17254         }
17255         
17256         this.renderEvents();
17257         
17258         if(this.calevents.length && this.loadMask){
17259             this.maskEl.hide();
17260         }
17261     },
17262     
17263     onBeforeLoad: function()
17264     {
17265         this.clearEvents();
17266         if(this.loadMask){
17267             this.maskEl.show();
17268         }
17269     }
17270 });
17271
17272  
17273  /*
17274  * - LGPL
17275  *
17276  * element
17277  * 
17278  */
17279
17280 /**
17281  * @class Roo.bootstrap.Popover
17282  * @extends Roo.bootstrap.Component
17283  * Bootstrap Popover class
17284  * @cfg {String} html contents of the popover   (or false to use children..)
17285  * @cfg {String} title of popover (or false to hide)
17286  * @cfg {String} placement how it is placed
17287  * @cfg {String} trigger click || hover (or false to trigger manually)
17288  * @cfg {String} over what (parent or false to trigger manually.)
17289  * @cfg {Number} delay - delay before showing
17290  
17291  * @constructor
17292  * Create a new Popover
17293  * @param {Object} config The config object
17294  */
17295
17296 Roo.bootstrap.Popover = function(config){
17297     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17298     
17299     this.addEvents({
17300         // raw events
17301          /**
17302          * @event show
17303          * After the popover show
17304          * 
17305          * @param {Roo.bootstrap.Popover} this
17306          */
17307         "show" : true,
17308         /**
17309          * @event hide
17310          * After the popover hide
17311          * 
17312          * @param {Roo.bootstrap.Popover} this
17313          */
17314         "hide" : true
17315     });
17316 };
17317
17318 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17319     
17320     title: 'Fill in a title',
17321     html: false,
17322     
17323     placement : 'right',
17324     trigger : 'hover', // hover
17325     
17326     delay : 0,
17327     
17328     over: 'parent',
17329     
17330     can_build_overlaid : false,
17331     
17332     getChildContainer : function()
17333     {
17334         return this.el.select('.popover-content',true).first();
17335     },
17336     
17337     getAutoCreate : function(){
17338          
17339         var cfg = {
17340            cls : 'popover roo-dynamic',
17341            style: 'display:block',
17342            cn : [
17343                 {
17344                     cls : 'arrow'
17345                 },
17346                 {
17347                     cls : 'popover-inner',
17348                     cn : [
17349                         {
17350                             tag: 'h3',
17351                             cls: 'popover-title',
17352                             html : this.title
17353                         },
17354                         {
17355                             cls : 'popover-content',
17356                             html : this.html
17357                         }
17358                     ]
17359                     
17360                 }
17361            ]
17362         };
17363         
17364         return cfg;
17365     },
17366     setTitle: function(str)
17367     {
17368         this.title = str;
17369         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17370     },
17371     setContent: function(str)
17372     {
17373         this.html = str;
17374         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17375     },
17376     // as it get's added to the bottom of the page.
17377     onRender : function(ct, position)
17378     {
17379         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17380         if(!this.el){
17381             var cfg = Roo.apply({},  this.getAutoCreate());
17382             cfg.id = Roo.id();
17383             
17384             if (this.cls) {
17385                 cfg.cls += ' ' + this.cls;
17386             }
17387             if (this.style) {
17388                 cfg.style = this.style;
17389             }
17390             //Roo.log("adding to ");
17391             this.el = Roo.get(document.body).createChild(cfg, position);
17392 //            Roo.log(this.el);
17393         }
17394         this.initEvents();
17395     },
17396     
17397     initEvents : function()
17398     {
17399         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17400         this.el.enableDisplayMode('block');
17401         this.el.hide();
17402         if (this.over === false) {
17403             return; 
17404         }
17405         if (this.triggers === false) {
17406             return;
17407         }
17408         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17409         var triggers = this.trigger ? this.trigger.split(' ') : [];
17410         Roo.each(triggers, function(trigger) {
17411         
17412             if (trigger == 'click') {
17413                 on_el.on('click', this.toggle, this);
17414             } else if (trigger != 'manual') {
17415                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17416                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17417       
17418                 on_el.on(eventIn  ,this.enter, this);
17419                 on_el.on(eventOut, this.leave, this);
17420             }
17421         }, this);
17422         
17423     },
17424     
17425     
17426     // private
17427     timeout : null,
17428     hoverState : null,
17429     
17430     toggle : function () {
17431         this.hoverState == 'in' ? this.leave() : this.enter();
17432     },
17433     
17434     enter : function () {
17435         
17436         clearTimeout(this.timeout);
17437     
17438         this.hoverState = 'in';
17439     
17440         if (!this.delay || !this.delay.show) {
17441             this.show();
17442             return;
17443         }
17444         var _t = this;
17445         this.timeout = setTimeout(function () {
17446             if (_t.hoverState == 'in') {
17447                 _t.show();
17448             }
17449         }, this.delay.show)
17450     },
17451     
17452     leave : function() {
17453         clearTimeout(this.timeout);
17454     
17455         this.hoverState = 'out';
17456     
17457         if (!this.delay || !this.delay.hide) {
17458             this.hide();
17459             return;
17460         }
17461         var _t = this;
17462         this.timeout = setTimeout(function () {
17463             if (_t.hoverState == 'out') {
17464                 _t.hide();
17465             }
17466         }, this.delay.hide)
17467     },
17468     
17469     show : function (on_el)
17470     {
17471         if (!on_el) {
17472             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17473         }
17474         
17475         // set content.
17476         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17477         if (this.html !== false) {
17478             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17479         }
17480         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17481         if (!this.title.length) {
17482             this.el.select('.popover-title',true).hide();
17483         }
17484         
17485         var placement = typeof this.placement == 'function' ?
17486             this.placement.call(this, this.el, on_el) :
17487             this.placement;
17488             
17489         var autoToken = /\s?auto?\s?/i;
17490         var autoPlace = autoToken.test(placement);
17491         if (autoPlace) {
17492             placement = placement.replace(autoToken, '') || 'top';
17493         }
17494         
17495         //this.el.detach()
17496         //this.el.setXY([0,0]);
17497         this.el.show();
17498         this.el.dom.style.display='block';
17499         this.el.addClass(placement);
17500         
17501         //this.el.appendTo(on_el);
17502         
17503         var p = this.getPosition();
17504         var box = this.el.getBox();
17505         
17506         if (autoPlace) {
17507             // fixme..
17508         }
17509         var align = Roo.bootstrap.Popover.alignment[placement];
17510         
17511 //        Roo.log(align);
17512         this.el.alignTo(on_el, align[0],align[1]);
17513         //var arrow = this.el.select('.arrow',true).first();
17514         //arrow.set(align[2], 
17515         
17516         this.el.addClass('in');
17517         
17518         
17519         if (this.el.hasClass('fade')) {
17520             // fade it?
17521         }
17522         
17523         this.hoverState = 'in';
17524         
17525         this.fireEvent('show', this);
17526         
17527     },
17528     hide : function()
17529     {
17530         this.el.setXY([0,0]);
17531         this.el.removeClass('in');
17532         this.el.hide();
17533         this.hoverState = null;
17534         
17535         this.fireEvent('hide', this);
17536     }
17537     
17538 });
17539
17540 Roo.bootstrap.Popover.alignment = {
17541     'left' : ['r-l', [-10,0], 'right'],
17542     'right' : ['l-r', [10,0], 'left'],
17543     'bottom' : ['t-b', [0,10], 'top'],
17544     'top' : [ 'b-t', [0,-10], 'bottom']
17545 };
17546
17547  /*
17548  * - LGPL
17549  *
17550  * Progress
17551  * 
17552  */
17553
17554 /**
17555  * @class Roo.bootstrap.Progress
17556  * @extends Roo.bootstrap.Component
17557  * Bootstrap Progress class
17558  * @cfg {Boolean} striped striped of the progress bar
17559  * @cfg {Boolean} active animated of the progress bar
17560  * 
17561  * 
17562  * @constructor
17563  * Create a new Progress
17564  * @param {Object} config The config object
17565  */
17566
17567 Roo.bootstrap.Progress = function(config){
17568     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17569 };
17570
17571 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17572     
17573     striped : false,
17574     active: false,
17575     
17576     getAutoCreate : function(){
17577         var cfg = {
17578             tag: 'div',
17579             cls: 'progress'
17580         };
17581         
17582         
17583         if(this.striped){
17584             cfg.cls += ' progress-striped';
17585         }
17586       
17587         if(this.active){
17588             cfg.cls += ' active';
17589         }
17590         
17591         
17592         return cfg;
17593     }
17594    
17595 });
17596
17597  
17598
17599  /*
17600  * - LGPL
17601  *
17602  * ProgressBar
17603  * 
17604  */
17605
17606 /**
17607  * @class Roo.bootstrap.ProgressBar
17608  * @extends Roo.bootstrap.Component
17609  * Bootstrap ProgressBar class
17610  * @cfg {Number} aria_valuenow aria-value now
17611  * @cfg {Number} aria_valuemin aria-value min
17612  * @cfg {Number} aria_valuemax aria-value max
17613  * @cfg {String} label label for the progress bar
17614  * @cfg {String} panel (success | info | warning | danger )
17615  * @cfg {String} role role of the progress bar
17616  * @cfg {String} sr_only text
17617  * 
17618  * 
17619  * @constructor
17620  * Create a new ProgressBar
17621  * @param {Object} config The config object
17622  */
17623
17624 Roo.bootstrap.ProgressBar = function(config){
17625     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17626 };
17627
17628 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17629     
17630     aria_valuenow : 0,
17631     aria_valuemin : 0,
17632     aria_valuemax : 100,
17633     label : false,
17634     panel : false,
17635     role : false,
17636     sr_only: false,
17637     
17638     getAutoCreate : function()
17639     {
17640         
17641         var cfg = {
17642             tag: 'div',
17643             cls: 'progress-bar',
17644             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17645         };
17646         
17647         if(this.sr_only){
17648             cfg.cn = {
17649                 tag: 'span',
17650                 cls: 'sr-only',
17651                 html: this.sr_only
17652             }
17653         }
17654         
17655         if(this.role){
17656             cfg.role = this.role;
17657         }
17658         
17659         if(this.aria_valuenow){
17660             cfg['aria-valuenow'] = this.aria_valuenow;
17661         }
17662         
17663         if(this.aria_valuemin){
17664             cfg['aria-valuemin'] = this.aria_valuemin;
17665         }
17666         
17667         if(this.aria_valuemax){
17668             cfg['aria-valuemax'] = this.aria_valuemax;
17669         }
17670         
17671         if(this.label && !this.sr_only){
17672             cfg.html = this.label;
17673         }
17674         
17675         if(this.panel){
17676             cfg.cls += ' progress-bar-' + this.panel;
17677         }
17678         
17679         return cfg;
17680     },
17681     
17682     update : function(aria_valuenow)
17683     {
17684         this.aria_valuenow = aria_valuenow;
17685         
17686         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17687     }
17688    
17689 });
17690
17691  
17692
17693  /*
17694  * - LGPL
17695  *
17696  * column
17697  * 
17698  */
17699
17700 /**
17701  * @class Roo.bootstrap.TabGroup
17702  * @extends Roo.bootstrap.Column
17703  * Bootstrap Column class
17704  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17705  * @cfg {Boolean} carousel true to make the group behave like a carousel
17706  * @cfg {Boolean} bullets show bullets for the panels
17707  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17708  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17709  * @cfg {Boolean} showarrow (true|false) show arrow default true
17710  * 
17711  * @constructor
17712  * Create a new TabGroup
17713  * @param {Object} config The config object
17714  */
17715
17716 Roo.bootstrap.TabGroup = function(config){
17717     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17718     if (!this.navId) {
17719         this.navId = Roo.id();
17720     }
17721     this.tabs = [];
17722     Roo.bootstrap.TabGroup.register(this);
17723     
17724 };
17725
17726 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17727     
17728     carousel : false,
17729     transition : false,
17730     bullets : 0,
17731     timer : 0,
17732     autoslide : false,
17733     slideFn : false,
17734     slideOnTouch : false,
17735     showarrow : true,
17736     
17737     getAutoCreate : function()
17738     {
17739         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17740         
17741         cfg.cls += ' tab-content';
17742         
17743         if (this.carousel) {
17744             cfg.cls += ' carousel slide';
17745             
17746             cfg.cn = [{
17747                cls : 'carousel-inner',
17748                cn : []
17749             }];
17750         
17751             if(this.bullets  && !Roo.isTouch){
17752                 
17753                 var bullets = {
17754                     cls : 'carousel-bullets',
17755                     cn : []
17756                 };
17757                
17758                 if(this.bullets_cls){
17759                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17760                 }
17761                 
17762                 bullets.cn.push({
17763                     cls : 'clear'
17764                 });
17765                 
17766                 cfg.cn[0].cn.push(bullets);
17767             }
17768             
17769             if(this.showarrow){
17770                 cfg.cn[0].cn.push({
17771                     tag : 'div',
17772                     class : 'carousel-arrow',
17773                     cn : [
17774                         {
17775                             tag : 'div',
17776                             class : 'carousel-prev',
17777                             cn : [
17778                                 {
17779                                     tag : 'i',
17780                                     class : 'fa fa-chevron-left'
17781                                 }
17782                             ]
17783                         },
17784                         {
17785                             tag : 'div',
17786                             class : 'carousel-next',
17787                             cn : [
17788                                 {
17789                                     tag : 'i',
17790                                     class : 'fa fa-chevron-right'
17791                                 }
17792                             ]
17793                         }
17794                     ]
17795                 });
17796             }
17797             
17798         }
17799         
17800         return cfg;
17801     },
17802     
17803     initEvents:  function()
17804     {
17805 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17806 //            this.el.on("touchstart", this.onTouchStart, this);
17807 //        }
17808         
17809         if(this.autoslide){
17810             var _this = this;
17811             
17812             this.slideFn = window.setInterval(function() {
17813                 _this.showPanelNext();
17814             }, this.timer);
17815         }
17816         
17817         if(this.showarrow){
17818             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17819             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17820         }
17821         
17822         
17823     },
17824     
17825 //    onTouchStart : function(e, el, o)
17826 //    {
17827 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17828 //            return;
17829 //        }
17830 //        
17831 //        this.showPanelNext();
17832 //    },
17833     
17834     
17835     getChildContainer : function()
17836     {
17837         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17838     },
17839     
17840     /**
17841     * register a Navigation item
17842     * @param {Roo.bootstrap.NavItem} the navitem to add
17843     */
17844     register : function(item)
17845     {
17846         this.tabs.push( item);
17847         item.navId = this.navId; // not really needed..
17848         this.addBullet();
17849     
17850     },
17851     
17852     getActivePanel : function()
17853     {
17854         var r = false;
17855         Roo.each(this.tabs, function(t) {
17856             if (t.active) {
17857                 r = t;
17858                 return false;
17859             }
17860             return null;
17861         });
17862         return r;
17863         
17864     },
17865     getPanelByName : function(n)
17866     {
17867         var r = false;
17868         Roo.each(this.tabs, function(t) {
17869             if (t.tabId == n) {
17870                 r = t;
17871                 return false;
17872             }
17873             return null;
17874         });
17875         return r;
17876     },
17877     indexOfPanel : function(p)
17878     {
17879         var r = false;
17880         Roo.each(this.tabs, function(t,i) {
17881             if (t.tabId == p.tabId) {
17882                 r = i;
17883                 return false;
17884             }
17885             return null;
17886         });
17887         return r;
17888     },
17889     /**
17890      * show a specific panel
17891      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17892      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17893      */
17894     showPanel : function (pan)
17895     {
17896         if(this.transition || typeof(pan) == 'undefined'){
17897             Roo.log("waiting for the transitionend");
17898             return;
17899         }
17900         
17901         if (typeof(pan) == 'number') {
17902             pan = this.tabs[pan];
17903         }
17904         
17905         if (typeof(pan) == 'string') {
17906             pan = this.getPanelByName(pan);
17907         }
17908         
17909         var cur = this.getActivePanel();
17910         
17911         if(!pan || !cur){
17912             Roo.log('pan or acitve pan is undefined');
17913             return false;
17914         }
17915         
17916         if (pan.tabId == this.getActivePanel().tabId) {
17917             return true;
17918         }
17919         
17920         if (false === cur.fireEvent('beforedeactivate')) {
17921             return false;
17922         }
17923         
17924         if(this.bullets > 0 && !Roo.isTouch){
17925             this.setActiveBullet(this.indexOfPanel(pan));
17926         }
17927         
17928         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17929             
17930             this.transition = true;
17931             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17932             var lr = dir == 'next' ? 'left' : 'right';
17933             pan.el.addClass(dir); // or prev
17934             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17935             cur.el.addClass(lr); // or right
17936             pan.el.addClass(lr);
17937             
17938             var _this = this;
17939             cur.el.on('transitionend', function() {
17940                 Roo.log("trans end?");
17941                 
17942                 pan.el.removeClass([lr,dir]);
17943                 pan.setActive(true);
17944                 
17945                 cur.el.removeClass([lr]);
17946                 cur.setActive(false);
17947                 
17948                 _this.transition = false;
17949                 
17950             }, this, { single:  true } );
17951             
17952             return true;
17953         }
17954         
17955         cur.setActive(false);
17956         pan.setActive(true);
17957         
17958         return true;
17959         
17960     },
17961     showPanelNext : function()
17962     {
17963         var i = this.indexOfPanel(this.getActivePanel());
17964         
17965         if (i >= this.tabs.length - 1 && !this.autoslide) {
17966             return;
17967         }
17968         
17969         if (i >= this.tabs.length - 1 && this.autoslide) {
17970             i = -1;
17971         }
17972         
17973         this.showPanel(this.tabs[i+1]);
17974     },
17975     
17976     showPanelPrev : function()
17977     {
17978         var i = this.indexOfPanel(this.getActivePanel());
17979         
17980         if (i  < 1 && !this.autoslide) {
17981             return;
17982         }
17983         
17984         if (i < 1 && this.autoslide) {
17985             i = this.tabs.length;
17986         }
17987         
17988         this.showPanel(this.tabs[i-1]);
17989     },
17990     
17991     
17992     addBullet: function()
17993     {
17994         if(!this.bullets || Roo.isTouch){
17995             return;
17996         }
17997         var ctr = this.el.select('.carousel-bullets',true).first();
17998         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17999         var bullet = ctr.createChild({
18000             cls : 'bullet bullet-' + i
18001         },ctr.dom.lastChild);
18002         
18003         
18004         var _this = this;
18005         
18006         bullet.on('click', (function(e, el, o, ii, t){
18007
18008             e.preventDefault();
18009
18010             this.showPanel(ii);
18011
18012             if(this.autoslide && this.slideFn){
18013                 clearInterval(this.slideFn);
18014                 this.slideFn = window.setInterval(function() {
18015                     _this.showPanelNext();
18016                 }, this.timer);
18017             }
18018
18019         }).createDelegate(this, [i, bullet], true));
18020                 
18021         
18022     },
18023      
18024     setActiveBullet : function(i)
18025     {
18026         if(Roo.isTouch){
18027             return;
18028         }
18029         
18030         Roo.each(this.el.select('.bullet', true).elements, function(el){
18031             el.removeClass('selected');
18032         });
18033
18034         var bullet = this.el.select('.bullet-' + i, true).first();
18035         
18036         if(!bullet){
18037             return;
18038         }
18039         
18040         bullet.addClass('selected');
18041     }
18042     
18043     
18044   
18045 });
18046
18047  
18048
18049  
18050  
18051 Roo.apply(Roo.bootstrap.TabGroup, {
18052     
18053     groups: {},
18054      /**
18055     * register a Navigation Group
18056     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18057     */
18058     register : function(navgrp)
18059     {
18060         this.groups[navgrp.navId] = navgrp;
18061         
18062     },
18063     /**
18064     * fetch a Navigation Group based on the navigation ID
18065     * if one does not exist , it will get created.
18066     * @param {string} the navgroup to add
18067     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18068     */
18069     get: function(navId) {
18070         if (typeof(this.groups[navId]) == 'undefined') {
18071             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18072         }
18073         return this.groups[navId] ;
18074     }
18075     
18076     
18077     
18078 });
18079
18080  /*
18081  * - LGPL
18082  *
18083  * TabPanel
18084  * 
18085  */
18086
18087 /**
18088  * @class Roo.bootstrap.TabPanel
18089  * @extends Roo.bootstrap.Component
18090  * Bootstrap TabPanel class
18091  * @cfg {Boolean} active panel active
18092  * @cfg {String} html panel content
18093  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18094  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18095  * @cfg {String} href click to link..
18096  * 
18097  * 
18098  * @constructor
18099  * Create a new TabPanel
18100  * @param {Object} config The config object
18101  */
18102
18103 Roo.bootstrap.TabPanel = function(config){
18104     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18105     this.addEvents({
18106         /**
18107              * @event changed
18108              * Fires when the active status changes
18109              * @param {Roo.bootstrap.TabPanel} this
18110              * @param {Boolean} state the new state
18111             
18112          */
18113         'changed': true,
18114         /**
18115              * @event beforedeactivate
18116              * Fires before a tab is de-activated - can be used to do validation on a form.
18117              * @param {Roo.bootstrap.TabPanel} this
18118              * @return {Boolean} false if there is an error
18119             
18120          */
18121         'beforedeactivate': true
18122      });
18123     
18124     this.tabId = this.tabId || Roo.id();
18125   
18126 };
18127
18128 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18129     
18130     active: false,
18131     html: false,
18132     tabId: false,
18133     navId : false,
18134     href : '',
18135     
18136     getAutoCreate : function(){
18137         var cfg = {
18138             tag: 'div',
18139             // item is needed for carousel - not sure if it has any effect otherwise
18140             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18141             html: this.html || ''
18142         };
18143         
18144         if(this.active){
18145             cfg.cls += ' active';
18146         }
18147         
18148         if(this.tabId){
18149             cfg.tabId = this.tabId;
18150         }
18151         
18152         
18153         return cfg;
18154     },
18155     
18156     initEvents:  function()
18157     {
18158         var p = this.parent();
18159         
18160         this.navId = this.navId || p.navId;
18161         
18162         if (typeof(this.navId) != 'undefined') {
18163             // not really needed.. but just in case.. parent should be a NavGroup.
18164             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18165             
18166             tg.register(this);
18167             
18168             var i = tg.tabs.length - 1;
18169             
18170             if(this.active && tg.bullets > 0 && i < tg.bullets){
18171                 tg.setActiveBullet(i);
18172             }
18173         }
18174         
18175         this.el.on('click', this.onClick, this);
18176         
18177         if(Roo.isTouch){
18178             this.el.on("touchstart", this.onTouchStart, this);
18179             this.el.on("touchmove", this.onTouchMove, this);
18180             this.el.on("touchend", this.onTouchEnd, this);
18181         }
18182         
18183     },
18184     
18185     onRender : function(ct, position)
18186     {
18187         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18188     },
18189     
18190     setActive : function(state)
18191     {
18192         Roo.log("panel - set active " + this.tabId + "=" + state);
18193         
18194         this.active = state;
18195         if (!state) {
18196             this.el.removeClass('active');
18197             
18198         } else  if (!this.el.hasClass('active')) {
18199             this.el.addClass('active');
18200         }
18201         
18202         this.fireEvent('changed', this, state);
18203     },
18204     
18205     onClick : function(e)
18206     {
18207         e.preventDefault();
18208         
18209         if(!this.href.length){
18210             return;
18211         }
18212         
18213         window.location.href = this.href;
18214     },
18215     
18216     startX : 0,
18217     startY : 0,
18218     endX : 0,
18219     endY : 0,
18220     swiping : false,
18221     
18222     onTouchStart : function(e)
18223     {
18224         this.swiping = false;
18225         
18226         this.startX = e.browserEvent.touches[0].clientX;
18227         this.startY = e.browserEvent.touches[0].clientY;
18228     },
18229     
18230     onTouchMove : function(e)
18231     {
18232         this.swiping = true;
18233         
18234         this.endX = e.browserEvent.touches[0].clientX;
18235         this.endY = e.browserEvent.touches[0].clientY;
18236     },
18237     
18238     onTouchEnd : function(e)
18239     {
18240         if(!this.swiping){
18241             this.onClick(e);
18242             return;
18243         }
18244         
18245         var tabGroup = this.parent();
18246         
18247         if(this.endX > this.startX){ // swiping right
18248             tabGroup.showPanelPrev();
18249             return;
18250         }
18251         
18252         if(this.startX > this.endX){ // swiping left
18253             tabGroup.showPanelNext();
18254             return;
18255         }
18256     }
18257     
18258     
18259 });
18260  
18261
18262  
18263
18264  /*
18265  * - LGPL
18266  *
18267  * DateField
18268  * 
18269  */
18270
18271 /**
18272  * @class Roo.bootstrap.DateField
18273  * @extends Roo.bootstrap.Input
18274  * Bootstrap DateField class
18275  * @cfg {Number} weekStart default 0
18276  * @cfg {String} viewMode default empty, (months|years)
18277  * @cfg {String} minViewMode default empty, (months|years)
18278  * @cfg {Number} startDate default -Infinity
18279  * @cfg {Number} endDate default Infinity
18280  * @cfg {Boolean} todayHighlight default false
18281  * @cfg {Boolean} todayBtn default false
18282  * @cfg {Boolean} calendarWeeks default false
18283  * @cfg {Object} daysOfWeekDisabled default empty
18284  * @cfg {Boolean} singleMode default false (true | false)
18285  * 
18286  * @cfg {Boolean} keyboardNavigation default true
18287  * @cfg {String} language default en
18288  * 
18289  * @constructor
18290  * Create a new DateField
18291  * @param {Object} config The config object
18292  */
18293
18294 Roo.bootstrap.DateField = function(config){
18295     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18296      this.addEvents({
18297             /**
18298              * @event show
18299              * Fires when this field show.
18300              * @param {Roo.bootstrap.DateField} this
18301              * @param {Mixed} date The date value
18302              */
18303             show : true,
18304             /**
18305              * @event show
18306              * Fires when this field hide.
18307              * @param {Roo.bootstrap.DateField} this
18308              * @param {Mixed} date The date value
18309              */
18310             hide : true,
18311             /**
18312              * @event select
18313              * Fires when select a date.
18314              * @param {Roo.bootstrap.DateField} this
18315              * @param {Mixed} date The date value
18316              */
18317             select : true,
18318             /**
18319              * @event beforeselect
18320              * Fires when before select a date.
18321              * @param {Roo.bootstrap.DateField} this
18322              * @param {Mixed} date The date value
18323              */
18324             beforeselect : true
18325         });
18326 };
18327
18328 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18329     
18330     /**
18331      * @cfg {String} format
18332      * The default date format string which can be overriden for localization support.  The format must be
18333      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18334      */
18335     format : "m/d/y",
18336     /**
18337      * @cfg {String} altFormats
18338      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18339      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18340      */
18341     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18342     
18343     weekStart : 0,
18344     
18345     viewMode : '',
18346     
18347     minViewMode : '',
18348     
18349     todayHighlight : false,
18350     
18351     todayBtn: false,
18352     
18353     language: 'en',
18354     
18355     keyboardNavigation: true,
18356     
18357     calendarWeeks: false,
18358     
18359     startDate: -Infinity,
18360     
18361     endDate: Infinity,
18362     
18363     daysOfWeekDisabled: [],
18364     
18365     _events: [],
18366     
18367     singleMode : false,
18368     
18369     UTCDate: function()
18370     {
18371         return new Date(Date.UTC.apply(Date, arguments));
18372     },
18373     
18374     UTCToday: function()
18375     {
18376         var today = new Date();
18377         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18378     },
18379     
18380     getDate: function() {
18381             var d = this.getUTCDate();
18382             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18383     },
18384     
18385     getUTCDate: function() {
18386             return this.date;
18387     },
18388     
18389     setDate: function(d) {
18390             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18391     },
18392     
18393     setUTCDate: function(d) {
18394             this.date = d;
18395             this.setValue(this.formatDate(this.date));
18396     },
18397         
18398     onRender: function(ct, position)
18399     {
18400         
18401         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18402         
18403         this.language = this.language || 'en';
18404         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18405         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18406         
18407         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18408         this.format = this.format || 'm/d/y';
18409         this.isInline = false;
18410         this.isInput = true;
18411         this.component = this.el.select('.add-on', true).first() || false;
18412         this.component = (this.component && this.component.length === 0) ? false : this.component;
18413         this.hasInput = this.component && this.inputEl().length;
18414         
18415         if (typeof(this.minViewMode === 'string')) {
18416             switch (this.minViewMode) {
18417                 case 'months':
18418                     this.minViewMode = 1;
18419                     break;
18420                 case 'years':
18421                     this.minViewMode = 2;
18422                     break;
18423                 default:
18424                     this.minViewMode = 0;
18425                     break;
18426             }
18427         }
18428         
18429         if (typeof(this.viewMode === 'string')) {
18430             switch (this.viewMode) {
18431                 case 'months':
18432                     this.viewMode = 1;
18433                     break;
18434                 case 'years':
18435                     this.viewMode = 2;
18436                     break;
18437                 default:
18438                     this.viewMode = 0;
18439                     break;
18440             }
18441         }
18442                 
18443         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18444         
18445 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18446         
18447         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18448         
18449         this.picker().on('mousedown', this.onMousedown, this);
18450         this.picker().on('click', this.onClick, this);
18451         
18452         this.picker().addClass('datepicker-dropdown');
18453         
18454         this.startViewMode = this.viewMode;
18455         
18456         if(this.singleMode){
18457             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18458                 v.setVisibilityMode(Roo.Element.DISPLAY);
18459                 v.hide();
18460             });
18461             
18462             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18463                 v.setStyle('width', '189px');
18464             });
18465         }
18466         
18467         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18468             if(!this.calendarWeeks){
18469                 v.remove();
18470                 return;
18471             }
18472             
18473             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18474             v.attr('colspan', function(i, val){
18475                 return parseInt(val) + 1;
18476             });
18477         });
18478                         
18479         
18480         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18481         
18482         this.setStartDate(this.startDate);
18483         this.setEndDate(this.endDate);
18484         
18485         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18486         
18487         this.fillDow();
18488         this.fillMonths();
18489         this.update();
18490         this.showMode();
18491         
18492         if(this.isInline) {
18493             this.show();
18494         }
18495     },
18496     
18497     picker : function()
18498     {
18499         return this.pickerEl;
18500 //        return this.el.select('.datepicker', true).first();
18501     },
18502     
18503     fillDow: function()
18504     {
18505         var dowCnt = this.weekStart;
18506         
18507         var dow = {
18508             tag: 'tr',
18509             cn: [
18510                 
18511             ]
18512         };
18513         
18514         if(this.calendarWeeks){
18515             dow.cn.push({
18516                 tag: 'th',
18517                 cls: 'cw',
18518                 html: '&nbsp;'
18519             })
18520         }
18521         
18522         while (dowCnt < this.weekStart + 7) {
18523             dow.cn.push({
18524                 tag: 'th',
18525                 cls: 'dow',
18526                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18527             });
18528         }
18529         
18530         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18531     },
18532     
18533     fillMonths: function()
18534     {    
18535         var i = 0;
18536         var months = this.picker().select('>.datepicker-months td', true).first();
18537         
18538         months.dom.innerHTML = '';
18539         
18540         while (i < 12) {
18541             var month = {
18542                 tag: 'span',
18543                 cls: 'month',
18544                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18545             };
18546             
18547             months.createChild(month);
18548         }
18549         
18550     },
18551     
18552     update: function()
18553     {
18554         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;
18555         
18556         if (this.date < this.startDate) {
18557             this.viewDate = new Date(this.startDate);
18558         } else if (this.date > this.endDate) {
18559             this.viewDate = new Date(this.endDate);
18560         } else {
18561             this.viewDate = new Date(this.date);
18562         }
18563         
18564         this.fill();
18565     },
18566     
18567     fill: function() 
18568     {
18569         var d = new Date(this.viewDate),
18570                 year = d.getUTCFullYear(),
18571                 month = d.getUTCMonth(),
18572                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18573                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18574                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18575                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18576                 currentDate = this.date && this.date.valueOf(),
18577                 today = this.UTCToday();
18578         
18579         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18580         
18581 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18582         
18583 //        this.picker.select('>tfoot th.today').
18584 //                                              .text(dates[this.language].today)
18585 //                                              .toggle(this.todayBtn !== false);
18586     
18587         this.updateNavArrows();
18588         this.fillMonths();
18589                                                 
18590         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18591         
18592         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18593          
18594         prevMonth.setUTCDate(day);
18595         
18596         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18597         
18598         var nextMonth = new Date(prevMonth);
18599         
18600         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18601         
18602         nextMonth = nextMonth.valueOf();
18603         
18604         var fillMonths = false;
18605         
18606         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18607         
18608         while(prevMonth.valueOf() < nextMonth) {
18609             var clsName = '';
18610             
18611             if (prevMonth.getUTCDay() === this.weekStart) {
18612                 if(fillMonths){
18613                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18614                 }
18615                     
18616                 fillMonths = {
18617                     tag: 'tr',
18618                     cn: []
18619                 };
18620                 
18621                 if(this.calendarWeeks){
18622                     // ISO 8601: First week contains first thursday.
18623                     // ISO also states week starts on Monday, but we can be more abstract here.
18624                     var
18625                     // Start of current week: based on weekstart/current date
18626                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18627                     // Thursday of this week
18628                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18629                     // First Thursday of year, year from thursday
18630                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18631                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18632                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18633                     
18634                     fillMonths.cn.push({
18635                         tag: 'td',
18636                         cls: 'cw',
18637                         html: calWeek
18638                     });
18639                 }
18640             }
18641             
18642             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18643                 clsName += ' old';
18644             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18645                 clsName += ' new';
18646             }
18647             if (this.todayHighlight &&
18648                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18649                 prevMonth.getUTCMonth() == today.getMonth() &&
18650                 prevMonth.getUTCDate() == today.getDate()) {
18651                 clsName += ' today';
18652             }
18653             
18654             if (currentDate && prevMonth.valueOf() === currentDate) {
18655                 clsName += ' active';
18656             }
18657             
18658             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18659                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18660                     clsName += ' disabled';
18661             }
18662             
18663             fillMonths.cn.push({
18664                 tag: 'td',
18665                 cls: 'day ' + clsName,
18666                 html: prevMonth.getDate()
18667             });
18668             
18669             prevMonth.setDate(prevMonth.getDate()+1);
18670         }
18671           
18672         var currentYear = this.date && this.date.getUTCFullYear();
18673         var currentMonth = this.date && this.date.getUTCMonth();
18674         
18675         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18676         
18677         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18678             v.removeClass('active');
18679             
18680             if(currentYear === year && k === currentMonth){
18681                 v.addClass('active');
18682             }
18683             
18684             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18685                 v.addClass('disabled');
18686             }
18687             
18688         });
18689         
18690         
18691         year = parseInt(year/10, 10) * 10;
18692         
18693         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18694         
18695         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18696         
18697         year -= 1;
18698         for (var i = -1; i < 11; i++) {
18699             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18700                 tag: 'span',
18701                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18702                 html: year
18703             });
18704             
18705             year += 1;
18706         }
18707     },
18708     
18709     showMode: function(dir) 
18710     {
18711         if (dir) {
18712             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18713         }
18714         
18715         Roo.each(this.picker().select('>div',true).elements, function(v){
18716             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18717             v.hide();
18718         });
18719         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18720     },
18721     
18722     place: function()
18723     {
18724         if(this.isInline) {
18725             return;
18726         }
18727         
18728         this.picker().removeClass(['bottom', 'top']);
18729         
18730         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18731             /*
18732              * place to the top of element!
18733              *
18734              */
18735             
18736             this.picker().addClass('top');
18737             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18738             
18739             return;
18740         }
18741         
18742         this.picker().addClass('bottom');
18743         
18744         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18745     },
18746     
18747     parseDate : function(value)
18748     {
18749         if(!value || value instanceof Date){
18750             return value;
18751         }
18752         var v = Date.parseDate(value, this.format);
18753         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18754             v = Date.parseDate(value, 'Y-m-d');
18755         }
18756         if(!v && this.altFormats){
18757             if(!this.altFormatsArray){
18758                 this.altFormatsArray = this.altFormats.split("|");
18759             }
18760             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18761                 v = Date.parseDate(value, this.altFormatsArray[i]);
18762             }
18763         }
18764         return v;
18765     },
18766     
18767     formatDate : function(date, fmt)
18768     {   
18769         return (!date || !(date instanceof Date)) ?
18770         date : date.dateFormat(fmt || this.format);
18771     },
18772     
18773     onFocus : function()
18774     {
18775         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18776         this.show();
18777     },
18778     
18779     onBlur : function()
18780     {
18781         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18782         
18783         var d = this.inputEl().getValue();
18784         
18785         this.setValue(d);
18786                 
18787         this.hide();
18788     },
18789     
18790     show : function()
18791     {
18792         this.picker().show();
18793         this.update();
18794         this.place();
18795         
18796         this.fireEvent('show', this, this.date);
18797     },
18798     
18799     hide : function()
18800     {
18801         if(this.isInline) {
18802             return;
18803         }
18804         this.picker().hide();
18805         this.viewMode = this.startViewMode;
18806         this.showMode();
18807         
18808         this.fireEvent('hide', this, this.date);
18809         
18810     },
18811     
18812     onMousedown: function(e)
18813     {
18814         e.stopPropagation();
18815         e.preventDefault();
18816     },
18817     
18818     keyup: function(e)
18819     {
18820         Roo.bootstrap.DateField.superclass.keyup.call(this);
18821         this.update();
18822     },
18823
18824     setValue: function(v)
18825     {
18826         if(this.fireEvent('beforeselect', this, v) !== false){
18827             var d = new Date(this.parseDate(v) ).clearTime();
18828         
18829             if(isNaN(d.getTime())){
18830                 this.date = this.viewDate = '';
18831                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18832                 return;
18833             }
18834
18835             v = this.formatDate(d);
18836
18837             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18838
18839             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18840
18841             this.update();
18842
18843             this.fireEvent('select', this, this.date);
18844         }
18845     },
18846     
18847     getValue: function()
18848     {
18849         return this.formatDate(this.date);
18850     },
18851     
18852     fireKey: function(e)
18853     {
18854         if (!this.picker().isVisible()){
18855             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18856                 this.show();
18857             }
18858             return;
18859         }
18860         
18861         var dateChanged = false,
18862         dir, day, month,
18863         newDate, newViewDate;
18864         
18865         switch(e.keyCode){
18866             case 27: // escape
18867                 this.hide();
18868                 e.preventDefault();
18869                 break;
18870             case 37: // left
18871             case 39: // right
18872                 if (!this.keyboardNavigation) {
18873                     break;
18874                 }
18875                 dir = e.keyCode == 37 ? -1 : 1;
18876                 
18877                 if (e.ctrlKey){
18878                     newDate = this.moveYear(this.date, dir);
18879                     newViewDate = this.moveYear(this.viewDate, dir);
18880                 } else if (e.shiftKey){
18881                     newDate = this.moveMonth(this.date, dir);
18882                     newViewDate = this.moveMonth(this.viewDate, dir);
18883                 } else {
18884                     newDate = new Date(this.date);
18885                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18886                     newViewDate = new Date(this.viewDate);
18887                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18888                 }
18889                 if (this.dateWithinRange(newDate)){
18890                     this.date = newDate;
18891                     this.viewDate = newViewDate;
18892                     this.setValue(this.formatDate(this.date));
18893 //                    this.update();
18894                     e.preventDefault();
18895                     dateChanged = true;
18896                 }
18897                 break;
18898             case 38: // up
18899             case 40: // down
18900                 if (!this.keyboardNavigation) {
18901                     break;
18902                 }
18903                 dir = e.keyCode == 38 ? -1 : 1;
18904                 if (e.ctrlKey){
18905                     newDate = this.moveYear(this.date, dir);
18906                     newViewDate = this.moveYear(this.viewDate, dir);
18907                 } else if (e.shiftKey){
18908                     newDate = this.moveMonth(this.date, dir);
18909                     newViewDate = this.moveMonth(this.viewDate, dir);
18910                 } else {
18911                     newDate = new Date(this.date);
18912                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18913                     newViewDate = new Date(this.viewDate);
18914                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18915                 }
18916                 if (this.dateWithinRange(newDate)){
18917                     this.date = newDate;
18918                     this.viewDate = newViewDate;
18919                     this.setValue(this.formatDate(this.date));
18920 //                    this.update();
18921                     e.preventDefault();
18922                     dateChanged = true;
18923                 }
18924                 break;
18925             case 13: // enter
18926                 this.setValue(this.formatDate(this.date));
18927                 this.hide();
18928                 e.preventDefault();
18929                 break;
18930             case 9: // tab
18931                 this.setValue(this.formatDate(this.date));
18932                 this.hide();
18933                 break;
18934             case 16: // shift
18935             case 17: // ctrl
18936             case 18: // alt
18937                 break;
18938             default :
18939                 this.hide();
18940                 
18941         }
18942     },
18943     
18944     
18945     onClick: function(e) 
18946     {
18947         e.stopPropagation();
18948         e.preventDefault();
18949         
18950         var target = e.getTarget();
18951         
18952         if(target.nodeName.toLowerCase() === 'i'){
18953             target = Roo.get(target).dom.parentNode;
18954         }
18955         
18956         var nodeName = target.nodeName;
18957         var className = target.className;
18958         var html = target.innerHTML;
18959         //Roo.log(nodeName);
18960         
18961         switch(nodeName.toLowerCase()) {
18962             case 'th':
18963                 switch(className) {
18964                     case 'switch':
18965                         this.showMode(1);
18966                         break;
18967                     case 'prev':
18968                     case 'next':
18969                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18970                         switch(this.viewMode){
18971                                 case 0:
18972                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18973                                         break;
18974                                 case 1:
18975                                 case 2:
18976                                         this.viewDate = this.moveYear(this.viewDate, dir);
18977                                         break;
18978                         }
18979                         this.fill();
18980                         break;
18981                     case 'today':
18982                         var date = new Date();
18983                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18984 //                        this.fill()
18985                         this.setValue(this.formatDate(this.date));
18986                         
18987                         this.hide();
18988                         break;
18989                 }
18990                 break;
18991             case 'span':
18992                 if (className.indexOf('disabled') < 0) {
18993                     this.viewDate.setUTCDate(1);
18994                     if (className.indexOf('month') > -1) {
18995                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18996                     } else {
18997                         var year = parseInt(html, 10) || 0;
18998                         this.viewDate.setUTCFullYear(year);
18999                         
19000                     }
19001                     
19002                     if(this.singleMode){
19003                         this.setValue(this.formatDate(this.viewDate));
19004                         this.hide();
19005                         return;
19006                     }
19007                     
19008                     this.showMode(-1);
19009                     this.fill();
19010                 }
19011                 break;
19012                 
19013             case 'td':
19014                 //Roo.log(className);
19015                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19016                     var day = parseInt(html, 10) || 1;
19017                     var year = this.viewDate.getUTCFullYear(),
19018                         month = this.viewDate.getUTCMonth();
19019
19020                     if (className.indexOf('old') > -1) {
19021                         if(month === 0 ){
19022                             month = 11;
19023                             year -= 1;
19024                         }else{
19025                             month -= 1;
19026                         }
19027                     } else if (className.indexOf('new') > -1) {
19028                         if (month == 11) {
19029                             month = 0;
19030                             year += 1;
19031                         } else {
19032                             month += 1;
19033                         }
19034                     }
19035                     //Roo.log([year,month,day]);
19036                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19037                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19038 //                    this.fill();
19039                     //Roo.log(this.formatDate(this.date));
19040                     this.setValue(this.formatDate(this.date));
19041                     this.hide();
19042                 }
19043                 break;
19044         }
19045     },
19046     
19047     setStartDate: function(startDate)
19048     {
19049         this.startDate = startDate || -Infinity;
19050         if (this.startDate !== -Infinity) {
19051             this.startDate = this.parseDate(this.startDate);
19052         }
19053         this.update();
19054         this.updateNavArrows();
19055     },
19056
19057     setEndDate: function(endDate)
19058     {
19059         this.endDate = endDate || Infinity;
19060         if (this.endDate !== Infinity) {
19061             this.endDate = this.parseDate(this.endDate);
19062         }
19063         this.update();
19064         this.updateNavArrows();
19065     },
19066     
19067     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19068     {
19069         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19070         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19071             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19072         }
19073         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19074             return parseInt(d, 10);
19075         });
19076         this.update();
19077         this.updateNavArrows();
19078     },
19079     
19080     updateNavArrows: function() 
19081     {
19082         if(this.singleMode){
19083             return;
19084         }
19085         
19086         var d = new Date(this.viewDate),
19087         year = d.getUTCFullYear(),
19088         month = d.getUTCMonth();
19089         
19090         Roo.each(this.picker().select('.prev', true).elements, function(v){
19091             v.show();
19092             switch (this.viewMode) {
19093                 case 0:
19094
19095                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19096                         v.hide();
19097                     }
19098                     break;
19099                 case 1:
19100                 case 2:
19101                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19102                         v.hide();
19103                     }
19104                     break;
19105             }
19106         });
19107         
19108         Roo.each(this.picker().select('.next', true).elements, function(v){
19109             v.show();
19110             switch (this.viewMode) {
19111                 case 0:
19112
19113                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19114                         v.hide();
19115                     }
19116                     break;
19117                 case 1:
19118                 case 2:
19119                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19120                         v.hide();
19121                     }
19122                     break;
19123             }
19124         })
19125     },
19126     
19127     moveMonth: function(date, dir)
19128     {
19129         if (!dir) {
19130             return date;
19131         }
19132         var new_date = new Date(date.valueOf()),
19133         day = new_date.getUTCDate(),
19134         month = new_date.getUTCMonth(),
19135         mag = Math.abs(dir),
19136         new_month, test;
19137         dir = dir > 0 ? 1 : -1;
19138         if (mag == 1){
19139             test = dir == -1
19140             // If going back one month, make sure month is not current month
19141             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19142             ? function(){
19143                 return new_date.getUTCMonth() == month;
19144             }
19145             // If going forward one month, make sure month is as expected
19146             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19147             : function(){
19148                 return new_date.getUTCMonth() != new_month;
19149             };
19150             new_month = month + dir;
19151             new_date.setUTCMonth(new_month);
19152             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19153             if (new_month < 0 || new_month > 11) {
19154                 new_month = (new_month + 12) % 12;
19155             }
19156         } else {
19157             // For magnitudes >1, move one month at a time...
19158             for (var i=0; i<mag; i++) {
19159                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19160                 new_date = this.moveMonth(new_date, dir);
19161             }
19162             // ...then reset the day, keeping it in the new month
19163             new_month = new_date.getUTCMonth();
19164             new_date.setUTCDate(day);
19165             test = function(){
19166                 return new_month != new_date.getUTCMonth();
19167             };
19168         }
19169         // Common date-resetting loop -- if date is beyond end of month, make it
19170         // end of month
19171         while (test()){
19172             new_date.setUTCDate(--day);
19173             new_date.setUTCMonth(new_month);
19174         }
19175         return new_date;
19176     },
19177
19178     moveYear: function(date, dir)
19179     {
19180         return this.moveMonth(date, dir*12);
19181     },
19182
19183     dateWithinRange: function(date)
19184     {
19185         return date >= this.startDate && date <= this.endDate;
19186     },
19187
19188     
19189     remove: function() 
19190     {
19191         this.picker().remove();
19192     },
19193     
19194     validateValue : function(value)
19195     {
19196         if(this.getVisibilityEl().hasClass('hidden')){
19197             return true;
19198         }
19199         
19200         if(value.length < 1)  {
19201             if(this.allowBlank){
19202                 return true;
19203             }
19204             return false;
19205         }
19206         
19207         if(value.length < this.minLength){
19208             return false;
19209         }
19210         if(value.length > this.maxLength){
19211             return false;
19212         }
19213         if(this.vtype){
19214             var vt = Roo.form.VTypes;
19215             if(!vt[this.vtype](value, this)){
19216                 return false;
19217             }
19218         }
19219         if(typeof this.validator == "function"){
19220             var msg = this.validator(value);
19221             if(msg !== true){
19222                 return false;
19223             }
19224         }
19225         
19226         if(this.regex && !this.regex.test(value)){
19227             return false;
19228         }
19229         
19230         if(typeof(this.parseDate(value)) == 'undefined'){
19231             return false;
19232         }
19233         
19234         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19235             return false;
19236         }      
19237         
19238         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19239             return false;
19240         } 
19241         
19242         
19243         return true;
19244     },
19245     
19246     setVisible : function(visible)
19247     {
19248         if(!this.getEl()){
19249             return;
19250         }
19251         
19252         this.getEl().removeClass('hidden');
19253         
19254         if(visible){
19255             return;
19256         }
19257         
19258         this.getEl().addClass('hidden');
19259     }
19260    
19261 });
19262
19263 Roo.apply(Roo.bootstrap.DateField,  {
19264     
19265     head : {
19266         tag: 'thead',
19267         cn: [
19268         {
19269             tag: 'tr',
19270             cn: [
19271             {
19272                 tag: 'th',
19273                 cls: 'prev',
19274                 html: '<i class="fa fa-arrow-left"/>'
19275             },
19276             {
19277                 tag: 'th',
19278                 cls: 'switch',
19279                 colspan: '5'
19280             },
19281             {
19282                 tag: 'th',
19283                 cls: 'next',
19284                 html: '<i class="fa fa-arrow-right"/>'
19285             }
19286
19287             ]
19288         }
19289         ]
19290     },
19291     
19292     content : {
19293         tag: 'tbody',
19294         cn: [
19295         {
19296             tag: 'tr',
19297             cn: [
19298             {
19299                 tag: 'td',
19300                 colspan: '7'
19301             }
19302             ]
19303         }
19304         ]
19305     },
19306     
19307     footer : {
19308         tag: 'tfoot',
19309         cn: [
19310         {
19311             tag: 'tr',
19312             cn: [
19313             {
19314                 tag: 'th',
19315                 colspan: '7',
19316                 cls: 'today'
19317             }
19318                     
19319             ]
19320         }
19321         ]
19322     },
19323     
19324     dates:{
19325         en: {
19326             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19327             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19328             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19329             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19330             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19331             today: "Today"
19332         }
19333     },
19334     
19335     modes: [
19336     {
19337         clsName: 'days',
19338         navFnc: 'Month',
19339         navStep: 1
19340     },
19341     {
19342         clsName: 'months',
19343         navFnc: 'FullYear',
19344         navStep: 1
19345     },
19346     {
19347         clsName: 'years',
19348         navFnc: 'FullYear',
19349         navStep: 10
19350     }]
19351 });
19352
19353 Roo.apply(Roo.bootstrap.DateField,  {
19354   
19355     template : {
19356         tag: 'div',
19357         cls: 'datepicker dropdown-menu roo-dynamic',
19358         cn: [
19359         {
19360             tag: 'div',
19361             cls: 'datepicker-days',
19362             cn: [
19363             {
19364                 tag: 'table',
19365                 cls: 'table-condensed',
19366                 cn:[
19367                 Roo.bootstrap.DateField.head,
19368                 {
19369                     tag: 'tbody'
19370                 },
19371                 Roo.bootstrap.DateField.footer
19372                 ]
19373             }
19374             ]
19375         },
19376         {
19377             tag: 'div',
19378             cls: 'datepicker-months',
19379             cn: [
19380             {
19381                 tag: 'table',
19382                 cls: 'table-condensed',
19383                 cn:[
19384                 Roo.bootstrap.DateField.head,
19385                 Roo.bootstrap.DateField.content,
19386                 Roo.bootstrap.DateField.footer
19387                 ]
19388             }
19389             ]
19390         },
19391         {
19392             tag: 'div',
19393             cls: 'datepicker-years',
19394             cn: [
19395             {
19396                 tag: 'table',
19397                 cls: 'table-condensed',
19398                 cn:[
19399                 Roo.bootstrap.DateField.head,
19400                 Roo.bootstrap.DateField.content,
19401                 Roo.bootstrap.DateField.footer
19402                 ]
19403             }
19404             ]
19405         }
19406         ]
19407     }
19408 });
19409
19410  
19411
19412  /*
19413  * - LGPL
19414  *
19415  * TimeField
19416  * 
19417  */
19418
19419 /**
19420  * @class Roo.bootstrap.TimeField
19421  * @extends Roo.bootstrap.Input
19422  * Bootstrap DateField class
19423  * 
19424  * 
19425  * @constructor
19426  * Create a new TimeField
19427  * @param {Object} config The config object
19428  */
19429
19430 Roo.bootstrap.TimeField = function(config){
19431     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19432     this.addEvents({
19433             /**
19434              * @event show
19435              * Fires when this field show.
19436              * @param {Roo.bootstrap.DateField} thisthis
19437              * @param {Mixed} date The date value
19438              */
19439             show : true,
19440             /**
19441              * @event show
19442              * Fires when this field hide.
19443              * @param {Roo.bootstrap.DateField} this
19444              * @param {Mixed} date The date value
19445              */
19446             hide : true,
19447             /**
19448              * @event select
19449              * Fires when select a date.
19450              * @param {Roo.bootstrap.DateField} this
19451              * @param {Mixed} date The date value
19452              */
19453             select : true
19454         });
19455 };
19456
19457 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19458     
19459     /**
19460      * @cfg {String} format
19461      * The default time format string which can be overriden for localization support.  The format must be
19462      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19463      */
19464     format : "H:i",
19465        
19466     onRender: function(ct, position)
19467     {
19468         
19469         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19470                 
19471         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19472         
19473         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19474         
19475         this.pop = this.picker().select('>.datepicker-time',true).first();
19476         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19477         
19478         this.picker().on('mousedown', this.onMousedown, this);
19479         this.picker().on('click', this.onClick, this);
19480         
19481         this.picker().addClass('datepicker-dropdown');
19482     
19483         this.fillTime();
19484         this.update();
19485             
19486         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19487         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19488         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19489         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19490         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19491         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19492
19493     },
19494     
19495     fireKey: function(e){
19496         if (!this.picker().isVisible()){
19497             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19498                 this.show();
19499             }
19500             return;
19501         }
19502
19503         e.preventDefault();
19504         
19505         switch(e.keyCode){
19506             case 27: // escape
19507                 this.hide();
19508                 break;
19509             case 37: // left
19510             case 39: // right
19511                 this.onTogglePeriod();
19512                 break;
19513             case 38: // up
19514                 this.onIncrementMinutes();
19515                 break;
19516             case 40: // down
19517                 this.onDecrementMinutes();
19518                 break;
19519             case 13: // enter
19520             case 9: // tab
19521                 this.setTime();
19522                 break;
19523         }
19524     },
19525     
19526     onClick: function(e) {
19527         e.stopPropagation();
19528         e.preventDefault();
19529     },
19530     
19531     picker : function()
19532     {
19533         return this.el.select('.datepicker', true).first();
19534     },
19535     
19536     fillTime: function()
19537     {    
19538         var time = this.pop.select('tbody', true).first();
19539         
19540         time.dom.innerHTML = '';
19541         
19542         time.createChild({
19543             tag: 'tr',
19544             cn: [
19545                 {
19546                     tag: 'td',
19547                     cn: [
19548                         {
19549                             tag: 'a',
19550                             href: '#',
19551                             cls: 'btn',
19552                             cn: [
19553                                 {
19554                                     tag: 'span',
19555                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19556                                 }
19557                             ]
19558                         } 
19559                     ]
19560                 },
19561                 {
19562                     tag: 'td',
19563                     cls: 'separator'
19564                 },
19565                 {
19566                     tag: 'td',
19567                     cn: [
19568                         {
19569                             tag: 'a',
19570                             href: '#',
19571                             cls: 'btn',
19572                             cn: [
19573                                 {
19574                                     tag: 'span',
19575                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19576                                 }
19577                             ]
19578                         }
19579                     ]
19580                 },
19581                 {
19582                     tag: 'td',
19583                     cls: 'separator'
19584                 }
19585             ]
19586         });
19587         
19588         time.createChild({
19589             tag: 'tr',
19590             cn: [
19591                 {
19592                     tag: 'td',
19593                     cn: [
19594                         {
19595                             tag: 'span',
19596                             cls: 'timepicker-hour',
19597                             html: '00'
19598                         }  
19599                     ]
19600                 },
19601                 {
19602                     tag: 'td',
19603                     cls: 'separator',
19604                     html: ':'
19605                 },
19606                 {
19607                     tag: 'td',
19608                     cn: [
19609                         {
19610                             tag: 'span',
19611                             cls: 'timepicker-minute',
19612                             html: '00'
19613                         }  
19614                     ]
19615                 },
19616                 {
19617                     tag: 'td',
19618                     cls: 'separator'
19619                 },
19620                 {
19621                     tag: 'td',
19622                     cn: [
19623                         {
19624                             tag: 'button',
19625                             type: 'button',
19626                             cls: 'btn btn-primary period',
19627                             html: 'AM'
19628                             
19629                         }
19630                     ]
19631                 }
19632             ]
19633         });
19634         
19635         time.createChild({
19636             tag: 'tr',
19637             cn: [
19638                 {
19639                     tag: 'td',
19640                     cn: [
19641                         {
19642                             tag: 'a',
19643                             href: '#',
19644                             cls: 'btn',
19645                             cn: [
19646                                 {
19647                                     tag: 'span',
19648                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19649                                 }
19650                             ]
19651                         }
19652                     ]
19653                 },
19654                 {
19655                     tag: 'td',
19656                     cls: 'separator'
19657                 },
19658                 {
19659                     tag: 'td',
19660                     cn: [
19661                         {
19662                             tag: 'a',
19663                             href: '#',
19664                             cls: 'btn',
19665                             cn: [
19666                                 {
19667                                     tag: 'span',
19668                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19669                                 }
19670                             ]
19671                         }
19672                     ]
19673                 },
19674                 {
19675                     tag: 'td',
19676                     cls: 'separator'
19677                 }
19678             ]
19679         });
19680         
19681     },
19682     
19683     update: function()
19684     {
19685         
19686         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19687         
19688         this.fill();
19689     },
19690     
19691     fill: function() 
19692     {
19693         var hours = this.time.getHours();
19694         var minutes = this.time.getMinutes();
19695         var period = 'AM';
19696         
19697         if(hours > 11){
19698             period = 'PM';
19699         }
19700         
19701         if(hours == 0){
19702             hours = 12;
19703         }
19704         
19705         
19706         if(hours > 12){
19707             hours = hours - 12;
19708         }
19709         
19710         if(hours < 10){
19711             hours = '0' + hours;
19712         }
19713         
19714         if(minutes < 10){
19715             minutes = '0' + minutes;
19716         }
19717         
19718         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19719         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19720         this.pop.select('button', true).first().dom.innerHTML = period;
19721         
19722     },
19723     
19724     place: function()
19725     {   
19726         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19727         
19728         var cls = ['bottom'];
19729         
19730         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19731             cls.pop();
19732             cls.push('top');
19733         }
19734         
19735         cls.push('right');
19736         
19737         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19738             cls.pop();
19739             cls.push('left');
19740         }
19741         
19742         this.picker().addClass(cls.join('-'));
19743         
19744         var _this = this;
19745         
19746         Roo.each(cls, function(c){
19747             if(c == 'bottom'){
19748                 _this.picker().setTop(_this.inputEl().getHeight());
19749                 return;
19750             }
19751             if(c == 'top'){
19752                 _this.picker().setTop(0 - _this.picker().getHeight());
19753                 return;
19754             }
19755             
19756             if(c == 'left'){
19757                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19758                 return;
19759             }
19760             if(c == 'right'){
19761                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19762                 return;
19763             }
19764         });
19765         
19766     },
19767   
19768     onFocus : function()
19769     {
19770         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19771         this.show();
19772     },
19773     
19774     onBlur : function()
19775     {
19776         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19777         this.hide();
19778     },
19779     
19780     show : function()
19781     {
19782         this.picker().show();
19783         this.pop.show();
19784         this.update();
19785         this.place();
19786         
19787         this.fireEvent('show', this, this.date);
19788     },
19789     
19790     hide : function()
19791     {
19792         this.picker().hide();
19793         this.pop.hide();
19794         
19795         this.fireEvent('hide', this, this.date);
19796     },
19797     
19798     setTime : function()
19799     {
19800         this.hide();
19801         this.setValue(this.time.format(this.format));
19802         
19803         this.fireEvent('select', this, this.date);
19804         
19805         
19806     },
19807     
19808     onMousedown: function(e){
19809         e.stopPropagation();
19810         e.preventDefault();
19811     },
19812     
19813     onIncrementHours: function()
19814     {
19815         Roo.log('onIncrementHours');
19816         this.time = this.time.add(Date.HOUR, 1);
19817         this.update();
19818         
19819     },
19820     
19821     onDecrementHours: function()
19822     {
19823         Roo.log('onDecrementHours');
19824         this.time = this.time.add(Date.HOUR, -1);
19825         this.update();
19826     },
19827     
19828     onIncrementMinutes: function()
19829     {
19830         Roo.log('onIncrementMinutes');
19831         this.time = this.time.add(Date.MINUTE, 1);
19832         this.update();
19833     },
19834     
19835     onDecrementMinutes: function()
19836     {
19837         Roo.log('onDecrementMinutes');
19838         this.time = this.time.add(Date.MINUTE, -1);
19839         this.update();
19840     },
19841     
19842     onTogglePeriod: function()
19843     {
19844         Roo.log('onTogglePeriod');
19845         this.time = this.time.add(Date.HOUR, 12);
19846         this.update();
19847     }
19848     
19849    
19850 });
19851
19852 Roo.apply(Roo.bootstrap.TimeField,  {
19853     
19854     content : {
19855         tag: 'tbody',
19856         cn: [
19857             {
19858                 tag: 'tr',
19859                 cn: [
19860                 {
19861                     tag: 'td',
19862                     colspan: '7'
19863                 }
19864                 ]
19865             }
19866         ]
19867     },
19868     
19869     footer : {
19870         tag: 'tfoot',
19871         cn: [
19872             {
19873                 tag: 'tr',
19874                 cn: [
19875                 {
19876                     tag: 'th',
19877                     colspan: '7',
19878                     cls: '',
19879                     cn: [
19880                         {
19881                             tag: 'button',
19882                             cls: 'btn btn-info ok',
19883                             html: 'OK'
19884                         }
19885                     ]
19886                 }
19887
19888                 ]
19889             }
19890         ]
19891     }
19892 });
19893
19894 Roo.apply(Roo.bootstrap.TimeField,  {
19895   
19896     template : {
19897         tag: 'div',
19898         cls: 'datepicker dropdown-menu',
19899         cn: [
19900             {
19901                 tag: 'div',
19902                 cls: 'datepicker-time',
19903                 cn: [
19904                 {
19905                     tag: 'table',
19906                     cls: 'table-condensed',
19907                     cn:[
19908                     Roo.bootstrap.TimeField.content,
19909                     Roo.bootstrap.TimeField.footer
19910                     ]
19911                 }
19912                 ]
19913             }
19914         ]
19915     }
19916 });
19917
19918  
19919
19920  /*
19921  * - LGPL
19922  *
19923  * MonthField
19924  * 
19925  */
19926
19927 /**
19928  * @class Roo.bootstrap.MonthField
19929  * @extends Roo.bootstrap.Input
19930  * Bootstrap MonthField class
19931  * 
19932  * @cfg {String} language default en
19933  * 
19934  * @constructor
19935  * Create a new MonthField
19936  * @param {Object} config The config object
19937  */
19938
19939 Roo.bootstrap.MonthField = function(config){
19940     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19941     
19942     this.addEvents({
19943         /**
19944          * @event show
19945          * Fires when this field show.
19946          * @param {Roo.bootstrap.MonthField} this
19947          * @param {Mixed} date The date value
19948          */
19949         show : true,
19950         /**
19951          * @event show
19952          * Fires when this field hide.
19953          * @param {Roo.bootstrap.MonthField} this
19954          * @param {Mixed} date The date value
19955          */
19956         hide : true,
19957         /**
19958          * @event select
19959          * Fires when select a date.
19960          * @param {Roo.bootstrap.MonthField} this
19961          * @param {String} oldvalue The old value
19962          * @param {String} newvalue The new value
19963          */
19964         select : true
19965     });
19966 };
19967
19968 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19969     
19970     onRender: function(ct, position)
19971     {
19972         
19973         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19974         
19975         this.language = this.language || 'en';
19976         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19977         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19978         
19979         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19980         this.isInline = false;
19981         this.isInput = true;
19982         this.component = this.el.select('.add-on', true).first() || false;
19983         this.component = (this.component && this.component.length === 0) ? false : this.component;
19984         this.hasInput = this.component && this.inputEL().length;
19985         
19986         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19987         
19988         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19989         
19990         this.picker().on('mousedown', this.onMousedown, this);
19991         this.picker().on('click', this.onClick, this);
19992         
19993         this.picker().addClass('datepicker-dropdown');
19994         
19995         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19996             v.setStyle('width', '189px');
19997         });
19998         
19999         this.fillMonths();
20000         
20001         this.update();
20002         
20003         if(this.isInline) {
20004             this.show();
20005         }
20006         
20007     },
20008     
20009     setValue: function(v, suppressEvent)
20010     {   
20011         var o = this.getValue();
20012         
20013         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20014         
20015         this.update();
20016
20017         if(suppressEvent !== true){
20018             this.fireEvent('select', this, o, v);
20019         }
20020         
20021     },
20022     
20023     getValue: function()
20024     {
20025         return this.value;
20026     },
20027     
20028     onClick: function(e) 
20029     {
20030         e.stopPropagation();
20031         e.preventDefault();
20032         
20033         var target = e.getTarget();
20034         
20035         if(target.nodeName.toLowerCase() === 'i'){
20036             target = Roo.get(target).dom.parentNode;
20037         }
20038         
20039         var nodeName = target.nodeName;
20040         var className = target.className;
20041         var html = target.innerHTML;
20042         
20043         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20044             return;
20045         }
20046         
20047         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20048         
20049         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20050         
20051         this.hide();
20052                         
20053     },
20054     
20055     picker : function()
20056     {
20057         return this.pickerEl;
20058     },
20059     
20060     fillMonths: function()
20061     {    
20062         var i = 0;
20063         var months = this.picker().select('>.datepicker-months td', true).first();
20064         
20065         months.dom.innerHTML = '';
20066         
20067         while (i < 12) {
20068             var month = {
20069                 tag: 'span',
20070                 cls: 'month',
20071                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20072             };
20073             
20074             months.createChild(month);
20075         }
20076         
20077     },
20078     
20079     update: function()
20080     {
20081         var _this = this;
20082         
20083         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20084             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20085         }
20086         
20087         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20088             e.removeClass('active');
20089             
20090             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20091                 e.addClass('active');
20092             }
20093         })
20094     },
20095     
20096     place: function()
20097     {
20098         if(this.isInline) {
20099             return;
20100         }
20101         
20102         this.picker().removeClass(['bottom', 'top']);
20103         
20104         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20105             /*
20106              * place to the top of element!
20107              *
20108              */
20109             
20110             this.picker().addClass('top');
20111             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20112             
20113             return;
20114         }
20115         
20116         this.picker().addClass('bottom');
20117         
20118         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20119     },
20120     
20121     onFocus : function()
20122     {
20123         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20124         this.show();
20125     },
20126     
20127     onBlur : function()
20128     {
20129         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20130         
20131         var d = this.inputEl().getValue();
20132         
20133         this.setValue(d);
20134                 
20135         this.hide();
20136     },
20137     
20138     show : function()
20139     {
20140         this.picker().show();
20141         this.picker().select('>.datepicker-months', true).first().show();
20142         this.update();
20143         this.place();
20144         
20145         this.fireEvent('show', this, this.date);
20146     },
20147     
20148     hide : function()
20149     {
20150         if(this.isInline) {
20151             return;
20152         }
20153         this.picker().hide();
20154         this.fireEvent('hide', this, this.date);
20155         
20156     },
20157     
20158     onMousedown: function(e)
20159     {
20160         e.stopPropagation();
20161         e.preventDefault();
20162     },
20163     
20164     keyup: function(e)
20165     {
20166         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20167         this.update();
20168     },
20169
20170     fireKey: function(e)
20171     {
20172         if (!this.picker().isVisible()){
20173             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20174                 this.show();
20175             }
20176             return;
20177         }
20178         
20179         var dir;
20180         
20181         switch(e.keyCode){
20182             case 27: // escape
20183                 this.hide();
20184                 e.preventDefault();
20185                 break;
20186             case 37: // left
20187             case 39: // right
20188                 dir = e.keyCode == 37 ? -1 : 1;
20189                 
20190                 this.vIndex = this.vIndex + dir;
20191                 
20192                 if(this.vIndex < 0){
20193                     this.vIndex = 0;
20194                 }
20195                 
20196                 if(this.vIndex > 11){
20197                     this.vIndex = 11;
20198                 }
20199                 
20200                 if(isNaN(this.vIndex)){
20201                     this.vIndex = 0;
20202                 }
20203                 
20204                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20205                 
20206                 break;
20207             case 38: // up
20208             case 40: // down
20209                 
20210                 dir = e.keyCode == 38 ? -1 : 1;
20211                 
20212                 this.vIndex = this.vIndex + dir * 4;
20213                 
20214                 if(this.vIndex < 0){
20215                     this.vIndex = 0;
20216                 }
20217                 
20218                 if(this.vIndex > 11){
20219                     this.vIndex = 11;
20220                 }
20221                 
20222                 if(isNaN(this.vIndex)){
20223                     this.vIndex = 0;
20224                 }
20225                 
20226                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20227                 break;
20228                 
20229             case 13: // enter
20230                 
20231                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20232                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20233                 }
20234                 
20235                 this.hide();
20236                 e.preventDefault();
20237                 break;
20238             case 9: // tab
20239                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20240                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20241                 }
20242                 this.hide();
20243                 break;
20244             case 16: // shift
20245             case 17: // ctrl
20246             case 18: // alt
20247                 break;
20248             default :
20249                 this.hide();
20250                 
20251         }
20252     },
20253     
20254     remove: function() 
20255     {
20256         this.picker().remove();
20257     }
20258    
20259 });
20260
20261 Roo.apply(Roo.bootstrap.MonthField,  {
20262     
20263     content : {
20264         tag: 'tbody',
20265         cn: [
20266         {
20267             tag: 'tr',
20268             cn: [
20269             {
20270                 tag: 'td',
20271                 colspan: '7'
20272             }
20273             ]
20274         }
20275         ]
20276     },
20277     
20278     dates:{
20279         en: {
20280             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20281             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20282         }
20283     }
20284 });
20285
20286 Roo.apply(Roo.bootstrap.MonthField,  {
20287   
20288     template : {
20289         tag: 'div',
20290         cls: 'datepicker dropdown-menu roo-dynamic',
20291         cn: [
20292             {
20293                 tag: 'div',
20294                 cls: 'datepicker-months',
20295                 cn: [
20296                 {
20297                     tag: 'table',
20298                     cls: 'table-condensed',
20299                     cn:[
20300                         Roo.bootstrap.DateField.content
20301                     ]
20302                 }
20303                 ]
20304             }
20305         ]
20306     }
20307 });
20308
20309  
20310
20311  
20312  /*
20313  * - LGPL
20314  *
20315  * CheckBox
20316  * 
20317  */
20318
20319 /**
20320  * @class Roo.bootstrap.CheckBox
20321  * @extends Roo.bootstrap.Input
20322  * Bootstrap CheckBox class
20323  * 
20324  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20325  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20326  * @cfg {String} boxLabel The text that appears beside the checkbox
20327  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20328  * @cfg {Boolean} checked initnal the element
20329  * @cfg {Boolean} inline inline the element (default false)
20330  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20331  * @cfg {String} tooltip label tooltip
20332  * 
20333  * @constructor
20334  * Create a new CheckBox
20335  * @param {Object} config The config object
20336  */
20337
20338 Roo.bootstrap.CheckBox = function(config){
20339     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20340    
20341     this.addEvents({
20342         /**
20343         * @event check
20344         * Fires when the element is checked or unchecked.
20345         * @param {Roo.bootstrap.CheckBox} this This input
20346         * @param {Boolean} checked The new checked value
20347         */
20348        check : true,
20349        /**
20350         * @event click
20351         * Fires when the element is click.
20352         * @param {Roo.bootstrap.CheckBox} this This input
20353         */
20354        click : true
20355     });
20356     
20357 };
20358
20359 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20360   
20361     inputType: 'checkbox',
20362     inputValue: 1,
20363     valueOff: 0,
20364     boxLabel: false,
20365     checked: false,
20366     weight : false,
20367     inline: false,
20368     tooltip : '',
20369     
20370     getAutoCreate : function()
20371     {
20372         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20373         
20374         var id = Roo.id();
20375         
20376         var cfg = {};
20377         
20378         cfg.cls = 'form-group ' + this.inputType; //input-group
20379         
20380         if(this.inline){
20381             cfg.cls += ' ' + this.inputType + '-inline';
20382         }
20383         
20384         var input =  {
20385             tag: 'input',
20386             id : id,
20387             type : this.inputType,
20388             value : this.inputValue,
20389             cls : 'roo-' + this.inputType, //'form-box',
20390             placeholder : this.placeholder || ''
20391             
20392         };
20393         
20394         if(this.inputType != 'radio'){
20395             var hidden =  {
20396                 tag: 'input',
20397                 type : 'hidden',
20398                 cls : 'roo-hidden-value',
20399                 value : this.checked ? this.inputValue : this.valueOff
20400             };
20401         }
20402         
20403             
20404         if (this.weight) { // Validity check?
20405             cfg.cls += " " + this.inputType + "-" + this.weight;
20406         }
20407         
20408         if (this.disabled) {
20409             input.disabled=true;
20410         }
20411         
20412         if(this.checked){
20413             input.checked = this.checked;
20414         }
20415         
20416         if (this.name) {
20417             
20418             input.name = this.name;
20419             
20420             if(this.inputType != 'radio'){
20421                 hidden.name = this.name;
20422                 input.name = '_hidden_' + this.name;
20423             }
20424         }
20425         
20426         if (this.size) {
20427             input.cls += ' input-' + this.size;
20428         }
20429         
20430         var settings=this;
20431         
20432         ['xs','sm','md','lg'].map(function(size){
20433             if (settings[size]) {
20434                 cfg.cls += ' col-' + size + '-' + settings[size];
20435             }
20436         });
20437         
20438         var inputblock = input;
20439          
20440         if (this.before || this.after) {
20441             
20442             inputblock = {
20443                 cls : 'input-group',
20444                 cn :  [] 
20445             };
20446             
20447             if (this.before) {
20448                 inputblock.cn.push({
20449                     tag :'span',
20450                     cls : 'input-group-addon',
20451                     html : this.before
20452                 });
20453             }
20454             
20455             inputblock.cn.push(input);
20456             
20457             if(this.inputType != 'radio'){
20458                 inputblock.cn.push(hidden);
20459             }
20460             
20461             if (this.after) {
20462                 inputblock.cn.push({
20463                     tag :'span',
20464                     cls : 'input-group-addon',
20465                     html : this.after
20466                 });
20467             }
20468             
20469         }
20470         
20471         if (align ==='left' && this.fieldLabel.length) {
20472 //                Roo.log("left and has label");
20473             cfg.cn = [
20474                 {
20475                     tag: 'label',
20476                     'for' :  id,
20477                     cls : 'control-label',
20478                     html : this.fieldLabel
20479                 },
20480                 {
20481                     cls : "", 
20482                     cn: [
20483                         inputblock
20484                     ]
20485                 }
20486             ];
20487             
20488             if(this.labelWidth > 12){
20489                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20490             }
20491             
20492             if(this.labelWidth < 13 && this.labelmd == 0){
20493                 this.labelmd = this.labelWidth;
20494             }
20495             
20496             if(this.labellg > 0){
20497                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20498                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20499             }
20500             
20501             if(this.labelmd > 0){
20502                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20503                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20504             }
20505             
20506             if(this.labelsm > 0){
20507                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20508                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20509             }
20510             
20511             if(this.labelxs > 0){
20512                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20513                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20514             }
20515             
20516         } else if ( this.fieldLabel.length) {
20517 //                Roo.log(" label");
20518                 cfg.cn = [
20519                    
20520                     {
20521                         tag: this.boxLabel ? 'span' : 'label',
20522                         'for': id,
20523                         cls: 'control-label box-input-label',
20524                         //cls : 'input-group-addon',
20525                         html : this.fieldLabel
20526                     },
20527                     
20528                     inputblock
20529                     
20530                 ];
20531
20532         } else {
20533             
20534 //                Roo.log(" no label && no align");
20535                 cfg.cn = [  inputblock ] ;
20536                 
20537                 
20538         }
20539         
20540         if(this.boxLabel){
20541              var boxLabelCfg = {
20542                 tag: 'label',
20543                 //'for': id, // box label is handled by onclick - so no for...
20544                 cls: 'box-label',
20545                 html: this.boxLabel
20546             };
20547             
20548             if(this.tooltip){
20549                 boxLabelCfg.tooltip = this.tooltip;
20550             }
20551              
20552             cfg.cn.push(boxLabelCfg);
20553         }
20554         
20555         if(this.inputType != 'radio'){
20556             cfg.cn.push(hidden);
20557         }
20558         
20559         return cfg;
20560         
20561     },
20562     
20563     /**
20564      * return the real input element.
20565      */
20566     inputEl: function ()
20567     {
20568         return this.el.select('input.roo-' + this.inputType,true).first();
20569     },
20570     hiddenEl: function ()
20571     {
20572         return this.el.select('input.roo-hidden-value',true).first();
20573     },
20574     
20575     labelEl: function()
20576     {
20577         return this.el.select('label.control-label',true).first();
20578     },
20579     /* depricated... */
20580     
20581     label: function()
20582     {
20583         return this.labelEl();
20584     },
20585     
20586     boxLabelEl: function()
20587     {
20588         return this.el.select('label.box-label',true).first();
20589     },
20590     
20591     initEvents : function()
20592     {
20593 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20594         
20595         this.inputEl().on('click', this.onClick,  this);
20596         
20597         if (this.boxLabel) { 
20598             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20599         }
20600         
20601         this.startValue = this.getValue();
20602         
20603         if(this.groupId){
20604             Roo.bootstrap.CheckBox.register(this);
20605         }
20606     },
20607     
20608     onClick : function(e)
20609     {   
20610         if(this.fireEvent('click', this, e) !== false){
20611             this.setChecked(!this.checked);
20612         }
20613         
20614     },
20615     
20616     setChecked : function(state,suppressEvent)
20617     {
20618         this.startValue = this.getValue();
20619
20620         if(this.inputType == 'radio'){
20621             
20622             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20623                 e.dom.checked = false;
20624             });
20625             
20626             this.inputEl().dom.checked = true;
20627             
20628             this.inputEl().dom.value = this.inputValue;
20629             
20630             if(suppressEvent !== true){
20631                 this.fireEvent('check', this, true);
20632             }
20633             
20634             this.validate();
20635             
20636             return;
20637         }
20638         
20639         this.checked = state;
20640         
20641         this.inputEl().dom.checked = state;
20642         
20643         
20644         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20645         
20646         if(suppressEvent !== true){
20647             this.fireEvent('check', this, state);
20648         }
20649         
20650         this.validate();
20651     },
20652     
20653     getValue : function()
20654     {
20655         if(this.inputType == 'radio'){
20656             return this.getGroupValue();
20657         }
20658         
20659         return this.hiddenEl().dom.value;
20660         
20661     },
20662     
20663     getGroupValue : function()
20664     {
20665         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20666             return '';
20667         }
20668         
20669         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20670     },
20671     
20672     setValue : function(v,suppressEvent)
20673     {
20674         if(this.inputType == 'radio'){
20675             this.setGroupValue(v, suppressEvent);
20676             return;
20677         }
20678         
20679         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20680         
20681         this.validate();
20682     },
20683     
20684     setGroupValue : function(v, suppressEvent)
20685     {
20686         this.startValue = this.getValue();
20687         
20688         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20689             e.dom.checked = false;
20690             
20691             if(e.dom.value == v){
20692                 e.dom.checked = true;
20693             }
20694         });
20695         
20696         if(suppressEvent !== true){
20697             this.fireEvent('check', this, true);
20698         }
20699
20700         this.validate();
20701         
20702         return;
20703     },
20704     
20705     validate : function()
20706     {
20707         if(this.getVisibilityEl().hasClass('hidden')){
20708             return true;
20709         }
20710         
20711         if(
20712                 this.disabled || 
20713                 (this.inputType == 'radio' && this.validateRadio()) ||
20714                 (this.inputType == 'checkbox' && this.validateCheckbox())
20715         ){
20716             this.markValid();
20717             return true;
20718         }
20719         
20720         this.markInvalid();
20721         return false;
20722     },
20723     
20724     validateRadio : function()
20725     {
20726         if(this.getVisibilityEl().hasClass('hidden')){
20727             return true;
20728         }
20729         
20730         if(this.allowBlank){
20731             return true;
20732         }
20733         
20734         var valid = false;
20735         
20736         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20737             if(!e.dom.checked){
20738                 return;
20739             }
20740             
20741             valid = true;
20742             
20743             return false;
20744         });
20745         
20746         return valid;
20747     },
20748     
20749     validateCheckbox : function()
20750     {
20751         if(!this.groupId){
20752             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20753             //return (this.getValue() == this.inputValue) ? true : false;
20754         }
20755         
20756         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20757         
20758         if(!group){
20759             return false;
20760         }
20761         
20762         var r = false;
20763         
20764         for(var i in group){
20765             if(group[i].el.isVisible(true)){
20766                 r = false;
20767                 break;
20768             }
20769             
20770             r = true;
20771         }
20772         
20773         for(var i in group){
20774             if(r){
20775                 break;
20776             }
20777             
20778             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20779         }
20780         
20781         return r;
20782     },
20783     
20784     /**
20785      * Mark this field as valid
20786      */
20787     markValid : function()
20788     {
20789         var _this = this;
20790         
20791         this.fireEvent('valid', this);
20792         
20793         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20794         
20795         if(this.groupId){
20796             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20797         }
20798         
20799         if(label){
20800             label.markValid();
20801         }
20802
20803         if(this.inputType == 'radio'){
20804             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20805                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20806                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20807             });
20808             
20809             return;
20810         }
20811
20812         if(!this.groupId){
20813             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20814             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20815             return;
20816         }
20817         
20818         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20819         
20820         if(!group){
20821             return;
20822         }
20823         
20824         for(var i in group){
20825             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20826             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20827         }
20828     },
20829     
20830      /**
20831      * Mark this field as invalid
20832      * @param {String} msg The validation message
20833      */
20834     markInvalid : function(msg)
20835     {
20836         if(this.allowBlank){
20837             return;
20838         }
20839         
20840         var _this = this;
20841         
20842         this.fireEvent('invalid', this, msg);
20843         
20844         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20845         
20846         if(this.groupId){
20847             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20848         }
20849         
20850         if(label){
20851             label.markInvalid();
20852         }
20853             
20854         if(this.inputType == 'radio'){
20855             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20856                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20857                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20858             });
20859             
20860             return;
20861         }
20862         
20863         if(!this.groupId){
20864             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20865             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20866             return;
20867         }
20868         
20869         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20870         
20871         if(!group){
20872             return;
20873         }
20874         
20875         for(var i in group){
20876             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20877             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20878         }
20879         
20880     },
20881     
20882     clearInvalid : function()
20883     {
20884         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20885         
20886         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20887         
20888         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20889         
20890         if (label && label.iconEl) {
20891             label.iconEl.removeClass(label.validClass);
20892             label.iconEl.removeClass(label.invalidClass);
20893         }
20894     },
20895     
20896     disable : function()
20897     {
20898         if(this.inputType != 'radio'){
20899             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20900             return;
20901         }
20902         
20903         var _this = this;
20904         
20905         if(this.rendered){
20906             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20907                 _this.getActionEl().addClass(this.disabledClass);
20908                 e.dom.disabled = true;
20909             });
20910         }
20911         
20912         this.disabled = true;
20913         this.fireEvent("disable", this);
20914         return this;
20915     },
20916
20917     enable : function()
20918     {
20919         if(this.inputType != 'radio'){
20920             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20921             return;
20922         }
20923         
20924         var _this = this;
20925         
20926         if(this.rendered){
20927             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20928                 _this.getActionEl().removeClass(this.disabledClass);
20929                 e.dom.disabled = false;
20930             });
20931         }
20932         
20933         this.disabled = false;
20934         this.fireEvent("enable", this);
20935         return this;
20936     },
20937     
20938     setBoxLabel : function(v)
20939     {
20940         this.boxLabel = v;
20941         
20942         if(this.rendered){
20943             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20944         }
20945     }
20946
20947 });
20948
20949 Roo.apply(Roo.bootstrap.CheckBox, {
20950     
20951     groups: {},
20952     
20953      /**
20954     * register a CheckBox Group
20955     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20956     */
20957     register : function(checkbox)
20958     {
20959         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20960             this.groups[checkbox.groupId] = {};
20961         }
20962         
20963         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20964             return;
20965         }
20966         
20967         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20968         
20969     },
20970     /**
20971     * fetch a CheckBox Group based on the group ID
20972     * @param {string} the group ID
20973     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20974     */
20975     get: function(groupId) {
20976         if (typeof(this.groups[groupId]) == 'undefined') {
20977             return false;
20978         }
20979         
20980         return this.groups[groupId] ;
20981     }
20982     
20983     
20984 });
20985 /*
20986  * - LGPL
20987  *
20988  * RadioItem
20989  * 
20990  */
20991
20992 /**
20993  * @class Roo.bootstrap.Radio
20994  * @extends Roo.bootstrap.Component
20995  * Bootstrap Radio class
20996  * @cfg {String} boxLabel - the label associated
20997  * @cfg {String} value - the value of radio
20998  * 
20999  * @constructor
21000  * Create a new Radio
21001  * @param {Object} config The config object
21002  */
21003 Roo.bootstrap.Radio = function(config){
21004     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21005     
21006 };
21007
21008 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21009     
21010     boxLabel : '',
21011     
21012     value : '',
21013     
21014     getAutoCreate : function()
21015     {
21016         var cfg = {
21017             tag : 'div',
21018             cls : 'form-group radio',
21019             cn : [
21020                 {
21021                     tag : 'label',
21022                     cls : 'box-label',
21023                     html : this.boxLabel
21024                 }
21025             ]
21026         };
21027         
21028         return cfg;
21029     },
21030     
21031     initEvents : function() 
21032     {
21033         this.parent().register(this);
21034         
21035         this.el.on('click', this.onClick, this);
21036         
21037     },
21038     
21039     onClick : function(e)
21040     {
21041         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21042             this.setChecked(true);
21043         }
21044     },
21045     
21046     setChecked : function(state, suppressEvent)
21047     {
21048         this.parent().setValue(this.value, suppressEvent);
21049         
21050     },
21051     
21052     setBoxLabel : function(v)
21053     {
21054         this.boxLabel = v;
21055         
21056         if(this.rendered){
21057             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21058         }
21059     }
21060     
21061 });
21062  
21063
21064  /*
21065  * - LGPL
21066  *
21067  * Input
21068  * 
21069  */
21070
21071 /**
21072  * @class Roo.bootstrap.SecurePass
21073  * @extends Roo.bootstrap.Input
21074  * Bootstrap SecurePass class
21075  *
21076  * 
21077  * @constructor
21078  * Create a new SecurePass
21079  * @param {Object} config The config object
21080  */
21081  
21082 Roo.bootstrap.SecurePass = function (config) {
21083     // these go here, so the translation tool can replace them..
21084     this.errors = {
21085         PwdEmpty: "Please type a password, and then retype it to confirm.",
21086         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21087         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21088         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21089         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21090         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21091         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21092         TooWeak: "Your password is Too Weak."
21093     },
21094     this.meterLabel = "Password strength:";
21095     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21096     this.meterClass = [
21097         "roo-password-meter-tooweak", 
21098         "roo-password-meter-weak", 
21099         "roo-password-meter-medium", 
21100         "roo-password-meter-strong", 
21101         "roo-password-meter-grey"
21102     ];
21103     
21104     this.errors = {};
21105     
21106     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21107 }
21108
21109 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21110     /**
21111      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21112      * {
21113      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21114      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21115      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21116      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21117      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21118      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21119      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21120      * })
21121      */
21122     // private
21123     
21124     meterWidth: 300,
21125     errorMsg :'',    
21126     errors: false,
21127     imageRoot: '/',
21128     /**
21129      * @cfg {String/Object} Label for the strength meter (defaults to
21130      * 'Password strength:')
21131      */
21132     // private
21133     meterLabel: '',
21134     /**
21135      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21136      * ['Weak', 'Medium', 'Strong'])
21137      */
21138     // private    
21139     pwdStrengths: false,    
21140     // private
21141     strength: 0,
21142     // private
21143     _lastPwd: null,
21144     // private
21145     kCapitalLetter: 0,
21146     kSmallLetter: 1,
21147     kDigit: 2,
21148     kPunctuation: 3,
21149     
21150     insecure: false,
21151     // private
21152     initEvents: function ()
21153     {
21154         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21155
21156         if (this.el.is('input[type=password]') && Roo.isSafari) {
21157             this.el.on('keydown', this.SafariOnKeyDown, this);
21158         }
21159
21160         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21161     },
21162     // private
21163     onRender: function (ct, position)
21164     {
21165         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21166         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21167         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21168
21169         this.trigger.createChild({
21170                    cn: [
21171                     {
21172                     //id: 'PwdMeter',
21173                     tag: 'div',
21174                     cls: 'roo-password-meter-grey col-xs-12',
21175                     style: {
21176                         //width: 0,
21177                         //width: this.meterWidth + 'px'                                                
21178                         }
21179                     },
21180                     {                            
21181                          cls: 'roo-password-meter-text'                          
21182                     }
21183                 ]            
21184         });
21185
21186          
21187         if (this.hideTrigger) {
21188             this.trigger.setDisplayed(false);
21189         }
21190         this.setSize(this.width || '', this.height || '');
21191     },
21192     // private
21193     onDestroy: function ()
21194     {
21195         if (this.trigger) {
21196             this.trigger.removeAllListeners();
21197             this.trigger.remove();
21198         }
21199         if (this.wrap) {
21200             this.wrap.remove();
21201         }
21202         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21203     },
21204     // private
21205     checkStrength: function ()
21206     {
21207         var pwd = this.inputEl().getValue();
21208         if (pwd == this._lastPwd) {
21209             return;
21210         }
21211
21212         var strength;
21213         if (this.ClientSideStrongPassword(pwd)) {
21214             strength = 3;
21215         } else if (this.ClientSideMediumPassword(pwd)) {
21216             strength = 2;
21217         } else if (this.ClientSideWeakPassword(pwd)) {
21218             strength = 1;
21219         } else {
21220             strength = 0;
21221         }
21222         
21223         Roo.log('strength1: ' + strength);
21224         
21225         //var pm = this.trigger.child('div/div/div').dom;
21226         var pm = this.trigger.child('div/div');
21227         pm.removeClass(this.meterClass);
21228         pm.addClass(this.meterClass[strength]);
21229                 
21230         
21231         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21232                 
21233         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21234         
21235         this._lastPwd = pwd;
21236     },
21237     reset: function ()
21238     {
21239         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21240         
21241         this._lastPwd = '';
21242         
21243         var pm = this.trigger.child('div/div');
21244         pm.removeClass(this.meterClass);
21245         pm.addClass('roo-password-meter-grey');        
21246         
21247         
21248         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21249         
21250         pt.innerHTML = '';
21251         this.inputEl().dom.type='password';
21252     },
21253     // private
21254     validateValue: function (value)
21255     {
21256         
21257         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21258             return false;
21259         }
21260         if (value.length == 0) {
21261             if (this.allowBlank) {
21262                 this.clearInvalid();
21263                 return true;
21264             }
21265
21266             this.markInvalid(this.errors.PwdEmpty);
21267             this.errorMsg = this.errors.PwdEmpty;
21268             return false;
21269         }
21270         
21271         if(this.insecure){
21272             return true;
21273         }
21274         
21275         if ('[\x21-\x7e]*'.match(value)) {
21276             this.markInvalid(this.errors.PwdBadChar);
21277             this.errorMsg = this.errors.PwdBadChar;
21278             return false;
21279         }
21280         if (value.length < 6) {
21281             this.markInvalid(this.errors.PwdShort);
21282             this.errorMsg = this.errors.PwdShort;
21283             return false;
21284         }
21285         if (value.length > 16) {
21286             this.markInvalid(this.errors.PwdLong);
21287             this.errorMsg = this.errors.PwdLong;
21288             return false;
21289         }
21290         var strength;
21291         if (this.ClientSideStrongPassword(value)) {
21292             strength = 3;
21293         } else if (this.ClientSideMediumPassword(value)) {
21294             strength = 2;
21295         } else if (this.ClientSideWeakPassword(value)) {
21296             strength = 1;
21297         } else {
21298             strength = 0;
21299         }
21300
21301         
21302         if (strength < 2) {
21303             //this.markInvalid(this.errors.TooWeak);
21304             this.errorMsg = this.errors.TooWeak;
21305             //return false;
21306         }
21307         
21308         
21309         console.log('strength2: ' + strength);
21310         
21311         //var pm = this.trigger.child('div/div/div').dom;
21312         
21313         var pm = this.trigger.child('div/div');
21314         pm.removeClass(this.meterClass);
21315         pm.addClass(this.meterClass[strength]);
21316                 
21317         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21318                 
21319         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21320         
21321         this.errorMsg = ''; 
21322         return true;
21323     },
21324     // private
21325     CharacterSetChecks: function (type)
21326     {
21327         this.type = type;
21328         this.fResult = false;
21329     },
21330     // private
21331     isctype: function (character, type)
21332     {
21333         switch (type) {  
21334             case this.kCapitalLetter:
21335                 if (character >= 'A' && character <= 'Z') {
21336                     return true;
21337                 }
21338                 break;
21339             
21340             case this.kSmallLetter:
21341                 if (character >= 'a' && character <= 'z') {
21342                     return true;
21343                 }
21344                 break;
21345             
21346             case this.kDigit:
21347                 if (character >= '0' && character <= '9') {
21348                     return true;
21349                 }
21350                 break;
21351             
21352             case this.kPunctuation:
21353                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21354                     return true;
21355                 }
21356                 break;
21357             
21358             default:
21359                 return false;
21360         }
21361
21362     },
21363     // private
21364     IsLongEnough: function (pwd, size)
21365     {
21366         return !(pwd == null || isNaN(size) || pwd.length < size);
21367     },
21368     // private
21369     SpansEnoughCharacterSets: function (word, nb)
21370     {
21371         if (!this.IsLongEnough(word, nb))
21372         {
21373             return false;
21374         }
21375
21376         var characterSetChecks = new Array(
21377             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21378             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21379         );
21380         
21381         for (var index = 0; index < word.length; ++index) {
21382             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21383                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21384                     characterSetChecks[nCharSet].fResult = true;
21385                     break;
21386                 }
21387             }
21388         }
21389
21390         var nCharSets = 0;
21391         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21392             if (characterSetChecks[nCharSet].fResult) {
21393                 ++nCharSets;
21394             }
21395         }
21396
21397         if (nCharSets < nb) {
21398             return false;
21399         }
21400         return true;
21401     },
21402     // private
21403     ClientSideStrongPassword: function (pwd)
21404     {
21405         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21406     },
21407     // private
21408     ClientSideMediumPassword: function (pwd)
21409     {
21410         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21411     },
21412     // private
21413     ClientSideWeakPassword: function (pwd)
21414     {
21415         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21416     }
21417           
21418 })//<script type="text/javascript">
21419
21420 /*
21421  * Based  Ext JS Library 1.1.1
21422  * Copyright(c) 2006-2007, Ext JS, LLC.
21423  * LGPL
21424  *
21425  */
21426  
21427 /**
21428  * @class Roo.HtmlEditorCore
21429  * @extends Roo.Component
21430  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21431  *
21432  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21433  */
21434
21435 Roo.HtmlEditorCore = function(config){
21436     
21437     
21438     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21439     
21440     
21441     this.addEvents({
21442         /**
21443          * @event initialize
21444          * Fires when the editor is fully initialized (including the iframe)
21445          * @param {Roo.HtmlEditorCore} this
21446          */
21447         initialize: true,
21448         /**
21449          * @event activate
21450          * Fires when the editor is first receives the focus. Any insertion must wait
21451          * until after this event.
21452          * @param {Roo.HtmlEditorCore} this
21453          */
21454         activate: true,
21455          /**
21456          * @event beforesync
21457          * Fires before the textarea is updated with content from the editor iframe. Return false
21458          * to cancel the sync.
21459          * @param {Roo.HtmlEditorCore} this
21460          * @param {String} html
21461          */
21462         beforesync: true,
21463          /**
21464          * @event beforepush
21465          * Fires before the iframe editor is updated with content from the textarea. Return false
21466          * to cancel the push.
21467          * @param {Roo.HtmlEditorCore} this
21468          * @param {String} html
21469          */
21470         beforepush: true,
21471          /**
21472          * @event sync
21473          * Fires when the textarea is updated with content from the editor iframe.
21474          * @param {Roo.HtmlEditorCore} this
21475          * @param {String} html
21476          */
21477         sync: true,
21478          /**
21479          * @event push
21480          * Fires when the iframe editor is updated with content from the textarea.
21481          * @param {Roo.HtmlEditorCore} this
21482          * @param {String} html
21483          */
21484         push: true,
21485         
21486         /**
21487          * @event editorevent
21488          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21489          * @param {Roo.HtmlEditorCore} this
21490          */
21491         editorevent: true
21492         
21493     });
21494     
21495     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21496     
21497     // defaults : white / black...
21498     this.applyBlacklists();
21499     
21500     
21501     
21502 };
21503
21504
21505 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21506
21507
21508      /**
21509      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21510      */
21511     
21512     owner : false,
21513     
21514      /**
21515      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21516      *                        Roo.resizable.
21517      */
21518     resizable : false,
21519      /**
21520      * @cfg {Number} height (in pixels)
21521      */   
21522     height: 300,
21523    /**
21524      * @cfg {Number} width (in pixels)
21525      */   
21526     width: 500,
21527     
21528     /**
21529      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21530      * 
21531      */
21532     stylesheets: false,
21533     
21534     // id of frame..
21535     frameId: false,
21536     
21537     // private properties
21538     validationEvent : false,
21539     deferHeight: true,
21540     initialized : false,
21541     activated : false,
21542     sourceEditMode : false,
21543     onFocus : Roo.emptyFn,
21544     iframePad:3,
21545     hideMode:'offsets',
21546     
21547     clearUp: true,
21548     
21549     // blacklist + whitelisted elements..
21550     black: false,
21551     white: false,
21552      
21553     bodyCls : '',
21554
21555     /**
21556      * Protected method that will not generally be called directly. It
21557      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21558      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21559      */
21560     getDocMarkup : function(){
21561         // body styles..
21562         var st = '';
21563         
21564         // inherit styels from page...?? 
21565         if (this.stylesheets === false) {
21566             
21567             Roo.get(document.head).select('style').each(function(node) {
21568                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21569             });
21570             
21571             Roo.get(document.head).select('link').each(function(node) { 
21572                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21573             });
21574             
21575         } else if (!this.stylesheets.length) {
21576                 // simple..
21577                 st = '<style type="text/css">' +
21578                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21579                    '</style>';
21580         } else { 
21581             st = '<style type="text/css">' +
21582                     this.stylesheets +
21583                 '</style>';
21584         }
21585         
21586         st +=  '<style type="text/css">' +
21587             'IMG { cursor: pointer } ' +
21588         '</style>';
21589
21590         var cls = 'roo-htmleditor-body';
21591         
21592         if(this.bodyCls.length){
21593             cls += ' ' + this.bodyCls;
21594         }
21595         
21596         return '<html><head>' + st  +
21597             //<style type="text/css">' +
21598             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21599             //'</style>' +
21600             ' </head><body class="' +  cls + '"></body></html>';
21601     },
21602
21603     // private
21604     onRender : function(ct, position)
21605     {
21606         var _t = this;
21607         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21608         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21609         
21610         
21611         this.el.dom.style.border = '0 none';
21612         this.el.dom.setAttribute('tabIndex', -1);
21613         this.el.addClass('x-hidden hide');
21614         
21615         
21616         
21617         if(Roo.isIE){ // fix IE 1px bogus margin
21618             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21619         }
21620        
21621         
21622         this.frameId = Roo.id();
21623         
21624          
21625         
21626         var iframe = this.owner.wrap.createChild({
21627             tag: 'iframe',
21628             cls: 'form-control', // bootstrap..
21629             id: this.frameId,
21630             name: this.frameId,
21631             frameBorder : 'no',
21632             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21633         }, this.el
21634         );
21635         
21636         
21637         this.iframe = iframe.dom;
21638
21639          this.assignDocWin();
21640         
21641         this.doc.designMode = 'on';
21642        
21643         this.doc.open();
21644         this.doc.write(this.getDocMarkup());
21645         this.doc.close();
21646
21647         
21648         var task = { // must defer to wait for browser to be ready
21649             run : function(){
21650                 //console.log("run task?" + this.doc.readyState);
21651                 this.assignDocWin();
21652                 if(this.doc.body || this.doc.readyState == 'complete'){
21653                     try {
21654                         this.doc.designMode="on";
21655                     } catch (e) {
21656                         return;
21657                     }
21658                     Roo.TaskMgr.stop(task);
21659                     this.initEditor.defer(10, this);
21660                 }
21661             },
21662             interval : 10,
21663             duration: 10000,
21664             scope: this
21665         };
21666         Roo.TaskMgr.start(task);
21667
21668     },
21669
21670     // private
21671     onResize : function(w, h)
21672     {
21673          Roo.log('resize: ' +w + ',' + h );
21674         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21675         if(!this.iframe){
21676             return;
21677         }
21678         if(typeof w == 'number'){
21679             
21680             this.iframe.style.width = w + 'px';
21681         }
21682         if(typeof h == 'number'){
21683             
21684             this.iframe.style.height = h + 'px';
21685             if(this.doc){
21686                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21687             }
21688         }
21689         
21690     },
21691
21692     /**
21693      * Toggles the editor between standard and source edit mode.
21694      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21695      */
21696     toggleSourceEdit : function(sourceEditMode){
21697         
21698         this.sourceEditMode = sourceEditMode === true;
21699         
21700         if(this.sourceEditMode){
21701  
21702             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21703             
21704         }else{
21705             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21706             //this.iframe.className = '';
21707             this.deferFocus();
21708         }
21709         //this.setSize(this.owner.wrap.getSize());
21710         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21711     },
21712
21713     
21714   
21715
21716     /**
21717      * Protected method that will not generally be called directly. If you need/want
21718      * custom HTML cleanup, this is the method you should override.
21719      * @param {String} html The HTML to be cleaned
21720      * return {String} The cleaned HTML
21721      */
21722     cleanHtml : function(html){
21723         html = String(html);
21724         if(html.length > 5){
21725             if(Roo.isSafari){ // strip safari nonsense
21726                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21727             }
21728         }
21729         if(html == '&nbsp;'){
21730             html = '';
21731         }
21732         return html;
21733     },
21734
21735     /**
21736      * HTML Editor -> Textarea
21737      * Protected method that will not generally be called directly. Syncs the contents
21738      * of the editor iframe with the textarea.
21739      */
21740     syncValue : function(){
21741         if(this.initialized){
21742             var bd = (this.doc.body || this.doc.documentElement);
21743             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21744             var html = bd.innerHTML;
21745             if(Roo.isSafari){
21746                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21747                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21748                 if(m && m[1]){
21749                     html = '<div style="'+m[0]+'">' + html + '</div>';
21750                 }
21751             }
21752             html = this.cleanHtml(html);
21753             // fix up the special chars.. normaly like back quotes in word...
21754             // however we do not want to do this with chinese..
21755             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21756                 var cc = b.charCodeAt();
21757                 if (
21758                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21759                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21760                     (cc >= 0xf900 && cc < 0xfb00 )
21761                 ) {
21762                         return b;
21763                 }
21764                 return "&#"+cc+";" 
21765             });
21766             if(this.owner.fireEvent('beforesync', this, html) !== false){
21767                 this.el.dom.value = html;
21768                 this.owner.fireEvent('sync', this, html);
21769             }
21770         }
21771     },
21772
21773     /**
21774      * Protected method that will not generally be called directly. Pushes the value of the textarea
21775      * into the iframe editor.
21776      */
21777     pushValue : function(){
21778         if(this.initialized){
21779             var v = this.el.dom.value.trim();
21780             
21781 //            if(v.length < 1){
21782 //                v = '&#160;';
21783 //            }
21784             
21785             if(this.owner.fireEvent('beforepush', this, v) !== false){
21786                 var d = (this.doc.body || this.doc.documentElement);
21787                 d.innerHTML = v;
21788                 this.cleanUpPaste();
21789                 this.el.dom.value = d.innerHTML;
21790                 this.owner.fireEvent('push', this, v);
21791             }
21792         }
21793     },
21794
21795     // private
21796     deferFocus : function(){
21797         this.focus.defer(10, this);
21798     },
21799
21800     // doc'ed in Field
21801     focus : function(){
21802         if(this.win && !this.sourceEditMode){
21803             this.win.focus();
21804         }else{
21805             this.el.focus();
21806         }
21807     },
21808     
21809     assignDocWin: function()
21810     {
21811         var iframe = this.iframe;
21812         
21813          if(Roo.isIE){
21814             this.doc = iframe.contentWindow.document;
21815             this.win = iframe.contentWindow;
21816         } else {
21817 //            if (!Roo.get(this.frameId)) {
21818 //                return;
21819 //            }
21820 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21821 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21822             
21823             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21824                 return;
21825             }
21826             
21827             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21828             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21829         }
21830     },
21831     
21832     // private
21833     initEditor : function(){
21834         //console.log("INIT EDITOR");
21835         this.assignDocWin();
21836         
21837         
21838         
21839         this.doc.designMode="on";
21840         this.doc.open();
21841         this.doc.write(this.getDocMarkup());
21842         this.doc.close();
21843         
21844         var dbody = (this.doc.body || this.doc.documentElement);
21845         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21846         // this copies styles from the containing element into thsi one..
21847         // not sure why we need all of this..
21848         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21849         
21850         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21851         //ss['background-attachment'] = 'fixed'; // w3c
21852         dbody.bgProperties = 'fixed'; // ie
21853         //Roo.DomHelper.applyStyles(dbody, ss);
21854         Roo.EventManager.on(this.doc, {
21855             //'mousedown': this.onEditorEvent,
21856             'mouseup': this.onEditorEvent,
21857             'dblclick': this.onEditorEvent,
21858             'click': this.onEditorEvent,
21859             'keyup': this.onEditorEvent,
21860             buffer:100,
21861             scope: this
21862         });
21863         if(Roo.isGecko){
21864             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21865         }
21866         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21867             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21868         }
21869         this.initialized = true;
21870
21871         this.owner.fireEvent('initialize', this);
21872         this.pushValue();
21873     },
21874
21875     // private
21876     onDestroy : function(){
21877         
21878         
21879         
21880         if(this.rendered){
21881             
21882             //for (var i =0; i < this.toolbars.length;i++) {
21883             //    // fixme - ask toolbars for heights?
21884             //    this.toolbars[i].onDestroy();
21885            // }
21886             
21887             //this.wrap.dom.innerHTML = '';
21888             //this.wrap.remove();
21889         }
21890     },
21891
21892     // private
21893     onFirstFocus : function(){
21894         
21895         this.assignDocWin();
21896         
21897         
21898         this.activated = true;
21899          
21900     
21901         if(Roo.isGecko){ // prevent silly gecko errors
21902             this.win.focus();
21903             var s = this.win.getSelection();
21904             if(!s.focusNode || s.focusNode.nodeType != 3){
21905                 var r = s.getRangeAt(0);
21906                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21907                 r.collapse(true);
21908                 this.deferFocus();
21909             }
21910             try{
21911                 this.execCmd('useCSS', true);
21912                 this.execCmd('styleWithCSS', false);
21913             }catch(e){}
21914         }
21915         this.owner.fireEvent('activate', this);
21916     },
21917
21918     // private
21919     adjustFont: function(btn){
21920         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21921         //if(Roo.isSafari){ // safari
21922         //    adjust *= 2;
21923        // }
21924         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21925         if(Roo.isSafari){ // safari
21926             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21927             v =  (v < 10) ? 10 : v;
21928             v =  (v > 48) ? 48 : v;
21929             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21930             
21931         }
21932         
21933         
21934         v = Math.max(1, v+adjust);
21935         
21936         this.execCmd('FontSize', v  );
21937     },
21938
21939     onEditorEvent : function(e)
21940     {
21941         this.owner.fireEvent('editorevent', this, e);
21942       //  this.updateToolbar();
21943         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21944     },
21945
21946     insertTag : function(tg)
21947     {
21948         // could be a bit smarter... -> wrap the current selected tRoo..
21949         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21950             
21951             range = this.createRange(this.getSelection());
21952             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21953             wrappingNode.appendChild(range.extractContents());
21954             range.insertNode(wrappingNode);
21955
21956             return;
21957             
21958             
21959             
21960         }
21961         this.execCmd("formatblock",   tg);
21962         
21963     },
21964     
21965     insertText : function(txt)
21966     {
21967         
21968         
21969         var range = this.createRange();
21970         range.deleteContents();
21971                //alert(Sender.getAttribute('label'));
21972                
21973         range.insertNode(this.doc.createTextNode(txt));
21974     } ,
21975     
21976      
21977
21978     /**
21979      * Executes a Midas editor command on the editor document and performs necessary focus and
21980      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21981      * @param {String} cmd The Midas command
21982      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21983      */
21984     relayCmd : function(cmd, value){
21985         this.win.focus();
21986         this.execCmd(cmd, value);
21987         this.owner.fireEvent('editorevent', this);
21988         //this.updateToolbar();
21989         this.owner.deferFocus();
21990     },
21991
21992     /**
21993      * Executes a Midas editor command directly on the editor document.
21994      * For visual commands, you should use {@link #relayCmd} instead.
21995      * <b>This should only be called after the editor is initialized.</b>
21996      * @param {String} cmd The Midas command
21997      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21998      */
21999     execCmd : function(cmd, value){
22000         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22001         this.syncValue();
22002     },
22003  
22004  
22005    
22006     /**
22007      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22008      * to insert tRoo.
22009      * @param {String} text | dom node.. 
22010      */
22011     insertAtCursor : function(text)
22012     {
22013         
22014         if(!this.activated){
22015             return;
22016         }
22017         /*
22018         if(Roo.isIE){
22019             this.win.focus();
22020             var r = this.doc.selection.createRange();
22021             if(r){
22022                 r.collapse(true);
22023                 r.pasteHTML(text);
22024                 this.syncValue();
22025                 this.deferFocus();
22026             
22027             }
22028             return;
22029         }
22030         */
22031         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22032             this.win.focus();
22033             
22034             
22035             // from jquery ui (MIT licenced)
22036             var range, node;
22037             var win = this.win;
22038             
22039             if (win.getSelection && win.getSelection().getRangeAt) {
22040                 range = win.getSelection().getRangeAt(0);
22041                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22042                 range.insertNode(node);
22043             } else if (win.document.selection && win.document.selection.createRange) {
22044                 // no firefox support
22045                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22046                 win.document.selection.createRange().pasteHTML(txt);
22047             } else {
22048                 // no firefox support
22049                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22050                 this.execCmd('InsertHTML', txt);
22051             } 
22052             
22053             this.syncValue();
22054             
22055             this.deferFocus();
22056         }
22057     },
22058  // private
22059     mozKeyPress : function(e){
22060         if(e.ctrlKey){
22061             var c = e.getCharCode(), cmd;
22062           
22063             if(c > 0){
22064                 c = String.fromCharCode(c).toLowerCase();
22065                 switch(c){
22066                     case 'b':
22067                         cmd = 'bold';
22068                         break;
22069                     case 'i':
22070                         cmd = 'italic';
22071                         break;
22072                     
22073                     case 'u':
22074                         cmd = 'underline';
22075                         break;
22076                     
22077                     case 'v':
22078                         this.cleanUpPaste.defer(100, this);
22079                         return;
22080                         
22081                 }
22082                 if(cmd){
22083                     this.win.focus();
22084                     this.execCmd(cmd);
22085                     this.deferFocus();
22086                     e.preventDefault();
22087                 }
22088                 
22089             }
22090         }
22091     },
22092
22093     // private
22094     fixKeys : function(){ // load time branching for fastest keydown performance
22095         if(Roo.isIE){
22096             return function(e){
22097                 var k = e.getKey(), r;
22098                 if(k == e.TAB){
22099                     e.stopEvent();
22100                     r = this.doc.selection.createRange();
22101                     if(r){
22102                         r.collapse(true);
22103                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22104                         this.deferFocus();
22105                     }
22106                     return;
22107                 }
22108                 
22109                 if(k == e.ENTER){
22110                     r = this.doc.selection.createRange();
22111                     if(r){
22112                         var target = r.parentElement();
22113                         if(!target || target.tagName.toLowerCase() != 'li'){
22114                             e.stopEvent();
22115                             r.pasteHTML('<br />');
22116                             r.collapse(false);
22117                             r.select();
22118                         }
22119                     }
22120                 }
22121                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22122                     this.cleanUpPaste.defer(100, this);
22123                     return;
22124                 }
22125                 
22126                 
22127             };
22128         }else if(Roo.isOpera){
22129             return function(e){
22130                 var k = e.getKey();
22131                 if(k == e.TAB){
22132                     e.stopEvent();
22133                     this.win.focus();
22134                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22135                     this.deferFocus();
22136                 }
22137                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22138                     this.cleanUpPaste.defer(100, this);
22139                     return;
22140                 }
22141                 
22142             };
22143         }else if(Roo.isSafari){
22144             return function(e){
22145                 var k = e.getKey();
22146                 
22147                 if(k == e.TAB){
22148                     e.stopEvent();
22149                     this.execCmd('InsertText','\t');
22150                     this.deferFocus();
22151                     return;
22152                 }
22153                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22154                     this.cleanUpPaste.defer(100, this);
22155                     return;
22156                 }
22157                 
22158              };
22159         }
22160     }(),
22161     
22162     getAllAncestors: function()
22163     {
22164         var p = this.getSelectedNode();
22165         var a = [];
22166         if (!p) {
22167             a.push(p); // push blank onto stack..
22168             p = this.getParentElement();
22169         }
22170         
22171         
22172         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22173             a.push(p);
22174             p = p.parentNode;
22175         }
22176         a.push(this.doc.body);
22177         return a;
22178     },
22179     lastSel : false,
22180     lastSelNode : false,
22181     
22182     
22183     getSelection : function() 
22184     {
22185         this.assignDocWin();
22186         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22187     },
22188     
22189     getSelectedNode: function() 
22190     {
22191         // this may only work on Gecko!!!
22192         
22193         // should we cache this!!!!
22194         
22195         
22196         
22197          
22198         var range = this.createRange(this.getSelection()).cloneRange();
22199         
22200         if (Roo.isIE) {
22201             var parent = range.parentElement();
22202             while (true) {
22203                 var testRange = range.duplicate();
22204                 testRange.moveToElementText(parent);
22205                 if (testRange.inRange(range)) {
22206                     break;
22207                 }
22208                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22209                     break;
22210                 }
22211                 parent = parent.parentElement;
22212             }
22213             return parent;
22214         }
22215         
22216         // is ancestor a text element.
22217         var ac =  range.commonAncestorContainer;
22218         if (ac.nodeType == 3) {
22219             ac = ac.parentNode;
22220         }
22221         
22222         var ar = ac.childNodes;
22223          
22224         var nodes = [];
22225         var other_nodes = [];
22226         var has_other_nodes = false;
22227         for (var i=0;i<ar.length;i++) {
22228             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22229                 continue;
22230             }
22231             // fullly contained node.
22232             
22233             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22234                 nodes.push(ar[i]);
22235                 continue;
22236             }
22237             
22238             // probably selected..
22239             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22240                 other_nodes.push(ar[i]);
22241                 continue;
22242             }
22243             // outer..
22244             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22245                 continue;
22246             }
22247             
22248             
22249             has_other_nodes = true;
22250         }
22251         if (!nodes.length && other_nodes.length) {
22252             nodes= other_nodes;
22253         }
22254         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22255             return false;
22256         }
22257         
22258         return nodes[0];
22259     },
22260     createRange: function(sel)
22261     {
22262         // this has strange effects when using with 
22263         // top toolbar - not sure if it's a great idea.
22264         //this.editor.contentWindow.focus();
22265         if (typeof sel != "undefined") {
22266             try {
22267                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22268             } catch(e) {
22269                 return this.doc.createRange();
22270             }
22271         } else {
22272             return this.doc.createRange();
22273         }
22274     },
22275     getParentElement: function()
22276     {
22277         
22278         this.assignDocWin();
22279         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22280         
22281         var range = this.createRange(sel);
22282          
22283         try {
22284             var p = range.commonAncestorContainer;
22285             while (p.nodeType == 3) { // text node
22286                 p = p.parentNode;
22287             }
22288             return p;
22289         } catch (e) {
22290             return null;
22291         }
22292     
22293     },
22294     /***
22295      *
22296      * Range intersection.. the hard stuff...
22297      *  '-1' = before
22298      *  '0' = hits..
22299      *  '1' = after.
22300      *         [ -- selected range --- ]
22301      *   [fail]                        [fail]
22302      *
22303      *    basically..
22304      *      if end is before start or  hits it. fail.
22305      *      if start is after end or hits it fail.
22306      *
22307      *   if either hits (but other is outside. - then it's not 
22308      *   
22309      *    
22310      **/
22311     
22312     
22313     // @see http://www.thismuchiknow.co.uk/?p=64.
22314     rangeIntersectsNode : function(range, node)
22315     {
22316         var nodeRange = node.ownerDocument.createRange();
22317         try {
22318             nodeRange.selectNode(node);
22319         } catch (e) {
22320             nodeRange.selectNodeContents(node);
22321         }
22322     
22323         var rangeStartRange = range.cloneRange();
22324         rangeStartRange.collapse(true);
22325     
22326         var rangeEndRange = range.cloneRange();
22327         rangeEndRange.collapse(false);
22328     
22329         var nodeStartRange = nodeRange.cloneRange();
22330         nodeStartRange.collapse(true);
22331     
22332         var nodeEndRange = nodeRange.cloneRange();
22333         nodeEndRange.collapse(false);
22334     
22335         return rangeStartRange.compareBoundaryPoints(
22336                  Range.START_TO_START, nodeEndRange) == -1 &&
22337                rangeEndRange.compareBoundaryPoints(
22338                  Range.START_TO_START, nodeStartRange) == 1;
22339         
22340          
22341     },
22342     rangeCompareNode : function(range, node)
22343     {
22344         var nodeRange = node.ownerDocument.createRange();
22345         try {
22346             nodeRange.selectNode(node);
22347         } catch (e) {
22348             nodeRange.selectNodeContents(node);
22349         }
22350         
22351         
22352         range.collapse(true);
22353     
22354         nodeRange.collapse(true);
22355      
22356         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22357         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22358          
22359         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22360         
22361         var nodeIsBefore   =  ss == 1;
22362         var nodeIsAfter    = ee == -1;
22363         
22364         if (nodeIsBefore && nodeIsAfter) {
22365             return 0; // outer
22366         }
22367         if (!nodeIsBefore && nodeIsAfter) {
22368             return 1; //right trailed.
22369         }
22370         
22371         if (nodeIsBefore && !nodeIsAfter) {
22372             return 2;  // left trailed.
22373         }
22374         // fully contined.
22375         return 3;
22376     },
22377
22378     // private? - in a new class?
22379     cleanUpPaste :  function()
22380     {
22381         // cleans up the whole document..
22382         Roo.log('cleanuppaste');
22383         
22384         this.cleanUpChildren(this.doc.body);
22385         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22386         if (clean != this.doc.body.innerHTML) {
22387             this.doc.body.innerHTML = clean;
22388         }
22389         
22390     },
22391     
22392     cleanWordChars : function(input) {// change the chars to hex code
22393         var he = Roo.HtmlEditorCore;
22394         
22395         var output = input;
22396         Roo.each(he.swapCodes, function(sw) { 
22397             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22398             
22399             output = output.replace(swapper, sw[1]);
22400         });
22401         
22402         return output;
22403     },
22404     
22405     
22406     cleanUpChildren : function (n)
22407     {
22408         if (!n.childNodes.length) {
22409             return;
22410         }
22411         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22412            this.cleanUpChild(n.childNodes[i]);
22413         }
22414     },
22415     
22416     
22417         
22418     
22419     cleanUpChild : function (node)
22420     {
22421         var ed = this;
22422         //console.log(node);
22423         if (node.nodeName == "#text") {
22424             // clean up silly Windows -- stuff?
22425             return; 
22426         }
22427         if (node.nodeName == "#comment") {
22428             node.parentNode.removeChild(node);
22429             // clean up silly Windows -- stuff?
22430             return; 
22431         }
22432         var lcname = node.tagName.toLowerCase();
22433         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22434         // whitelist of tags..
22435         
22436         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22437             // remove node.
22438             node.parentNode.removeChild(node);
22439             return;
22440             
22441         }
22442         
22443         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22444         
22445         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22446         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22447         
22448         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22449         //    remove_keep_children = true;
22450         //}
22451         
22452         if (remove_keep_children) {
22453             this.cleanUpChildren(node);
22454             // inserts everything just before this node...
22455             while (node.childNodes.length) {
22456                 var cn = node.childNodes[0];
22457                 node.removeChild(cn);
22458                 node.parentNode.insertBefore(cn, node);
22459             }
22460             node.parentNode.removeChild(node);
22461             return;
22462         }
22463         
22464         if (!node.attributes || !node.attributes.length) {
22465             this.cleanUpChildren(node);
22466             return;
22467         }
22468         
22469         function cleanAttr(n,v)
22470         {
22471             
22472             if (v.match(/^\./) || v.match(/^\//)) {
22473                 return;
22474             }
22475             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22476                 return;
22477             }
22478             if (v.match(/^#/)) {
22479                 return;
22480             }
22481 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22482             node.removeAttribute(n);
22483             
22484         }
22485         
22486         var cwhite = this.cwhite;
22487         var cblack = this.cblack;
22488             
22489         function cleanStyle(n,v)
22490         {
22491             if (v.match(/expression/)) { //XSS?? should we even bother..
22492                 node.removeAttribute(n);
22493                 return;
22494             }
22495             
22496             var parts = v.split(/;/);
22497             var clean = [];
22498             
22499             Roo.each(parts, function(p) {
22500                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22501                 if (!p.length) {
22502                     return true;
22503                 }
22504                 var l = p.split(':').shift().replace(/\s+/g,'');
22505                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22506                 
22507                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22508 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22509                     //node.removeAttribute(n);
22510                     return true;
22511                 }
22512                 //Roo.log()
22513                 // only allow 'c whitelisted system attributes'
22514                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22515 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22516                     //node.removeAttribute(n);
22517                     return true;
22518                 }
22519                 
22520                 
22521                  
22522                 
22523                 clean.push(p);
22524                 return true;
22525             });
22526             if (clean.length) { 
22527                 node.setAttribute(n, clean.join(';'));
22528             } else {
22529                 node.removeAttribute(n);
22530             }
22531             
22532         }
22533         
22534         
22535         for (var i = node.attributes.length-1; i > -1 ; i--) {
22536             var a = node.attributes[i];
22537             //console.log(a);
22538             
22539             if (a.name.toLowerCase().substr(0,2)=='on')  {
22540                 node.removeAttribute(a.name);
22541                 continue;
22542             }
22543             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22544                 node.removeAttribute(a.name);
22545                 continue;
22546             }
22547             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22548                 cleanAttr(a.name,a.value); // fixme..
22549                 continue;
22550             }
22551             if (a.name == 'style') {
22552                 cleanStyle(a.name,a.value);
22553                 continue;
22554             }
22555             /// clean up MS crap..
22556             // tecnically this should be a list of valid class'es..
22557             
22558             
22559             if (a.name == 'class') {
22560                 if (a.value.match(/^Mso/)) {
22561                     node.className = '';
22562                 }
22563                 
22564                 if (a.value.match(/^body$/)) {
22565                     node.className = '';
22566                 }
22567                 continue;
22568             }
22569             
22570             // style cleanup!?
22571             // class cleanup?
22572             
22573         }
22574         
22575         
22576         this.cleanUpChildren(node);
22577         
22578         
22579     },
22580     
22581     /**
22582      * Clean up MS wordisms...
22583      */
22584     cleanWord : function(node)
22585     {
22586         
22587         
22588         if (!node) {
22589             this.cleanWord(this.doc.body);
22590             return;
22591         }
22592         if (node.nodeName == "#text") {
22593             // clean up silly Windows -- stuff?
22594             return; 
22595         }
22596         if (node.nodeName == "#comment") {
22597             node.parentNode.removeChild(node);
22598             // clean up silly Windows -- stuff?
22599             return; 
22600         }
22601         
22602         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22603             node.parentNode.removeChild(node);
22604             return;
22605         }
22606         
22607         // remove - but keep children..
22608         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22609             while (node.childNodes.length) {
22610                 var cn = node.childNodes[0];
22611                 node.removeChild(cn);
22612                 node.parentNode.insertBefore(cn, node);
22613             }
22614             node.parentNode.removeChild(node);
22615             this.iterateChildren(node, this.cleanWord);
22616             return;
22617         }
22618         // clean styles
22619         if (node.className.length) {
22620             
22621             var cn = node.className.split(/\W+/);
22622             var cna = [];
22623             Roo.each(cn, function(cls) {
22624                 if (cls.match(/Mso[a-zA-Z]+/)) {
22625                     return;
22626                 }
22627                 cna.push(cls);
22628             });
22629             node.className = cna.length ? cna.join(' ') : '';
22630             if (!cna.length) {
22631                 node.removeAttribute("class");
22632             }
22633         }
22634         
22635         if (node.hasAttribute("lang")) {
22636             node.removeAttribute("lang");
22637         }
22638         
22639         if (node.hasAttribute("style")) {
22640             
22641             var styles = node.getAttribute("style").split(";");
22642             var nstyle = [];
22643             Roo.each(styles, function(s) {
22644                 if (!s.match(/:/)) {
22645                     return;
22646                 }
22647                 var kv = s.split(":");
22648                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22649                     return;
22650                 }
22651                 // what ever is left... we allow.
22652                 nstyle.push(s);
22653             });
22654             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22655             if (!nstyle.length) {
22656                 node.removeAttribute('style');
22657             }
22658         }
22659         this.iterateChildren(node, this.cleanWord);
22660         
22661         
22662         
22663     },
22664     /**
22665      * iterateChildren of a Node, calling fn each time, using this as the scole..
22666      * @param {DomNode} node node to iterate children of.
22667      * @param {Function} fn method of this class to call on each item.
22668      */
22669     iterateChildren : function(node, fn)
22670     {
22671         if (!node.childNodes.length) {
22672                 return;
22673         }
22674         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22675            fn.call(this, node.childNodes[i])
22676         }
22677     },
22678     
22679     
22680     /**
22681      * cleanTableWidths.
22682      *
22683      * Quite often pasting from word etc.. results in tables with column and widths.
22684      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22685      *
22686      */
22687     cleanTableWidths : function(node)
22688     {
22689          
22690          
22691         if (!node) {
22692             this.cleanTableWidths(this.doc.body);
22693             return;
22694         }
22695         
22696         // ignore list...
22697         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22698             return; 
22699         }
22700         Roo.log(node.tagName);
22701         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22702             this.iterateChildren(node, this.cleanTableWidths);
22703             return;
22704         }
22705         if (node.hasAttribute('width')) {
22706             node.removeAttribute('width');
22707         }
22708         
22709          
22710         if (node.hasAttribute("style")) {
22711             // pretty basic...
22712             
22713             var styles = node.getAttribute("style").split(";");
22714             var nstyle = [];
22715             Roo.each(styles, function(s) {
22716                 if (!s.match(/:/)) {
22717                     return;
22718                 }
22719                 var kv = s.split(":");
22720                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22721                     return;
22722                 }
22723                 // what ever is left... we allow.
22724                 nstyle.push(s);
22725             });
22726             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22727             if (!nstyle.length) {
22728                 node.removeAttribute('style');
22729             }
22730         }
22731         
22732         this.iterateChildren(node, this.cleanTableWidths);
22733         
22734         
22735     },
22736     
22737     
22738     
22739     
22740     domToHTML : function(currentElement, depth, nopadtext) {
22741         
22742         depth = depth || 0;
22743         nopadtext = nopadtext || false;
22744     
22745         if (!currentElement) {
22746             return this.domToHTML(this.doc.body);
22747         }
22748         
22749         //Roo.log(currentElement);
22750         var j;
22751         var allText = false;
22752         var nodeName = currentElement.nodeName;
22753         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22754         
22755         if  (nodeName == '#text') {
22756             
22757             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22758         }
22759         
22760         
22761         var ret = '';
22762         if (nodeName != 'BODY') {
22763              
22764             var i = 0;
22765             // Prints the node tagName, such as <A>, <IMG>, etc
22766             if (tagName) {
22767                 var attr = [];
22768                 for(i = 0; i < currentElement.attributes.length;i++) {
22769                     // quoting?
22770                     var aname = currentElement.attributes.item(i).name;
22771                     if (!currentElement.attributes.item(i).value.length) {
22772                         continue;
22773                     }
22774                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22775                 }
22776                 
22777                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22778             } 
22779             else {
22780                 
22781                 // eack
22782             }
22783         } else {
22784             tagName = false;
22785         }
22786         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22787             return ret;
22788         }
22789         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22790             nopadtext = true;
22791         }
22792         
22793         
22794         // Traverse the tree
22795         i = 0;
22796         var currentElementChild = currentElement.childNodes.item(i);
22797         var allText = true;
22798         var innerHTML  = '';
22799         lastnode = '';
22800         while (currentElementChild) {
22801             // Formatting code (indent the tree so it looks nice on the screen)
22802             var nopad = nopadtext;
22803             if (lastnode == 'SPAN') {
22804                 nopad  = true;
22805             }
22806             // text
22807             if  (currentElementChild.nodeName == '#text') {
22808                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22809                 toadd = nopadtext ? toadd : toadd.trim();
22810                 if (!nopad && toadd.length > 80) {
22811                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22812                 }
22813                 innerHTML  += toadd;
22814                 
22815                 i++;
22816                 currentElementChild = currentElement.childNodes.item(i);
22817                 lastNode = '';
22818                 continue;
22819             }
22820             allText = false;
22821             
22822             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22823                 
22824             // Recursively traverse the tree structure of the child node
22825             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22826             lastnode = currentElementChild.nodeName;
22827             i++;
22828             currentElementChild=currentElement.childNodes.item(i);
22829         }
22830         
22831         ret += innerHTML;
22832         
22833         if (!allText) {
22834                 // The remaining code is mostly for formatting the tree
22835             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22836         }
22837         
22838         
22839         if (tagName) {
22840             ret+= "</"+tagName+">";
22841         }
22842         return ret;
22843         
22844     },
22845         
22846     applyBlacklists : function()
22847     {
22848         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22849         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22850         
22851         this.white = [];
22852         this.black = [];
22853         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22854             if (b.indexOf(tag) > -1) {
22855                 return;
22856             }
22857             this.white.push(tag);
22858             
22859         }, this);
22860         
22861         Roo.each(w, function(tag) {
22862             if (b.indexOf(tag) > -1) {
22863                 return;
22864             }
22865             if (this.white.indexOf(tag) > -1) {
22866                 return;
22867             }
22868             this.white.push(tag);
22869             
22870         }, this);
22871         
22872         
22873         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22874             if (w.indexOf(tag) > -1) {
22875                 return;
22876             }
22877             this.black.push(tag);
22878             
22879         }, this);
22880         
22881         Roo.each(b, function(tag) {
22882             if (w.indexOf(tag) > -1) {
22883                 return;
22884             }
22885             if (this.black.indexOf(tag) > -1) {
22886                 return;
22887             }
22888             this.black.push(tag);
22889             
22890         }, this);
22891         
22892         
22893         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22894         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22895         
22896         this.cwhite = [];
22897         this.cblack = [];
22898         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22899             if (b.indexOf(tag) > -1) {
22900                 return;
22901             }
22902             this.cwhite.push(tag);
22903             
22904         }, this);
22905         
22906         Roo.each(w, function(tag) {
22907             if (b.indexOf(tag) > -1) {
22908                 return;
22909             }
22910             if (this.cwhite.indexOf(tag) > -1) {
22911                 return;
22912             }
22913             this.cwhite.push(tag);
22914             
22915         }, this);
22916         
22917         
22918         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22919             if (w.indexOf(tag) > -1) {
22920                 return;
22921             }
22922             this.cblack.push(tag);
22923             
22924         }, this);
22925         
22926         Roo.each(b, function(tag) {
22927             if (w.indexOf(tag) > -1) {
22928                 return;
22929             }
22930             if (this.cblack.indexOf(tag) > -1) {
22931                 return;
22932             }
22933             this.cblack.push(tag);
22934             
22935         }, this);
22936     },
22937     
22938     setStylesheets : function(stylesheets)
22939     {
22940         if(typeof(stylesheets) == 'string'){
22941             Roo.get(this.iframe.contentDocument.head).createChild({
22942                 tag : 'link',
22943                 rel : 'stylesheet',
22944                 type : 'text/css',
22945                 href : stylesheets
22946             });
22947             
22948             return;
22949         }
22950         var _this = this;
22951      
22952         Roo.each(stylesheets, function(s) {
22953             if(!s.length){
22954                 return;
22955             }
22956             
22957             Roo.get(_this.iframe.contentDocument.head).createChild({
22958                 tag : 'link',
22959                 rel : 'stylesheet',
22960                 type : 'text/css',
22961                 href : s
22962             });
22963         });
22964
22965         
22966     },
22967     
22968     removeStylesheets : function()
22969     {
22970         var _this = this;
22971         
22972         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22973             s.remove();
22974         });
22975     },
22976     
22977     setStyle : function(style)
22978     {
22979         Roo.get(this.iframe.contentDocument.head).createChild({
22980             tag : 'style',
22981             type : 'text/css',
22982             html : style
22983         });
22984
22985         return;
22986     }
22987     
22988     // hide stuff that is not compatible
22989     /**
22990      * @event blur
22991      * @hide
22992      */
22993     /**
22994      * @event change
22995      * @hide
22996      */
22997     /**
22998      * @event focus
22999      * @hide
23000      */
23001     /**
23002      * @event specialkey
23003      * @hide
23004      */
23005     /**
23006      * @cfg {String} fieldClass @hide
23007      */
23008     /**
23009      * @cfg {String} focusClass @hide
23010      */
23011     /**
23012      * @cfg {String} autoCreate @hide
23013      */
23014     /**
23015      * @cfg {String} inputType @hide
23016      */
23017     /**
23018      * @cfg {String} invalidClass @hide
23019      */
23020     /**
23021      * @cfg {String} invalidText @hide
23022      */
23023     /**
23024      * @cfg {String} msgFx @hide
23025      */
23026     /**
23027      * @cfg {String} validateOnBlur @hide
23028      */
23029 });
23030
23031 Roo.HtmlEditorCore.white = [
23032         'area', 'br', 'img', 'input', 'hr', 'wbr',
23033         
23034        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23035        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23036        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23037        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23038        'table',   'ul',         'xmp', 
23039        
23040        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23041       'thead',   'tr', 
23042      
23043       'dir', 'menu', 'ol', 'ul', 'dl',
23044        
23045       'embed',  'object'
23046 ];
23047
23048
23049 Roo.HtmlEditorCore.black = [
23050     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23051         'applet', // 
23052         'base',   'basefont', 'bgsound', 'blink',  'body', 
23053         'frame',  'frameset', 'head',    'html',   'ilayer', 
23054         'iframe', 'layer',  'link',     'meta',    'object',   
23055         'script', 'style' ,'title',  'xml' // clean later..
23056 ];
23057 Roo.HtmlEditorCore.clean = [
23058     'script', 'style', 'title', 'xml'
23059 ];
23060 Roo.HtmlEditorCore.remove = [
23061     'font'
23062 ];
23063 // attributes..
23064
23065 Roo.HtmlEditorCore.ablack = [
23066     'on'
23067 ];
23068     
23069 Roo.HtmlEditorCore.aclean = [ 
23070     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23071 ];
23072
23073 // protocols..
23074 Roo.HtmlEditorCore.pwhite= [
23075         'http',  'https',  'mailto'
23076 ];
23077
23078 // white listed style attributes.
23079 Roo.HtmlEditorCore.cwhite= [
23080       //  'text-align', /// default is to allow most things..
23081       
23082          
23083 //        'font-size'//??
23084 ];
23085
23086 // black listed style attributes.
23087 Roo.HtmlEditorCore.cblack= [
23088       //  'font-size' -- this can be set by the project 
23089 ];
23090
23091
23092 Roo.HtmlEditorCore.swapCodes   =[ 
23093     [    8211, "--" ], 
23094     [    8212, "--" ], 
23095     [    8216,  "'" ],  
23096     [    8217, "'" ],  
23097     [    8220, '"' ],  
23098     [    8221, '"' ],  
23099     [    8226, "*" ],  
23100     [    8230, "..." ]
23101 ]; 
23102
23103     /*
23104  * - LGPL
23105  *
23106  * HtmlEditor
23107  * 
23108  */
23109
23110 /**
23111  * @class Roo.bootstrap.HtmlEditor
23112  * @extends Roo.bootstrap.TextArea
23113  * Bootstrap HtmlEditor class
23114
23115  * @constructor
23116  * Create a new HtmlEditor
23117  * @param {Object} config The config object
23118  */
23119
23120 Roo.bootstrap.HtmlEditor = function(config){
23121     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23122     if (!this.toolbars) {
23123         this.toolbars = [];
23124     }
23125     
23126     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23127     this.addEvents({
23128             /**
23129              * @event initialize
23130              * Fires when the editor is fully initialized (including the iframe)
23131              * @param {HtmlEditor} this
23132              */
23133             initialize: true,
23134             /**
23135              * @event activate
23136              * Fires when the editor is first receives the focus. Any insertion must wait
23137              * until after this event.
23138              * @param {HtmlEditor} this
23139              */
23140             activate: true,
23141              /**
23142              * @event beforesync
23143              * Fires before the textarea is updated with content from the editor iframe. Return false
23144              * to cancel the sync.
23145              * @param {HtmlEditor} this
23146              * @param {String} html
23147              */
23148             beforesync: true,
23149              /**
23150              * @event beforepush
23151              * Fires before the iframe editor is updated with content from the textarea. Return false
23152              * to cancel the push.
23153              * @param {HtmlEditor} this
23154              * @param {String} html
23155              */
23156             beforepush: true,
23157              /**
23158              * @event sync
23159              * Fires when the textarea is updated with content from the editor iframe.
23160              * @param {HtmlEditor} this
23161              * @param {String} html
23162              */
23163             sync: true,
23164              /**
23165              * @event push
23166              * Fires when the iframe editor is updated with content from the textarea.
23167              * @param {HtmlEditor} this
23168              * @param {String} html
23169              */
23170             push: true,
23171              /**
23172              * @event editmodechange
23173              * Fires when the editor switches edit modes
23174              * @param {HtmlEditor} this
23175              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23176              */
23177             editmodechange: true,
23178             /**
23179              * @event editorevent
23180              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23181              * @param {HtmlEditor} this
23182              */
23183             editorevent: true,
23184             /**
23185              * @event firstfocus
23186              * Fires when on first focus - needed by toolbars..
23187              * @param {HtmlEditor} this
23188              */
23189             firstfocus: true,
23190             /**
23191              * @event autosave
23192              * Auto save the htmlEditor value as a file into Events
23193              * @param {HtmlEditor} this
23194              */
23195             autosave: true,
23196             /**
23197              * @event savedpreview
23198              * preview the saved version of htmlEditor
23199              * @param {HtmlEditor} this
23200              */
23201             savedpreview: true
23202         });
23203 };
23204
23205
23206 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23207     
23208     
23209       /**
23210      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23211      */
23212     toolbars : false,
23213     
23214      /**
23215     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23216     */
23217     btns : [],
23218    
23219      /**
23220      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23221      *                        Roo.resizable.
23222      */
23223     resizable : false,
23224      /**
23225      * @cfg {Number} height (in pixels)
23226      */   
23227     height: 300,
23228    /**
23229      * @cfg {Number} width (in pixels)
23230      */   
23231     width: false,
23232     
23233     /**
23234      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23235      * 
23236      */
23237     stylesheets: false,
23238     
23239     // id of frame..
23240     frameId: false,
23241     
23242     // private properties
23243     validationEvent : false,
23244     deferHeight: true,
23245     initialized : false,
23246     activated : false,
23247     
23248     onFocus : Roo.emptyFn,
23249     iframePad:3,
23250     hideMode:'offsets',
23251     
23252     tbContainer : false,
23253     
23254     bodyCls : '',
23255     
23256     toolbarContainer :function() {
23257         return this.wrap.select('.x-html-editor-tb',true).first();
23258     },
23259
23260     /**
23261      * Protected method that will not generally be called directly. It
23262      * is called when the editor creates its toolbar. Override this method if you need to
23263      * add custom toolbar buttons.
23264      * @param {HtmlEditor} editor
23265      */
23266     createToolbar : function(){
23267         Roo.log('renewing');
23268         Roo.log("create toolbars");
23269         
23270         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23271         this.toolbars[0].render(this.toolbarContainer());
23272         
23273         return;
23274         
23275 //        if (!editor.toolbars || !editor.toolbars.length) {
23276 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23277 //        }
23278 //        
23279 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23280 //            editor.toolbars[i] = Roo.factory(
23281 //                    typeof(editor.toolbars[i]) == 'string' ?
23282 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23283 //                Roo.bootstrap.HtmlEditor);
23284 //            editor.toolbars[i].init(editor);
23285 //        }
23286     },
23287
23288      
23289     // private
23290     onRender : function(ct, position)
23291     {
23292        // Roo.log("Call onRender: " + this.xtype);
23293         var _t = this;
23294         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23295       
23296         this.wrap = this.inputEl().wrap({
23297             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23298         });
23299         
23300         this.editorcore.onRender(ct, position);
23301          
23302         if (this.resizable) {
23303             this.resizeEl = new Roo.Resizable(this.wrap, {
23304                 pinned : true,
23305                 wrap: true,
23306                 dynamic : true,
23307                 minHeight : this.height,
23308                 height: this.height,
23309                 handles : this.resizable,
23310                 width: this.width,
23311                 listeners : {
23312                     resize : function(r, w, h) {
23313                         _t.onResize(w,h); // -something
23314                     }
23315                 }
23316             });
23317             
23318         }
23319         this.createToolbar(this);
23320        
23321         
23322         if(!this.width && this.resizable){
23323             this.setSize(this.wrap.getSize());
23324         }
23325         if (this.resizeEl) {
23326             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23327             // should trigger onReize..
23328         }
23329         
23330     },
23331
23332     // private
23333     onResize : function(w, h)
23334     {
23335         Roo.log('resize: ' +w + ',' + h );
23336         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23337         var ew = false;
23338         var eh = false;
23339         
23340         if(this.inputEl() ){
23341             if(typeof w == 'number'){
23342                 var aw = w - this.wrap.getFrameWidth('lr');
23343                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23344                 ew = aw;
23345             }
23346             if(typeof h == 'number'){
23347                  var tbh = -11;  // fixme it needs to tool bar size!
23348                 for (var i =0; i < this.toolbars.length;i++) {
23349                     // fixme - ask toolbars for heights?
23350                     tbh += this.toolbars[i].el.getHeight();
23351                     //if (this.toolbars[i].footer) {
23352                     //    tbh += this.toolbars[i].footer.el.getHeight();
23353                     //}
23354                 }
23355               
23356                 
23357                 
23358                 
23359                 
23360                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23361                 ah -= 5; // knock a few pixes off for look..
23362                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23363                 var eh = ah;
23364             }
23365         }
23366         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23367         this.editorcore.onResize(ew,eh);
23368         
23369     },
23370
23371     /**
23372      * Toggles the editor between standard and source edit mode.
23373      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23374      */
23375     toggleSourceEdit : function(sourceEditMode)
23376     {
23377         this.editorcore.toggleSourceEdit(sourceEditMode);
23378         
23379         if(this.editorcore.sourceEditMode){
23380             Roo.log('editor - showing textarea');
23381             
23382 //            Roo.log('in');
23383 //            Roo.log(this.syncValue());
23384             this.syncValue();
23385             this.inputEl().removeClass(['hide', 'x-hidden']);
23386             this.inputEl().dom.removeAttribute('tabIndex');
23387             this.inputEl().focus();
23388         }else{
23389             Roo.log('editor - hiding textarea');
23390 //            Roo.log('out')
23391 //            Roo.log(this.pushValue()); 
23392             this.pushValue();
23393             
23394             this.inputEl().addClass(['hide', 'x-hidden']);
23395             this.inputEl().dom.setAttribute('tabIndex', -1);
23396             //this.deferFocus();
23397         }
23398          
23399         if(this.resizable){
23400             this.setSize(this.wrap.getSize());
23401         }
23402         
23403         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23404     },
23405  
23406     // private (for BoxComponent)
23407     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23408
23409     // private (for BoxComponent)
23410     getResizeEl : function(){
23411         return this.wrap;
23412     },
23413
23414     // private (for BoxComponent)
23415     getPositionEl : function(){
23416         return this.wrap;
23417     },
23418
23419     // private
23420     initEvents : function(){
23421         this.originalValue = this.getValue();
23422     },
23423
23424 //    /**
23425 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23426 //     * @method
23427 //     */
23428 //    markInvalid : Roo.emptyFn,
23429 //    /**
23430 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23431 //     * @method
23432 //     */
23433 //    clearInvalid : Roo.emptyFn,
23434
23435     setValue : function(v){
23436         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23437         this.editorcore.pushValue();
23438     },
23439
23440      
23441     // private
23442     deferFocus : function(){
23443         this.focus.defer(10, this);
23444     },
23445
23446     // doc'ed in Field
23447     focus : function(){
23448         this.editorcore.focus();
23449         
23450     },
23451       
23452
23453     // private
23454     onDestroy : function(){
23455         
23456         
23457         
23458         if(this.rendered){
23459             
23460             for (var i =0; i < this.toolbars.length;i++) {
23461                 // fixme - ask toolbars for heights?
23462                 this.toolbars[i].onDestroy();
23463             }
23464             
23465             this.wrap.dom.innerHTML = '';
23466             this.wrap.remove();
23467         }
23468     },
23469
23470     // private
23471     onFirstFocus : function(){
23472         //Roo.log("onFirstFocus");
23473         this.editorcore.onFirstFocus();
23474          for (var i =0; i < this.toolbars.length;i++) {
23475             this.toolbars[i].onFirstFocus();
23476         }
23477         
23478     },
23479     
23480     // private
23481     syncValue : function()
23482     {   
23483         this.editorcore.syncValue();
23484     },
23485     
23486     pushValue : function()
23487     {   
23488         this.editorcore.pushValue();
23489     }
23490      
23491     
23492     // hide stuff that is not compatible
23493     /**
23494      * @event blur
23495      * @hide
23496      */
23497     /**
23498      * @event change
23499      * @hide
23500      */
23501     /**
23502      * @event focus
23503      * @hide
23504      */
23505     /**
23506      * @event specialkey
23507      * @hide
23508      */
23509     /**
23510      * @cfg {String} fieldClass @hide
23511      */
23512     /**
23513      * @cfg {String} focusClass @hide
23514      */
23515     /**
23516      * @cfg {String} autoCreate @hide
23517      */
23518     /**
23519      * @cfg {String} inputType @hide
23520      */
23521     /**
23522      * @cfg {String} invalidClass @hide
23523      */
23524     /**
23525      * @cfg {String} invalidText @hide
23526      */
23527     /**
23528      * @cfg {String} msgFx @hide
23529      */
23530     /**
23531      * @cfg {String} validateOnBlur @hide
23532      */
23533 });
23534  
23535     
23536    
23537    
23538    
23539       
23540 Roo.namespace('Roo.bootstrap.htmleditor');
23541 /**
23542  * @class Roo.bootstrap.HtmlEditorToolbar1
23543  * Basic Toolbar
23544  * 
23545  * Usage:
23546  *
23547  new Roo.bootstrap.HtmlEditor({
23548     ....
23549     toolbars : [
23550         new Roo.bootstrap.HtmlEditorToolbar1({
23551             disable : { fonts: 1 , format: 1, ..., ... , ...],
23552             btns : [ .... ]
23553         })
23554     }
23555      
23556  * 
23557  * @cfg {Object} disable List of elements to disable..
23558  * @cfg {Array} btns List of additional buttons.
23559  * 
23560  * 
23561  * NEEDS Extra CSS? 
23562  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23563  */
23564  
23565 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23566 {
23567     
23568     Roo.apply(this, config);
23569     
23570     // default disabled, based on 'good practice'..
23571     this.disable = this.disable || {};
23572     Roo.applyIf(this.disable, {
23573         fontSize : true,
23574         colors : true,
23575         specialElements : true
23576     });
23577     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23578     
23579     this.editor = config.editor;
23580     this.editorcore = config.editor.editorcore;
23581     
23582     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23583     
23584     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23585     // dont call parent... till later.
23586 }
23587 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23588      
23589     bar : true,
23590     
23591     editor : false,
23592     editorcore : false,
23593     
23594     
23595     formats : [
23596         "p" ,  
23597         "h1","h2","h3","h4","h5","h6", 
23598         "pre", "code", 
23599         "abbr", "acronym", "address", "cite", "samp", "var",
23600         'div','span'
23601     ],
23602     
23603     onRender : function(ct, position)
23604     {
23605        // Roo.log("Call onRender: " + this.xtype);
23606         
23607        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23608        Roo.log(this.el);
23609        this.el.dom.style.marginBottom = '0';
23610        var _this = this;
23611        var editorcore = this.editorcore;
23612        var editor= this.editor;
23613        
23614        var children = [];
23615        var btn = function(id,cmd , toggle, handler, html){
23616        
23617             var  event = toggle ? 'toggle' : 'click';
23618        
23619             var a = {
23620                 size : 'sm',
23621                 xtype: 'Button',
23622                 xns: Roo.bootstrap,
23623                 glyphicon : id,
23624                 cmd : id || cmd,
23625                 enableToggle:toggle !== false,
23626                 html : html || '',
23627                 pressed : toggle ? false : null,
23628                 listeners : {}
23629             };
23630             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23631                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23632             };
23633             children.push(a);
23634             return a;
23635        }
23636        
23637     //    var cb_box = function...
23638         
23639         var style = {
23640                 xtype: 'Button',
23641                 size : 'sm',
23642                 xns: Roo.bootstrap,
23643                 glyphicon : 'font',
23644                 //html : 'submit'
23645                 menu : {
23646                     xtype: 'Menu',
23647                     xns: Roo.bootstrap,
23648                     items:  []
23649                 }
23650         };
23651         Roo.each(this.formats, function(f) {
23652             style.menu.items.push({
23653                 xtype :'MenuItem',
23654                 xns: Roo.bootstrap,
23655                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23656                 tagname : f,
23657                 listeners : {
23658                     click : function()
23659                     {
23660                         editorcore.insertTag(this.tagname);
23661                         editor.focus();
23662                     }
23663                 }
23664                 
23665             });
23666         });
23667         children.push(style);   
23668         
23669         btn('bold',false,true);
23670         btn('italic',false,true);
23671         btn('align-left', 'justifyleft',true);
23672         btn('align-center', 'justifycenter',true);
23673         btn('align-right' , 'justifyright',true);
23674         btn('link', false, false, function(btn) {
23675             //Roo.log("create link?");
23676             var url = prompt(this.createLinkText, this.defaultLinkValue);
23677             if(url && url != 'http:/'+'/'){
23678                 this.editorcore.relayCmd('createlink', url);
23679             }
23680         }),
23681         btn('list','insertunorderedlist',true);
23682         btn('pencil', false,true, function(btn){
23683                 Roo.log(this);
23684                 this.toggleSourceEdit(btn.pressed);
23685         });
23686         
23687         if (this.editor.btns.length > 0) {
23688             for (var i = 0; i<this.editor.btns.length; i++) {
23689                 children.push(this.editor.btns[i]);
23690             }
23691         }
23692         
23693         /*
23694         var cog = {
23695                 xtype: 'Button',
23696                 size : 'sm',
23697                 xns: Roo.bootstrap,
23698                 glyphicon : 'cog',
23699                 //html : 'submit'
23700                 menu : {
23701                     xtype: 'Menu',
23702                     xns: Roo.bootstrap,
23703                     items:  []
23704                 }
23705         };
23706         
23707         cog.menu.items.push({
23708             xtype :'MenuItem',
23709             xns: Roo.bootstrap,
23710             html : Clean styles,
23711             tagname : f,
23712             listeners : {
23713                 click : function()
23714                 {
23715                     editorcore.insertTag(this.tagname);
23716                     editor.focus();
23717                 }
23718             }
23719             
23720         });
23721        */
23722         
23723          
23724        this.xtype = 'NavSimplebar';
23725         
23726         for(var i=0;i< children.length;i++) {
23727             
23728             this.buttons.add(this.addxtypeChild(children[i]));
23729             
23730         }
23731         
23732         editor.on('editorevent', this.updateToolbar, this);
23733     },
23734     onBtnClick : function(id)
23735     {
23736        this.editorcore.relayCmd(id);
23737        this.editorcore.focus();
23738     },
23739     
23740     /**
23741      * Protected method that will not generally be called directly. It triggers
23742      * a toolbar update by reading the markup state of the current selection in the editor.
23743      */
23744     updateToolbar: function(){
23745
23746         if(!this.editorcore.activated){
23747             this.editor.onFirstFocus(); // is this neeed?
23748             return;
23749         }
23750
23751         var btns = this.buttons; 
23752         var doc = this.editorcore.doc;
23753         btns.get('bold').setActive(doc.queryCommandState('bold'));
23754         btns.get('italic').setActive(doc.queryCommandState('italic'));
23755         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23756         
23757         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23758         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23759         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23760         
23761         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23762         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23763          /*
23764         
23765         var ans = this.editorcore.getAllAncestors();
23766         if (this.formatCombo) {
23767             
23768             
23769             var store = this.formatCombo.store;
23770             this.formatCombo.setValue("");
23771             for (var i =0; i < ans.length;i++) {
23772                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23773                     // select it..
23774                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23775                     break;
23776                 }
23777             }
23778         }
23779         
23780         
23781         
23782         // hides menus... - so this cant be on a menu...
23783         Roo.bootstrap.MenuMgr.hideAll();
23784         */
23785         Roo.bootstrap.MenuMgr.hideAll();
23786         //this.editorsyncValue();
23787     },
23788     onFirstFocus: function() {
23789         this.buttons.each(function(item){
23790            item.enable();
23791         });
23792     },
23793     toggleSourceEdit : function(sourceEditMode){
23794         
23795           
23796         if(sourceEditMode){
23797             Roo.log("disabling buttons");
23798            this.buttons.each( function(item){
23799                 if(item.cmd != 'pencil'){
23800                     item.disable();
23801                 }
23802             });
23803           
23804         }else{
23805             Roo.log("enabling buttons");
23806             if(this.editorcore.initialized){
23807                 this.buttons.each( function(item){
23808                     item.enable();
23809                 });
23810             }
23811             
23812         }
23813         Roo.log("calling toggole on editor");
23814         // tell the editor that it's been pressed..
23815         this.editor.toggleSourceEdit(sourceEditMode);
23816        
23817     }
23818 });
23819
23820
23821
23822
23823
23824 /**
23825  * @class Roo.bootstrap.Table.AbstractSelectionModel
23826  * @extends Roo.util.Observable
23827  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23828  * implemented by descendant classes.  This class should not be directly instantiated.
23829  * @constructor
23830  */
23831 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23832     this.locked = false;
23833     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23834 };
23835
23836
23837 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23838     /** @ignore Called by the grid automatically. Do not call directly. */
23839     init : function(grid){
23840         this.grid = grid;
23841         this.initEvents();
23842     },
23843
23844     /**
23845      * Locks the selections.
23846      */
23847     lock : function(){
23848         this.locked = true;
23849     },
23850
23851     /**
23852      * Unlocks the selections.
23853      */
23854     unlock : function(){
23855         this.locked = false;
23856     },
23857
23858     /**
23859      * Returns true if the selections are locked.
23860      * @return {Boolean}
23861      */
23862     isLocked : function(){
23863         return this.locked;
23864     }
23865 });
23866 /**
23867  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23868  * @class Roo.bootstrap.Table.RowSelectionModel
23869  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23870  * It supports multiple selections and keyboard selection/navigation. 
23871  * @constructor
23872  * @param {Object} config
23873  */
23874
23875 Roo.bootstrap.Table.RowSelectionModel = function(config){
23876     Roo.apply(this, config);
23877     this.selections = new Roo.util.MixedCollection(false, function(o){
23878         return o.id;
23879     });
23880
23881     this.last = false;
23882     this.lastActive = false;
23883
23884     this.addEvents({
23885         /**
23886              * @event selectionchange
23887              * Fires when the selection changes
23888              * @param {SelectionModel} this
23889              */
23890             "selectionchange" : true,
23891         /**
23892              * @event afterselectionchange
23893              * Fires after the selection changes (eg. by key press or clicking)
23894              * @param {SelectionModel} this
23895              */
23896             "afterselectionchange" : true,
23897         /**
23898              * @event beforerowselect
23899              * Fires when a row is selected being selected, return false to cancel.
23900              * @param {SelectionModel} this
23901              * @param {Number} rowIndex The selected index
23902              * @param {Boolean} keepExisting False if other selections will be cleared
23903              */
23904             "beforerowselect" : true,
23905         /**
23906              * @event rowselect
23907              * Fires when a row is selected.
23908              * @param {SelectionModel} this
23909              * @param {Number} rowIndex The selected index
23910              * @param {Roo.data.Record} r The record
23911              */
23912             "rowselect" : true,
23913         /**
23914              * @event rowdeselect
23915              * Fires when a row is deselected.
23916              * @param {SelectionModel} this
23917              * @param {Number} rowIndex The selected index
23918              */
23919         "rowdeselect" : true
23920     });
23921     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23922     this.locked = false;
23923  };
23924
23925 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23926     /**
23927      * @cfg {Boolean} singleSelect
23928      * True to allow selection of only one row at a time (defaults to false)
23929      */
23930     singleSelect : false,
23931
23932     // private
23933     initEvents : function()
23934     {
23935
23936         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23937         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23938         //}else{ // allow click to work like normal
23939          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23940         //}
23941         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23942         this.grid.on("rowclick", this.handleMouseDown, this);
23943         
23944         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23945             "up" : function(e){
23946                 if(!e.shiftKey){
23947                     this.selectPrevious(e.shiftKey);
23948                 }else if(this.last !== false && this.lastActive !== false){
23949                     var last = this.last;
23950                     this.selectRange(this.last,  this.lastActive-1);
23951                     this.grid.getView().focusRow(this.lastActive);
23952                     if(last !== false){
23953                         this.last = last;
23954                     }
23955                 }else{
23956                     this.selectFirstRow();
23957                 }
23958                 this.fireEvent("afterselectionchange", this);
23959             },
23960             "down" : function(e){
23961                 if(!e.shiftKey){
23962                     this.selectNext(e.shiftKey);
23963                 }else if(this.last !== false && this.lastActive !== false){
23964                     var last = this.last;
23965                     this.selectRange(this.last,  this.lastActive+1);
23966                     this.grid.getView().focusRow(this.lastActive);
23967                     if(last !== false){
23968                         this.last = last;
23969                     }
23970                 }else{
23971                     this.selectFirstRow();
23972                 }
23973                 this.fireEvent("afterselectionchange", this);
23974             },
23975             scope: this
23976         });
23977         this.grid.store.on('load', function(){
23978             this.selections.clear();
23979         },this);
23980         /*
23981         var view = this.grid.view;
23982         view.on("refresh", this.onRefresh, this);
23983         view.on("rowupdated", this.onRowUpdated, this);
23984         view.on("rowremoved", this.onRemove, this);
23985         */
23986     },
23987
23988     // private
23989     onRefresh : function()
23990     {
23991         var ds = this.grid.store, i, v = this.grid.view;
23992         var s = this.selections;
23993         s.each(function(r){
23994             if((i = ds.indexOfId(r.id)) != -1){
23995                 v.onRowSelect(i);
23996             }else{
23997                 s.remove(r);
23998             }
23999         });
24000     },
24001
24002     // private
24003     onRemove : function(v, index, r){
24004         this.selections.remove(r);
24005     },
24006
24007     // private
24008     onRowUpdated : function(v, index, r){
24009         if(this.isSelected(r)){
24010             v.onRowSelect(index);
24011         }
24012     },
24013
24014     /**
24015      * Select records.
24016      * @param {Array} records The records to select
24017      * @param {Boolean} keepExisting (optional) True to keep existing selections
24018      */
24019     selectRecords : function(records, keepExisting)
24020     {
24021         if(!keepExisting){
24022             this.clearSelections();
24023         }
24024             var ds = this.grid.store;
24025         for(var i = 0, len = records.length; i < len; i++){
24026             this.selectRow(ds.indexOf(records[i]), true);
24027         }
24028     },
24029
24030     /**
24031      * Gets the number of selected rows.
24032      * @return {Number}
24033      */
24034     getCount : function(){
24035         return this.selections.length;
24036     },
24037
24038     /**
24039      * Selects the first row in the grid.
24040      */
24041     selectFirstRow : function(){
24042         this.selectRow(0);
24043     },
24044
24045     /**
24046      * Select the last row.
24047      * @param {Boolean} keepExisting (optional) True to keep existing selections
24048      */
24049     selectLastRow : function(keepExisting){
24050         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24051         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24052     },
24053
24054     /**
24055      * Selects the row immediately following the last selected row.
24056      * @param {Boolean} keepExisting (optional) True to keep existing selections
24057      */
24058     selectNext : function(keepExisting)
24059     {
24060             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24061             this.selectRow(this.last+1, keepExisting);
24062             this.grid.getView().focusRow(this.last);
24063         }
24064     },
24065
24066     /**
24067      * Selects the row that precedes the last selected row.
24068      * @param {Boolean} keepExisting (optional) True to keep existing selections
24069      */
24070     selectPrevious : function(keepExisting){
24071         if(this.last){
24072             this.selectRow(this.last-1, keepExisting);
24073             this.grid.getView().focusRow(this.last);
24074         }
24075     },
24076
24077     /**
24078      * Returns the selected records
24079      * @return {Array} Array of selected records
24080      */
24081     getSelections : function(){
24082         return [].concat(this.selections.items);
24083     },
24084
24085     /**
24086      * Returns the first selected record.
24087      * @return {Record}
24088      */
24089     getSelected : function(){
24090         return this.selections.itemAt(0);
24091     },
24092
24093
24094     /**
24095      * Clears all selections.
24096      */
24097     clearSelections : function(fast)
24098     {
24099         if(this.locked) {
24100             return;
24101         }
24102         if(fast !== true){
24103                 var ds = this.grid.store;
24104             var s = this.selections;
24105             s.each(function(r){
24106                 this.deselectRow(ds.indexOfId(r.id));
24107             }, this);
24108             s.clear();
24109         }else{
24110             this.selections.clear();
24111         }
24112         this.last = false;
24113     },
24114
24115
24116     /**
24117      * Selects all rows.
24118      */
24119     selectAll : function(){
24120         if(this.locked) {
24121             return;
24122         }
24123         this.selections.clear();
24124         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24125             this.selectRow(i, true);
24126         }
24127     },
24128
24129     /**
24130      * Returns True if there is a selection.
24131      * @return {Boolean}
24132      */
24133     hasSelection : function(){
24134         return this.selections.length > 0;
24135     },
24136
24137     /**
24138      * Returns True if the specified row is selected.
24139      * @param {Number/Record} record The record or index of the record to check
24140      * @return {Boolean}
24141      */
24142     isSelected : function(index){
24143             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24144         return (r && this.selections.key(r.id) ? true : false);
24145     },
24146
24147     /**
24148      * Returns True if the specified record id is selected.
24149      * @param {String} id The id of record to check
24150      * @return {Boolean}
24151      */
24152     isIdSelected : function(id){
24153         return (this.selections.key(id) ? true : false);
24154     },
24155
24156
24157     // private
24158     handleMouseDBClick : function(e, t){
24159         
24160     },
24161     // private
24162     handleMouseDown : function(e, t)
24163     {
24164             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24165         if(this.isLocked() || rowIndex < 0 ){
24166             return;
24167         };
24168         if(e.shiftKey && this.last !== false){
24169             var last = this.last;
24170             this.selectRange(last, rowIndex, e.ctrlKey);
24171             this.last = last; // reset the last
24172             t.focus();
24173     
24174         }else{
24175             var isSelected = this.isSelected(rowIndex);
24176             //Roo.log("select row:" + rowIndex);
24177             if(isSelected){
24178                 this.deselectRow(rowIndex);
24179             } else {
24180                         this.selectRow(rowIndex, true);
24181             }
24182     
24183             /*
24184                 if(e.button !== 0 && isSelected){
24185                 alert('rowIndex 2: ' + rowIndex);
24186                     view.focusRow(rowIndex);
24187                 }else if(e.ctrlKey && isSelected){
24188                     this.deselectRow(rowIndex);
24189                 }else if(!isSelected){
24190                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24191                     view.focusRow(rowIndex);
24192                 }
24193             */
24194         }
24195         this.fireEvent("afterselectionchange", this);
24196     },
24197     // private
24198     handleDragableRowClick :  function(grid, rowIndex, e) 
24199     {
24200         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24201             this.selectRow(rowIndex, false);
24202             grid.view.focusRow(rowIndex);
24203              this.fireEvent("afterselectionchange", this);
24204         }
24205     },
24206     
24207     /**
24208      * Selects multiple rows.
24209      * @param {Array} rows Array of the indexes of the row to select
24210      * @param {Boolean} keepExisting (optional) True to keep existing selections
24211      */
24212     selectRows : function(rows, keepExisting){
24213         if(!keepExisting){
24214             this.clearSelections();
24215         }
24216         for(var i = 0, len = rows.length; i < len; i++){
24217             this.selectRow(rows[i], true);
24218         }
24219     },
24220
24221     /**
24222      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24223      * @param {Number} startRow The index of the first row in the range
24224      * @param {Number} endRow The index of the last row in the range
24225      * @param {Boolean} keepExisting (optional) True to retain existing selections
24226      */
24227     selectRange : function(startRow, endRow, keepExisting){
24228         if(this.locked) {
24229             return;
24230         }
24231         if(!keepExisting){
24232             this.clearSelections();
24233         }
24234         if(startRow <= endRow){
24235             for(var i = startRow; i <= endRow; i++){
24236                 this.selectRow(i, true);
24237             }
24238         }else{
24239             for(var i = startRow; i >= endRow; i--){
24240                 this.selectRow(i, true);
24241             }
24242         }
24243     },
24244
24245     /**
24246      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24247      * @param {Number} startRow The index of the first row in the range
24248      * @param {Number} endRow The index of the last row in the range
24249      */
24250     deselectRange : function(startRow, endRow, preventViewNotify){
24251         if(this.locked) {
24252             return;
24253         }
24254         for(var i = startRow; i <= endRow; i++){
24255             this.deselectRow(i, preventViewNotify);
24256         }
24257     },
24258
24259     /**
24260      * Selects a row.
24261      * @param {Number} row The index of the row to select
24262      * @param {Boolean} keepExisting (optional) True to keep existing selections
24263      */
24264     selectRow : function(index, keepExisting, preventViewNotify)
24265     {
24266             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24267             return;
24268         }
24269         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24270             if(!keepExisting || this.singleSelect){
24271                 this.clearSelections();
24272             }
24273             
24274             var r = this.grid.store.getAt(index);
24275             //console.log('selectRow - record id :' + r.id);
24276             
24277             this.selections.add(r);
24278             this.last = this.lastActive = index;
24279             if(!preventViewNotify){
24280                 var proxy = new Roo.Element(
24281                                 this.grid.getRowDom(index)
24282                 );
24283                 proxy.addClass('bg-info info');
24284             }
24285             this.fireEvent("rowselect", this, index, r);
24286             this.fireEvent("selectionchange", this);
24287         }
24288     },
24289
24290     /**
24291      * Deselects a row.
24292      * @param {Number} row The index of the row to deselect
24293      */
24294     deselectRow : function(index, preventViewNotify)
24295     {
24296         if(this.locked) {
24297             return;
24298         }
24299         if(this.last == index){
24300             this.last = false;
24301         }
24302         if(this.lastActive == index){
24303             this.lastActive = false;
24304         }
24305         
24306         var r = this.grid.store.getAt(index);
24307         if (!r) {
24308             return;
24309         }
24310         
24311         this.selections.remove(r);
24312         //.console.log('deselectRow - record id :' + r.id);
24313         if(!preventViewNotify){
24314         
24315             var proxy = new Roo.Element(
24316                 this.grid.getRowDom(index)
24317             );
24318             proxy.removeClass('bg-info info');
24319         }
24320         this.fireEvent("rowdeselect", this, index);
24321         this.fireEvent("selectionchange", this);
24322     },
24323
24324     // private
24325     restoreLast : function(){
24326         if(this._last){
24327             this.last = this._last;
24328         }
24329     },
24330
24331     // private
24332     acceptsNav : function(row, col, cm){
24333         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24334     },
24335
24336     // private
24337     onEditorKey : function(field, e){
24338         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24339         if(k == e.TAB){
24340             e.stopEvent();
24341             ed.completeEdit();
24342             if(e.shiftKey){
24343                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24344             }else{
24345                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24346             }
24347         }else if(k == e.ENTER && !e.ctrlKey){
24348             e.stopEvent();
24349             ed.completeEdit();
24350             if(e.shiftKey){
24351                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24352             }else{
24353                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24354             }
24355         }else if(k == e.ESC){
24356             ed.cancelEdit();
24357         }
24358         if(newCell){
24359             g.startEditing(newCell[0], newCell[1]);
24360         }
24361     }
24362 });
24363 /*
24364  * Based on:
24365  * Ext JS Library 1.1.1
24366  * Copyright(c) 2006-2007, Ext JS, LLC.
24367  *
24368  * Originally Released Under LGPL - original licence link has changed is not relivant.
24369  *
24370  * Fork - LGPL
24371  * <script type="text/javascript">
24372  */
24373  
24374 /**
24375  * @class Roo.bootstrap.PagingToolbar
24376  * @extends Roo.bootstrap.NavSimplebar
24377  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24378  * @constructor
24379  * Create a new PagingToolbar
24380  * @param {Object} config The config object
24381  * @param {Roo.data.Store} store
24382  */
24383 Roo.bootstrap.PagingToolbar = function(config)
24384 {
24385     // old args format still supported... - xtype is prefered..
24386         // created from xtype...
24387     
24388     this.ds = config.dataSource;
24389     
24390     if (config.store && !this.ds) {
24391         this.store= Roo.factory(config.store, Roo.data);
24392         this.ds = this.store;
24393         this.ds.xmodule = this.xmodule || false;
24394     }
24395     
24396     this.toolbarItems = [];
24397     if (config.items) {
24398         this.toolbarItems = config.items;
24399     }
24400     
24401     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24402     
24403     this.cursor = 0;
24404     
24405     if (this.ds) { 
24406         this.bind(this.ds);
24407     }
24408     
24409     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24410     
24411 };
24412
24413 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24414     /**
24415      * @cfg {Roo.data.Store} dataSource
24416      * The underlying data store providing the paged data
24417      */
24418     /**
24419      * @cfg {String/HTMLElement/Element} container
24420      * container The id or element that will contain the toolbar
24421      */
24422     /**
24423      * @cfg {Boolean} displayInfo
24424      * True to display the displayMsg (defaults to false)
24425      */
24426     /**
24427      * @cfg {Number} pageSize
24428      * The number of records to display per page (defaults to 20)
24429      */
24430     pageSize: 20,
24431     /**
24432      * @cfg {String} displayMsg
24433      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24434      */
24435     displayMsg : 'Displaying {0} - {1} of {2}',
24436     /**
24437      * @cfg {String} emptyMsg
24438      * The message to display when no records are found (defaults to "No data to display")
24439      */
24440     emptyMsg : 'No data to display',
24441     /**
24442      * Customizable piece of the default paging text (defaults to "Page")
24443      * @type String
24444      */
24445     beforePageText : "Page",
24446     /**
24447      * Customizable piece of the default paging text (defaults to "of %0")
24448      * @type String
24449      */
24450     afterPageText : "of {0}",
24451     /**
24452      * Customizable piece of the default paging text (defaults to "First Page")
24453      * @type String
24454      */
24455     firstText : "First Page",
24456     /**
24457      * Customizable piece of the default paging text (defaults to "Previous Page")
24458      * @type String
24459      */
24460     prevText : "Previous Page",
24461     /**
24462      * Customizable piece of the default paging text (defaults to "Next Page")
24463      * @type String
24464      */
24465     nextText : "Next Page",
24466     /**
24467      * Customizable piece of the default paging text (defaults to "Last Page")
24468      * @type String
24469      */
24470     lastText : "Last Page",
24471     /**
24472      * Customizable piece of the default paging text (defaults to "Refresh")
24473      * @type String
24474      */
24475     refreshText : "Refresh",
24476
24477     buttons : false,
24478     // private
24479     onRender : function(ct, position) 
24480     {
24481         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24482         this.navgroup.parentId = this.id;
24483         this.navgroup.onRender(this.el, null);
24484         // add the buttons to the navgroup
24485         
24486         if(this.displayInfo){
24487             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24488             this.displayEl = this.el.select('.x-paging-info', true).first();
24489 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24490 //            this.displayEl = navel.el.select('span',true).first();
24491         }
24492         
24493         var _this = this;
24494         
24495         if(this.buttons){
24496             Roo.each(_this.buttons, function(e){ // this might need to use render????
24497                Roo.factory(e).onRender(_this.el, null);
24498             });
24499         }
24500             
24501         Roo.each(_this.toolbarItems, function(e) {
24502             _this.navgroup.addItem(e);
24503         });
24504         
24505         
24506         this.first = this.navgroup.addItem({
24507             tooltip: this.firstText,
24508             cls: "prev",
24509             icon : 'fa fa-backward',
24510             disabled: true,
24511             preventDefault: true,
24512             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24513         });
24514         
24515         this.prev =  this.navgroup.addItem({
24516             tooltip: this.prevText,
24517             cls: "prev",
24518             icon : 'fa fa-step-backward',
24519             disabled: true,
24520             preventDefault: true,
24521             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24522         });
24523     //this.addSeparator();
24524         
24525         
24526         var field = this.navgroup.addItem( {
24527             tagtype : 'span',
24528             cls : 'x-paging-position',
24529             
24530             html : this.beforePageText  +
24531                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24532                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24533          } ); //?? escaped?
24534         
24535         this.field = field.el.select('input', true).first();
24536         this.field.on("keydown", this.onPagingKeydown, this);
24537         this.field.on("focus", function(){this.dom.select();});
24538     
24539     
24540         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24541         //this.field.setHeight(18);
24542         //this.addSeparator();
24543         this.next = this.navgroup.addItem({
24544             tooltip: this.nextText,
24545             cls: "next",
24546             html : ' <i class="fa fa-step-forward">',
24547             disabled: true,
24548             preventDefault: true,
24549             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24550         });
24551         this.last = this.navgroup.addItem({
24552             tooltip: this.lastText,
24553             icon : 'fa fa-forward',
24554             cls: "next",
24555             disabled: true,
24556             preventDefault: true,
24557             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24558         });
24559     //this.addSeparator();
24560         this.loading = this.navgroup.addItem({
24561             tooltip: this.refreshText,
24562             icon: 'fa fa-refresh',
24563             preventDefault: true,
24564             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24565         });
24566         
24567     },
24568
24569     // private
24570     updateInfo : function(){
24571         if(this.displayEl){
24572             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24573             var msg = count == 0 ?
24574                 this.emptyMsg :
24575                 String.format(
24576                     this.displayMsg,
24577                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24578                 );
24579             this.displayEl.update(msg);
24580         }
24581     },
24582
24583     // private
24584     onLoad : function(ds, r, o)
24585     {
24586         this.cursor = o.params.start ? o.params.start : 0;
24587         
24588         var d = this.getPageData(),
24589             ap = d.activePage,
24590             ps = d.pages;
24591         
24592         
24593         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24594         this.field.dom.value = ap;
24595         this.first.setDisabled(ap == 1);
24596         this.prev.setDisabled(ap == 1);
24597         this.next.setDisabled(ap == ps);
24598         this.last.setDisabled(ap == ps);
24599         this.loading.enable();
24600         this.updateInfo();
24601     },
24602
24603     // private
24604     getPageData : function(){
24605         var total = this.ds.getTotalCount();
24606         return {
24607             total : total,
24608             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24609             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24610         };
24611     },
24612
24613     // private
24614     onLoadError : function(){
24615         this.loading.enable();
24616     },
24617
24618     // private
24619     onPagingKeydown : function(e){
24620         var k = e.getKey();
24621         var d = this.getPageData();
24622         if(k == e.RETURN){
24623             var v = this.field.dom.value, pageNum;
24624             if(!v || isNaN(pageNum = parseInt(v, 10))){
24625                 this.field.dom.value = d.activePage;
24626                 return;
24627             }
24628             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24629             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24630             e.stopEvent();
24631         }
24632         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))
24633         {
24634           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24635           this.field.dom.value = pageNum;
24636           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24637           e.stopEvent();
24638         }
24639         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24640         {
24641           var v = this.field.dom.value, pageNum; 
24642           var increment = (e.shiftKey) ? 10 : 1;
24643           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24644                 increment *= -1;
24645           }
24646           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24647             this.field.dom.value = d.activePage;
24648             return;
24649           }
24650           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24651           {
24652             this.field.dom.value = parseInt(v, 10) + increment;
24653             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24654             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24655           }
24656           e.stopEvent();
24657         }
24658     },
24659
24660     // private
24661     beforeLoad : function(){
24662         if(this.loading){
24663             this.loading.disable();
24664         }
24665     },
24666
24667     // private
24668     onClick : function(which){
24669         
24670         var ds = this.ds;
24671         if (!ds) {
24672             return;
24673         }
24674         
24675         switch(which){
24676             case "first":
24677                 ds.load({params:{start: 0, limit: this.pageSize}});
24678             break;
24679             case "prev":
24680                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24681             break;
24682             case "next":
24683                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24684             break;
24685             case "last":
24686                 var total = ds.getTotalCount();
24687                 var extra = total % this.pageSize;
24688                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24689                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24690             break;
24691             case "refresh":
24692                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24693             break;
24694         }
24695     },
24696
24697     /**
24698      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24699      * @param {Roo.data.Store} store The data store to unbind
24700      */
24701     unbind : function(ds){
24702         ds.un("beforeload", this.beforeLoad, this);
24703         ds.un("load", this.onLoad, this);
24704         ds.un("loadexception", this.onLoadError, this);
24705         ds.un("remove", this.updateInfo, this);
24706         ds.un("add", this.updateInfo, this);
24707         this.ds = undefined;
24708     },
24709
24710     /**
24711      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24712      * @param {Roo.data.Store} store The data store to bind
24713      */
24714     bind : function(ds){
24715         ds.on("beforeload", this.beforeLoad, this);
24716         ds.on("load", this.onLoad, this);
24717         ds.on("loadexception", this.onLoadError, this);
24718         ds.on("remove", this.updateInfo, this);
24719         ds.on("add", this.updateInfo, this);
24720         this.ds = ds;
24721     }
24722 });/*
24723  * - LGPL
24724  *
24725  * element
24726  * 
24727  */
24728
24729 /**
24730  * @class Roo.bootstrap.MessageBar
24731  * @extends Roo.bootstrap.Component
24732  * Bootstrap MessageBar class
24733  * @cfg {String} html contents of the MessageBar
24734  * @cfg {String} weight (info | success | warning | danger) default info
24735  * @cfg {String} beforeClass insert the bar before the given class
24736  * @cfg {Boolean} closable (true | false) default false
24737  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24738  * 
24739  * @constructor
24740  * Create a new Element
24741  * @param {Object} config The config object
24742  */
24743
24744 Roo.bootstrap.MessageBar = function(config){
24745     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24746 };
24747
24748 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24749     
24750     html: '',
24751     weight: 'info',
24752     closable: false,
24753     fixed: false,
24754     beforeClass: 'bootstrap-sticky-wrap',
24755     
24756     getAutoCreate : function(){
24757         
24758         var cfg = {
24759             tag: 'div',
24760             cls: 'alert alert-dismissable alert-' + this.weight,
24761             cn: [
24762                 {
24763                     tag: 'span',
24764                     cls: 'message',
24765                     html: this.html || ''
24766                 }
24767             ]
24768         };
24769         
24770         if(this.fixed){
24771             cfg.cls += ' alert-messages-fixed';
24772         }
24773         
24774         if(this.closable){
24775             cfg.cn.push({
24776                 tag: 'button',
24777                 cls: 'close',
24778                 html: 'x'
24779             });
24780         }
24781         
24782         return cfg;
24783     },
24784     
24785     onRender : function(ct, position)
24786     {
24787         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24788         
24789         if(!this.el){
24790             var cfg = Roo.apply({},  this.getAutoCreate());
24791             cfg.id = Roo.id();
24792             
24793             if (this.cls) {
24794                 cfg.cls += ' ' + this.cls;
24795             }
24796             if (this.style) {
24797                 cfg.style = this.style;
24798             }
24799             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24800             
24801             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24802         }
24803         
24804         this.el.select('>button.close').on('click', this.hide, this);
24805         
24806     },
24807     
24808     show : function()
24809     {
24810         if (!this.rendered) {
24811             this.render();
24812         }
24813         
24814         this.el.show();
24815         
24816         this.fireEvent('show', this);
24817         
24818     },
24819     
24820     hide : function()
24821     {
24822         if (!this.rendered) {
24823             this.render();
24824         }
24825         
24826         this.el.hide();
24827         
24828         this.fireEvent('hide', this);
24829     },
24830     
24831     update : function()
24832     {
24833 //        var e = this.el.dom.firstChild;
24834 //        
24835 //        if(this.closable){
24836 //            e = e.nextSibling;
24837 //        }
24838 //        
24839 //        e.data = this.html || '';
24840
24841         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24842     }
24843    
24844 });
24845
24846  
24847
24848      /*
24849  * - LGPL
24850  *
24851  * Graph
24852  * 
24853  */
24854
24855
24856 /**
24857  * @class Roo.bootstrap.Graph
24858  * @extends Roo.bootstrap.Component
24859  * Bootstrap Graph class
24860 > Prameters
24861  -sm {number} sm 4
24862  -md {number} md 5
24863  @cfg {String} graphtype  bar | vbar | pie
24864  @cfg {number} g_x coodinator | centre x (pie)
24865  @cfg {number} g_y coodinator | centre y (pie)
24866  @cfg {number} g_r radius (pie)
24867  @cfg {number} g_height height of the chart (respected by all elements in the set)
24868  @cfg {number} g_width width of the chart (respected by all elements in the set)
24869  @cfg {Object} title The title of the chart
24870     
24871  -{Array}  values
24872  -opts (object) options for the chart 
24873      o {
24874      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24875      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24876      o vgutter (number)
24877      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.
24878      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24879      o to
24880      o stretch (boolean)
24881      o }
24882  -opts (object) options for the pie
24883      o{
24884      o cut
24885      o startAngle (number)
24886      o endAngle (number)
24887      } 
24888  *
24889  * @constructor
24890  * Create a new Input
24891  * @param {Object} config The config object
24892  */
24893
24894 Roo.bootstrap.Graph = function(config){
24895     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24896     
24897     this.addEvents({
24898         // img events
24899         /**
24900          * @event click
24901          * The img click event for the img.
24902          * @param {Roo.EventObject} e
24903          */
24904         "click" : true
24905     });
24906 };
24907
24908 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24909     
24910     sm: 4,
24911     md: 5,
24912     graphtype: 'bar',
24913     g_height: 250,
24914     g_width: 400,
24915     g_x: 50,
24916     g_y: 50,
24917     g_r: 30,
24918     opts:{
24919         //g_colors: this.colors,
24920         g_type: 'soft',
24921         g_gutter: '20%'
24922
24923     },
24924     title : false,
24925
24926     getAutoCreate : function(){
24927         
24928         var cfg = {
24929             tag: 'div',
24930             html : null
24931         };
24932         
24933         
24934         return  cfg;
24935     },
24936
24937     onRender : function(ct,position){
24938         
24939         
24940         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24941         
24942         if (typeof(Raphael) == 'undefined') {
24943             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24944             return;
24945         }
24946         
24947         this.raphael = Raphael(this.el.dom);
24948         
24949                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24950                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24951                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24952                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24953                 /*
24954                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24955                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24956                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24957                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24958                 
24959                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24960                 r.barchart(330, 10, 300, 220, data1);
24961                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24962                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24963                 */
24964                 
24965                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24966                 // r.barchart(30, 30, 560, 250,  xdata, {
24967                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24968                 //     axis : "0 0 1 1",
24969                 //     axisxlabels :  xdata
24970                 //     //yvalues : cols,
24971                    
24972                 // });
24973 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24974 //        
24975 //        this.load(null,xdata,{
24976 //                axis : "0 0 1 1",
24977 //                axisxlabels :  xdata
24978 //                });
24979
24980     },
24981
24982     load : function(graphtype,xdata,opts)
24983     {
24984         this.raphael.clear();
24985         if(!graphtype) {
24986             graphtype = this.graphtype;
24987         }
24988         if(!opts){
24989             opts = this.opts;
24990         }
24991         var r = this.raphael,
24992             fin = function () {
24993                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24994             },
24995             fout = function () {
24996                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24997             },
24998             pfin = function() {
24999                 this.sector.stop();
25000                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25001
25002                 if (this.label) {
25003                     this.label[0].stop();
25004                     this.label[0].attr({ r: 7.5 });
25005                     this.label[1].attr({ "font-weight": 800 });
25006                 }
25007             },
25008             pfout = function() {
25009                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25010
25011                 if (this.label) {
25012                     this.label[0].animate({ r: 5 }, 500, "bounce");
25013                     this.label[1].attr({ "font-weight": 400 });
25014                 }
25015             };
25016
25017         switch(graphtype){
25018             case 'bar':
25019                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25020                 break;
25021             case 'hbar':
25022                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25023                 break;
25024             case 'pie':
25025 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25026 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25027 //            
25028                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25029                 
25030                 break;
25031
25032         }
25033         
25034         if(this.title){
25035             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25036         }
25037         
25038     },
25039     
25040     setTitle: function(o)
25041     {
25042         this.title = o;
25043     },
25044     
25045     initEvents: function() {
25046         
25047         if(!this.href){
25048             this.el.on('click', this.onClick, this);
25049         }
25050     },
25051     
25052     onClick : function(e)
25053     {
25054         Roo.log('img onclick');
25055         this.fireEvent('click', this, e);
25056     }
25057    
25058 });
25059
25060  
25061 /*
25062  * - LGPL
25063  *
25064  * numberBox
25065  * 
25066  */
25067 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25068
25069 /**
25070  * @class Roo.bootstrap.dash.NumberBox
25071  * @extends Roo.bootstrap.Component
25072  * Bootstrap NumberBox class
25073  * @cfg {String} headline Box headline
25074  * @cfg {String} content Box content
25075  * @cfg {String} icon Box icon
25076  * @cfg {String} footer Footer text
25077  * @cfg {String} fhref Footer href
25078  * 
25079  * @constructor
25080  * Create a new NumberBox
25081  * @param {Object} config The config object
25082  */
25083
25084
25085 Roo.bootstrap.dash.NumberBox = function(config){
25086     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25087     
25088 };
25089
25090 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25091     
25092     headline : '',
25093     content : '',
25094     icon : '',
25095     footer : '',
25096     fhref : '',
25097     ficon : '',
25098     
25099     getAutoCreate : function(){
25100         
25101         var cfg = {
25102             tag : 'div',
25103             cls : 'small-box ',
25104             cn : [
25105                 {
25106                     tag : 'div',
25107                     cls : 'inner',
25108                     cn :[
25109                         {
25110                             tag : 'h3',
25111                             cls : 'roo-headline',
25112                             html : this.headline
25113                         },
25114                         {
25115                             tag : 'p',
25116                             cls : 'roo-content',
25117                             html : this.content
25118                         }
25119                     ]
25120                 }
25121             ]
25122         };
25123         
25124         if(this.icon){
25125             cfg.cn.push({
25126                 tag : 'div',
25127                 cls : 'icon',
25128                 cn :[
25129                     {
25130                         tag : 'i',
25131                         cls : 'ion ' + this.icon
25132                     }
25133                 ]
25134             });
25135         }
25136         
25137         if(this.footer){
25138             var footer = {
25139                 tag : 'a',
25140                 cls : 'small-box-footer',
25141                 href : this.fhref || '#',
25142                 html : this.footer
25143             };
25144             
25145             cfg.cn.push(footer);
25146             
25147         }
25148         
25149         return  cfg;
25150     },
25151
25152     onRender : function(ct,position){
25153         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25154
25155
25156        
25157                 
25158     },
25159
25160     setHeadline: function (value)
25161     {
25162         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25163     },
25164     
25165     setFooter: function (value, href)
25166     {
25167         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25168         
25169         if(href){
25170             this.el.select('a.small-box-footer',true).first().attr('href', href);
25171         }
25172         
25173     },
25174
25175     setContent: function (value)
25176     {
25177         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25178     },
25179
25180     initEvents: function() 
25181     {   
25182         
25183     }
25184     
25185 });
25186
25187  
25188 /*
25189  * - LGPL
25190  *
25191  * TabBox
25192  * 
25193  */
25194 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25195
25196 /**
25197  * @class Roo.bootstrap.dash.TabBox
25198  * @extends Roo.bootstrap.Component
25199  * Bootstrap TabBox class
25200  * @cfg {String} title Title of the TabBox
25201  * @cfg {String} icon Icon of the TabBox
25202  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25203  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25204  * 
25205  * @constructor
25206  * Create a new TabBox
25207  * @param {Object} config The config object
25208  */
25209
25210
25211 Roo.bootstrap.dash.TabBox = function(config){
25212     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25213     this.addEvents({
25214         // raw events
25215         /**
25216          * @event addpane
25217          * When a pane is added
25218          * @param {Roo.bootstrap.dash.TabPane} pane
25219          */
25220         "addpane" : true,
25221         /**
25222          * @event activatepane
25223          * When a pane is activated
25224          * @param {Roo.bootstrap.dash.TabPane} pane
25225          */
25226         "activatepane" : true
25227         
25228          
25229     });
25230     
25231     this.panes = [];
25232 };
25233
25234 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25235
25236     title : '',
25237     icon : false,
25238     showtabs : true,
25239     tabScrollable : false,
25240     
25241     getChildContainer : function()
25242     {
25243         return this.el.select('.tab-content', true).first();
25244     },
25245     
25246     getAutoCreate : function(){
25247         
25248         var header = {
25249             tag: 'li',
25250             cls: 'pull-left header',
25251             html: this.title,
25252             cn : []
25253         };
25254         
25255         if(this.icon){
25256             header.cn.push({
25257                 tag: 'i',
25258                 cls: 'fa ' + this.icon
25259             });
25260         }
25261         
25262         var h = {
25263             tag: 'ul',
25264             cls: 'nav nav-tabs pull-right',
25265             cn: [
25266                 header
25267             ]
25268         };
25269         
25270         if(this.tabScrollable){
25271             h = {
25272                 tag: 'div',
25273                 cls: 'tab-header',
25274                 cn: [
25275                     {
25276                         tag: 'ul',
25277                         cls: 'nav nav-tabs pull-right',
25278                         cn: [
25279                             header
25280                         ]
25281                     }
25282                 ]
25283             };
25284         }
25285         
25286         var cfg = {
25287             tag: 'div',
25288             cls: 'nav-tabs-custom',
25289             cn: [
25290                 h,
25291                 {
25292                     tag: 'div',
25293                     cls: 'tab-content no-padding',
25294                     cn: []
25295                 }
25296             ]
25297         };
25298
25299         return  cfg;
25300     },
25301     initEvents : function()
25302     {
25303         //Roo.log('add add pane handler');
25304         this.on('addpane', this.onAddPane, this);
25305     },
25306      /**
25307      * Updates the box title
25308      * @param {String} html to set the title to.
25309      */
25310     setTitle : function(value)
25311     {
25312         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25313     },
25314     onAddPane : function(pane)
25315     {
25316         this.panes.push(pane);
25317         //Roo.log('addpane');
25318         //Roo.log(pane);
25319         // tabs are rendere left to right..
25320         if(!this.showtabs){
25321             return;
25322         }
25323         
25324         var ctr = this.el.select('.nav-tabs', true).first();
25325          
25326          
25327         var existing = ctr.select('.nav-tab',true);
25328         var qty = existing.getCount();;
25329         
25330         
25331         var tab = ctr.createChild({
25332             tag : 'li',
25333             cls : 'nav-tab' + (qty ? '' : ' active'),
25334             cn : [
25335                 {
25336                     tag : 'a',
25337                     href:'#',
25338                     html : pane.title
25339                 }
25340             ]
25341         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25342         pane.tab = tab;
25343         
25344         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25345         if (!qty) {
25346             pane.el.addClass('active');
25347         }
25348         
25349                 
25350     },
25351     onTabClick : function(ev,un,ob,pane)
25352     {
25353         //Roo.log('tab - prev default');
25354         ev.preventDefault();
25355         
25356         
25357         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25358         pane.tab.addClass('active');
25359         //Roo.log(pane.title);
25360         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25361         // technically we should have a deactivate event.. but maybe add later.
25362         // and it should not de-activate the selected tab...
25363         this.fireEvent('activatepane', pane);
25364         pane.el.addClass('active');
25365         pane.fireEvent('activate');
25366         
25367         
25368     },
25369     
25370     getActivePane : function()
25371     {
25372         var r = false;
25373         Roo.each(this.panes, function(p) {
25374             if(p.el.hasClass('active')){
25375                 r = p;
25376                 return false;
25377             }
25378             
25379             return;
25380         });
25381         
25382         return r;
25383     }
25384     
25385     
25386 });
25387
25388  
25389 /*
25390  * - LGPL
25391  *
25392  * Tab pane
25393  * 
25394  */
25395 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25396 /**
25397  * @class Roo.bootstrap.TabPane
25398  * @extends Roo.bootstrap.Component
25399  * Bootstrap TabPane class
25400  * @cfg {Boolean} active (false | true) Default false
25401  * @cfg {String} title title of panel
25402
25403  * 
25404  * @constructor
25405  * Create a new TabPane
25406  * @param {Object} config The config object
25407  */
25408
25409 Roo.bootstrap.dash.TabPane = function(config){
25410     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25411     
25412     this.addEvents({
25413         // raw events
25414         /**
25415          * @event activate
25416          * When a pane is activated
25417          * @param {Roo.bootstrap.dash.TabPane} pane
25418          */
25419         "activate" : true
25420          
25421     });
25422 };
25423
25424 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25425     
25426     active : false,
25427     title : '',
25428     
25429     // the tabBox that this is attached to.
25430     tab : false,
25431      
25432     getAutoCreate : function() 
25433     {
25434         var cfg = {
25435             tag: 'div',
25436             cls: 'tab-pane'
25437         };
25438         
25439         if(this.active){
25440             cfg.cls += ' active';
25441         }
25442         
25443         return cfg;
25444     },
25445     initEvents  : function()
25446     {
25447         //Roo.log('trigger add pane handler');
25448         this.parent().fireEvent('addpane', this)
25449     },
25450     
25451      /**
25452      * Updates the tab title 
25453      * @param {String} html to set the title to.
25454      */
25455     setTitle: function(str)
25456     {
25457         if (!this.tab) {
25458             return;
25459         }
25460         this.title = str;
25461         this.tab.select('a', true).first().dom.innerHTML = str;
25462         
25463     }
25464     
25465     
25466     
25467 });
25468
25469  
25470
25471
25472  /*
25473  * - LGPL
25474  *
25475  * menu
25476  * 
25477  */
25478 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25479
25480 /**
25481  * @class Roo.bootstrap.menu.Menu
25482  * @extends Roo.bootstrap.Component
25483  * Bootstrap Menu class - container for Menu
25484  * @cfg {String} html Text of the menu
25485  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25486  * @cfg {String} icon Font awesome icon
25487  * @cfg {String} pos Menu align to (top | bottom) default bottom
25488  * 
25489  * 
25490  * @constructor
25491  * Create a new Menu
25492  * @param {Object} config The config object
25493  */
25494
25495
25496 Roo.bootstrap.menu.Menu = function(config){
25497     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25498     
25499     this.addEvents({
25500         /**
25501          * @event beforeshow
25502          * Fires before this menu is displayed
25503          * @param {Roo.bootstrap.menu.Menu} this
25504          */
25505         beforeshow : true,
25506         /**
25507          * @event beforehide
25508          * Fires before this menu is hidden
25509          * @param {Roo.bootstrap.menu.Menu} this
25510          */
25511         beforehide : true,
25512         /**
25513          * @event show
25514          * Fires after this menu is displayed
25515          * @param {Roo.bootstrap.menu.Menu} this
25516          */
25517         show : true,
25518         /**
25519          * @event hide
25520          * Fires after this menu is hidden
25521          * @param {Roo.bootstrap.menu.Menu} this
25522          */
25523         hide : true,
25524         /**
25525          * @event click
25526          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25527          * @param {Roo.bootstrap.menu.Menu} this
25528          * @param {Roo.EventObject} e
25529          */
25530         click : true
25531     });
25532     
25533 };
25534
25535 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25536     
25537     submenu : false,
25538     html : '',
25539     weight : 'default',
25540     icon : false,
25541     pos : 'bottom',
25542     
25543     
25544     getChildContainer : function() {
25545         if(this.isSubMenu){
25546             return this.el;
25547         }
25548         
25549         return this.el.select('ul.dropdown-menu', true).first();  
25550     },
25551     
25552     getAutoCreate : function()
25553     {
25554         var text = [
25555             {
25556                 tag : 'span',
25557                 cls : 'roo-menu-text',
25558                 html : this.html
25559             }
25560         ];
25561         
25562         if(this.icon){
25563             text.unshift({
25564                 tag : 'i',
25565                 cls : 'fa ' + this.icon
25566             })
25567         }
25568         
25569         
25570         var cfg = {
25571             tag : 'div',
25572             cls : 'btn-group',
25573             cn : [
25574                 {
25575                     tag : 'button',
25576                     cls : 'dropdown-button btn btn-' + this.weight,
25577                     cn : text
25578                 },
25579                 {
25580                     tag : 'button',
25581                     cls : 'dropdown-toggle btn btn-' + this.weight,
25582                     cn : [
25583                         {
25584                             tag : 'span',
25585                             cls : 'caret'
25586                         }
25587                     ]
25588                 },
25589                 {
25590                     tag : 'ul',
25591                     cls : 'dropdown-menu'
25592                 }
25593             ]
25594             
25595         };
25596         
25597         if(this.pos == 'top'){
25598             cfg.cls += ' dropup';
25599         }
25600         
25601         if(this.isSubMenu){
25602             cfg = {
25603                 tag : 'ul',
25604                 cls : 'dropdown-menu'
25605             }
25606         }
25607         
25608         return cfg;
25609     },
25610     
25611     onRender : function(ct, position)
25612     {
25613         this.isSubMenu = ct.hasClass('dropdown-submenu');
25614         
25615         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25616     },
25617     
25618     initEvents : function() 
25619     {
25620         if(this.isSubMenu){
25621             return;
25622         }
25623         
25624         this.hidden = true;
25625         
25626         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25627         this.triggerEl.on('click', this.onTriggerPress, this);
25628         
25629         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25630         this.buttonEl.on('click', this.onClick, this);
25631         
25632     },
25633     
25634     list : function()
25635     {
25636         if(this.isSubMenu){
25637             return this.el;
25638         }
25639         
25640         return this.el.select('ul.dropdown-menu', true).first();
25641     },
25642     
25643     onClick : function(e)
25644     {
25645         this.fireEvent("click", this, e);
25646     },
25647     
25648     onTriggerPress  : function(e)
25649     {   
25650         if (this.isVisible()) {
25651             this.hide();
25652         } else {
25653             this.show();
25654         }
25655     },
25656     
25657     isVisible : function(){
25658         return !this.hidden;
25659     },
25660     
25661     show : function()
25662     {
25663         this.fireEvent("beforeshow", this);
25664         
25665         this.hidden = false;
25666         this.el.addClass('open');
25667         
25668         Roo.get(document).on("mouseup", this.onMouseUp, this);
25669         
25670         this.fireEvent("show", this);
25671         
25672         
25673     },
25674     
25675     hide : function()
25676     {
25677         this.fireEvent("beforehide", this);
25678         
25679         this.hidden = true;
25680         this.el.removeClass('open');
25681         
25682         Roo.get(document).un("mouseup", this.onMouseUp);
25683         
25684         this.fireEvent("hide", this);
25685     },
25686     
25687     onMouseUp : function()
25688     {
25689         this.hide();
25690     }
25691     
25692 });
25693
25694  
25695  /*
25696  * - LGPL
25697  *
25698  * menu item
25699  * 
25700  */
25701 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25702
25703 /**
25704  * @class Roo.bootstrap.menu.Item
25705  * @extends Roo.bootstrap.Component
25706  * Bootstrap MenuItem class
25707  * @cfg {Boolean} submenu (true | false) default false
25708  * @cfg {String} html text of the item
25709  * @cfg {String} href the link
25710  * @cfg {Boolean} disable (true | false) default false
25711  * @cfg {Boolean} preventDefault (true | false) default true
25712  * @cfg {String} icon Font awesome icon
25713  * @cfg {String} pos Submenu align to (left | right) default right 
25714  * 
25715  * 
25716  * @constructor
25717  * Create a new Item
25718  * @param {Object} config The config object
25719  */
25720
25721
25722 Roo.bootstrap.menu.Item = function(config){
25723     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25724     this.addEvents({
25725         /**
25726          * @event mouseover
25727          * Fires when the mouse is hovering over this menu
25728          * @param {Roo.bootstrap.menu.Item} this
25729          * @param {Roo.EventObject} e
25730          */
25731         mouseover : true,
25732         /**
25733          * @event mouseout
25734          * Fires when the mouse exits this menu
25735          * @param {Roo.bootstrap.menu.Item} this
25736          * @param {Roo.EventObject} e
25737          */
25738         mouseout : true,
25739         // raw events
25740         /**
25741          * @event click
25742          * The raw click event for the entire grid.
25743          * @param {Roo.EventObject} e
25744          */
25745         click : true
25746     });
25747 };
25748
25749 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25750     
25751     submenu : false,
25752     href : '',
25753     html : '',
25754     preventDefault: true,
25755     disable : false,
25756     icon : false,
25757     pos : 'right',
25758     
25759     getAutoCreate : function()
25760     {
25761         var text = [
25762             {
25763                 tag : 'span',
25764                 cls : 'roo-menu-item-text',
25765                 html : this.html
25766             }
25767         ];
25768         
25769         if(this.icon){
25770             text.unshift({
25771                 tag : 'i',
25772                 cls : 'fa ' + this.icon
25773             })
25774         }
25775         
25776         var cfg = {
25777             tag : 'li',
25778             cn : [
25779                 {
25780                     tag : 'a',
25781                     href : this.href || '#',
25782                     cn : text
25783                 }
25784             ]
25785         };
25786         
25787         if(this.disable){
25788             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25789         }
25790         
25791         if(this.submenu){
25792             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25793             
25794             if(this.pos == 'left'){
25795                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25796             }
25797         }
25798         
25799         return cfg;
25800     },
25801     
25802     initEvents : function() 
25803     {
25804         this.el.on('mouseover', this.onMouseOver, this);
25805         this.el.on('mouseout', this.onMouseOut, this);
25806         
25807         this.el.select('a', true).first().on('click', this.onClick, this);
25808         
25809     },
25810     
25811     onClick : function(e)
25812     {
25813         if(this.preventDefault){
25814             e.preventDefault();
25815         }
25816         
25817         this.fireEvent("click", this, e);
25818     },
25819     
25820     onMouseOver : function(e)
25821     {
25822         if(this.submenu && this.pos == 'left'){
25823             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25824         }
25825         
25826         this.fireEvent("mouseover", this, e);
25827     },
25828     
25829     onMouseOut : function(e)
25830     {
25831         this.fireEvent("mouseout", this, e);
25832     }
25833 });
25834
25835  
25836
25837  /*
25838  * - LGPL
25839  *
25840  * menu separator
25841  * 
25842  */
25843 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25844
25845 /**
25846  * @class Roo.bootstrap.menu.Separator
25847  * @extends Roo.bootstrap.Component
25848  * Bootstrap Separator class
25849  * 
25850  * @constructor
25851  * Create a new Separator
25852  * @param {Object} config The config object
25853  */
25854
25855
25856 Roo.bootstrap.menu.Separator = function(config){
25857     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25858 };
25859
25860 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25861     
25862     getAutoCreate : function(){
25863         var cfg = {
25864             tag : 'li',
25865             cls: 'divider'
25866         };
25867         
25868         return cfg;
25869     }
25870    
25871 });
25872
25873  
25874
25875  /*
25876  * - LGPL
25877  *
25878  * Tooltip
25879  * 
25880  */
25881
25882 /**
25883  * @class Roo.bootstrap.Tooltip
25884  * Bootstrap Tooltip class
25885  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25886  * to determine which dom element triggers the tooltip.
25887  * 
25888  * It needs to add support for additional attributes like tooltip-position
25889  * 
25890  * @constructor
25891  * Create a new Toolti
25892  * @param {Object} config The config object
25893  */
25894
25895 Roo.bootstrap.Tooltip = function(config){
25896     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25897     
25898     this.alignment = Roo.bootstrap.Tooltip.alignment;
25899     
25900     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25901         this.alignment = config.alignment;
25902     }
25903     
25904 };
25905
25906 Roo.apply(Roo.bootstrap.Tooltip, {
25907     /**
25908      * @function init initialize tooltip monitoring.
25909      * @static
25910      */
25911     currentEl : false,
25912     currentTip : false,
25913     currentRegion : false,
25914     
25915     //  init : delay?
25916     
25917     init : function()
25918     {
25919         Roo.get(document).on('mouseover', this.enter ,this);
25920         Roo.get(document).on('mouseout', this.leave, this);
25921          
25922         
25923         this.currentTip = new Roo.bootstrap.Tooltip();
25924     },
25925     
25926     enter : function(ev)
25927     {
25928         var dom = ev.getTarget();
25929         
25930         //Roo.log(['enter',dom]);
25931         var el = Roo.fly(dom);
25932         if (this.currentEl) {
25933             //Roo.log(dom);
25934             //Roo.log(this.currentEl);
25935             //Roo.log(this.currentEl.contains(dom));
25936             if (this.currentEl == el) {
25937                 return;
25938             }
25939             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25940                 return;
25941             }
25942
25943         }
25944         
25945         if (this.currentTip.el) {
25946             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25947         }    
25948         //Roo.log(ev);
25949         
25950         if(!el || el.dom == document){
25951             return;
25952         }
25953         
25954         var bindEl = el;
25955         
25956         // you can not look for children, as if el is the body.. then everythign is the child..
25957         if (!el.attr('tooltip')) { //
25958             if (!el.select("[tooltip]").elements.length) {
25959                 return;
25960             }
25961             // is the mouse over this child...?
25962             bindEl = el.select("[tooltip]").first();
25963             var xy = ev.getXY();
25964             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25965                 //Roo.log("not in region.");
25966                 return;
25967             }
25968             //Roo.log("child element over..");
25969             
25970         }
25971         this.currentEl = bindEl;
25972         this.currentTip.bind(bindEl);
25973         this.currentRegion = Roo.lib.Region.getRegion(dom);
25974         this.currentTip.enter();
25975         
25976     },
25977     leave : function(ev)
25978     {
25979         var dom = ev.getTarget();
25980         //Roo.log(['leave',dom]);
25981         if (!this.currentEl) {
25982             return;
25983         }
25984         
25985         
25986         if (dom != this.currentEl.dom) {
25987             return;
25988         }
25989         var xy = ev.getXY();
25990         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25991             return;
25992         }
25993         // only activate leave if mouse cursor is outside... bounding box..
25994         
25995         
25996         
25997         
25998         if (this.currentTip) {
25999             this.currentTip.leave();
26000         }
26001         //Roo.log('clear currentEl');
26002         this.currentEl = false;
26003         
26004         
26005     },
26006     alignment : {
26007         'left' : ['r-l', [-2,0], 'right'],
26008         'right' : ['l-r', [2,0], 'left'],
26009         'bottom' : ['t-b', [0,2], 'top'],
26010         'top' : [ 'b-t', [0,-2], 'bottom']
26011     }
26012     
26013 });
26014
26015
26016 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26017     
26018     
26019     bindEl : false,
26020     
26021     delay : null, // can be { show : 300 , hide: 500}
26022     
26023     timeout : null,
26024     
26025     hoverState : null, //???
26026     
26027     placement : 'bottom', 
26028     
26029     alignment : false,
26030     
26031     getAutoCreate : function(){
26032     
26033         var cfg = {
26034            cls : 'tooltip',
26035            role : 'tooltip',
26036            cn : [
26037                 {
26038                     cls : 'tooltip-arrow'
26039                 },
26040                 {
26041                     cls : 'tooltip-inner'
26042                 }
26043            ]
26044         };
26045         
26046         return cfg;
26047     },
26048     bind : function(el)
26049     {
26050         this.bindEl = el;
26051     },
26052       
26053     
26054     enter : function () {
26055        
26056         if (this.timeout != null) {
26057             clearTimeout(this.timeout);
26058         }
26059         
26060         this.hoverState = 'in';
26061          //Roo.log("enter - show");
26062         if (!this.delay || !this.delay.show) {
26063             this.show();
26064             return;
26065         }
26066         var _t = this;
26067         this.timeout = setTimeout(function () {
26068             if (_t.hoverState == 'in') {
26069                 _t.show();
26070             }
26071         }, this.delay.show);
26072     },
26073     leave : function()
26074     {
26075         clearTimeout(this.timeout);
26076     
26077         this.hoverState = 'out';
26078          if (!this.delay || !this.delay.hide) {
26079             this.hide();
26080             return;
26081         }
26082        
26083         var _t = this;
26084         this.timeout = setTimeout(function () {
26085             //Roo.log("leave - timeout");
26086             
26087             if (_t.hoverState == 'out') {
26088                 _t.hide();
26089                 Roo.bootstrap.Tooltip.currentEl = false;
26090             }
26091         }, delay);
26092     },
26093     
26094     show : function (msg)
26095     {
26096         if (!this.el) {
26097             this.render(document.body);
26098         }
26099         // set content.
26100         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26101         
26102         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26103         
26104         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26105         
26106         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26107         
26108         var placement = typeof this.placement == 'function' ?
26109             this.placement.call(this, this.el, on_el) :
26110             this.placement;
26111             
26112         var autoToken = /\s?auto?\s?/i;
26113         var autoPlace = autoToken.test(placement);
26114         if (autoPlace) {
26115             placement = placement.replace(autoToken, '') || 'top';
26116         }
26117         
26118         //this.el.detach()
26119         //this.el.setXY([0,0]);
26120         this.el.show();
26121         //this.el.dom.style.display='block';
26122         
26123         //this.el.appendTo(on_el);
26124         
26125         var p = this.getPosition();
26126         var box = this.el.getBox();
26127         
26128         if (autoPlace) {
26129             // fixme..
26130         }
26131         
26132         var align = this.alignment[placement];
26133         
26134         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26135         
26136         if(placement == 'top' || placement == 'bottom'){
26137             if(xy[0] < 0){
26138                 placement = 'right';
26139             }
26140             
26141             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26142                 placement = 'left';
26143             }
26144             
26145             var scroll = Roo.select('body', true).first().getScroll();
26146             
26147             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26148                 placement = 'top';
26149             }
26150             
26151         }
26152         
26153         this.el.alignTo(this.bindEl, align[0],align[1]);
26154         //var arrow = this.el.select('.arrow',true).first();
26155         //arrow.set(align[2], 
26156         
26157         this.el.addClass(placement);
26158         
26159         this.el.addClass('in fade');
26160         
26161         this.hoverState = null;
26162         
26163         if (this.el.hasClass('fade')) {
26164             // fade it?
26165         }
26166         
26167     },
26168     hide : function()
26169     {
26170          
26171         if (!this.el) {
26172             return;
26173         }
26174         //this.el.setXY([0,0]);
26175         this.el.removeClass('in');
26176         //this.el.hide();
26177         
26178     }
26179     
26180 });
26181  
26182
26183  /*
26184  * - LGPL
26185  *
26186  * Location Picker
26187  * 
26188  */
26189
26190 /**
26191  * @class Roo.bootstrap.LocationPicker
26192  * @extends Roo.bootstrap.Component
26193  * Bootstrap LocationPicker class
26194  * @cfg {Number} latitude Position when init default 0
26195  * @cfg {Number} longitude Position when init default 0
26196  * @cfg {Number} zoom default 15
26197  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26198  * @cfg {Boolean} mapTypeControl default false
26199  * @cfg {Boolean} disableDoubleClickZoom default false
26200  * @cfg {Boolean} scrollwheel default true
26201  * @cfg {Boolean} streetViewControl default false
26202  * @cfg {Number} radius default 0
26203  * @cfg {String} locationName
26204  * @cfg {Boolean} draggable default true
26205  * @cfg {Boolean} enableAutocomplete default false
26206  * @cfg {Boolean} enableReverseGeocode default true
26207  * @cfg {String} markerTitle
26208  * 
26209  * @constructor
26210  * Create a new LocationPicker
26211  * @param {Object} config The config object
26212  */
26213
26214
26215 Roo.bootstrap.LocationPicker = function(config){
26216     
26217     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26218     
26219     this.addEvents({
26220         /**
26221          * @event initial
26222          * Fires when the picker initialized.
26223          * @param {Roo.bootstrap.LocationPicker} this
26224          * @param {Google Location} location
26225          */
26226         initial : true,
26227         /**
26228          * @event positionchanged
26229          * Fires when the picker position changed.
26230          * @param {Roo.bootstrap.LocationPicker} this
26231          * @param {Google Location} location
26232          */
26233         positionchanged : true,
26234         /**
26235          * @event resize
26236          * Fires when the map resize.
26237          * @param {Roo.bootstrap.LocationPicker} this
26238          */
26239         resize : true,
26240         /**
26241          * @event show
26242          * Fires when the map show.
26243          * @param {Roo.bootstrap.LocationPicker} this
26244          */
26245         show : true,
26246         /**
26247          * @event hide
26248          * Fires when the map hide.
26249          * @param {Roo.bootstrap.LocationPicker} this
26250          */
26251         hide : true,
26252         /**
26253          * @event mapClick
26254          * Fires when click the map.
26255          * @param {Roo.bootstrap.LocationPicker} this
26256          * @param {Map event} e
26257          */
26258         mapClick : true,
26259         /**
26260          * @event mapRightClick
26261          * Fires when right click the map.
26262          * @param {Roo.bootstrap.LocationPicker} this
26263          * @param {Map event} e
26264          */
26265         mapRightClick : true,
26266         /**
26267          * @event markerClick
26268          * Fires when click the marker.
26269          * @param {Roo.bootstrap.LocationPicker} this
26270          * @param {Map event} e
26271          */
26272         markerClick : true,
26273         /**
26274          * @event markerRightClick
26275          * Fires when right click the marker.
26276          * @param {Roo.bootstrap.LocationPicker} this
26277          * @param {Map event} e
26278          */
26279         markerRightClick : true,
26280         /**
26281          * @event OverlayViewDraw
26282          * Fires when OverlayView Draw
26283          * @param {Roo.bootstrap.LocationPicker} this
26284          */
26285         OverlayViewDraw : true,
26286         /**
26287          * @event OverlayViewOnAdd
26288          * Fires when OverlayView Draw
26289          * @param {Roo.bootstrap.LocationPicker} this
26290          */
26291         OverlayViewOnAdd : true,
26292         /**
26293          * @event OverlayViewOnRemove
26294          * Fires when OverlayView Draw
26295          * @param {Roo.bootstrap.LocationPicker} this
26296          */
26297         OverlayViewOnRemove : true,
26298         /**
26299          * @event OverlayViewShow
26300          * Fires when OverlayView Draw
26301          * @param {Roo.bootstrap.LocationPicker} this
26302          * @param {Pixel} cpx
26303          */
26304         OverlayViewShow : true,
26305         /**
26306          * @event OverlayViewHide
26307          * Fires when OverlayView Draw
26308          * @param {Roo.bootstrap.LocationPicker} this
26309          */
26310         OverlayViewHide : true,
26311         /**
26312          * @event loadexception
26313          * Fires when load google lib failed.
26314          * @param {Roo.bootstrap.LocationPicker} this
26315          */
26316         loadexception : true
26317     });
26318         
26319 };
26320
26321 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26322     
26323     gMapContext: false,
26324     
26325     latitude: 0,
26326     longitude: 0,
26327     zoom: 15,
26328     mapTypeId: false,
26329     mapTypeControl: false,
26330     disableDoubleClickZoom: false,
26331     scrollwheel: true,
26332     streetViewControl: false,
26333     radius: 0,
26334     locationName: '',
26335     draggable: true,
26336     enableAutocomplete: false,
26337     enableReverseGeocode: true,
26338     markerTitle: '',
26339     
26340     getAutoCreate: function()
26341     {
26342
26343         var cfg = {
26344             tag: 'div',
26345             cls: 'roo-location-picker'
26346         };
26347         
26348         return cfg
26349     },
26350     
26351     initEvents: function(ct, position)
26352     {       
26353         if(!this.el.getWidth() || this.isApplied()){
26354             return;
26355         }
26356         
26357         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26358         
26359         this.initial();
26360     },
26361     
26362     initial: function()
26363     {
26364         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26365             this.fireEvent('loadexception', this);
26366             return;
26367         }
26368         
26369         if(!this.mapTypeId){
26370             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26371         }
26372         
26373         this.gMapContext = this.GMapContext();
26374         
26375         this.initOverlayView();
26376         
26377         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26378         
26379         var _this = this;
26380                 
26381         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26382             _this.setPosition(_this.gMapContext.marker.position);
26383         });
26384         
26385         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26386             _this.fireEvent('mapClick', this, event);
26387             
26388         });
26389
26390         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26391             _this.fireEvent('mapRightClick', this, event);
26392             
26393         });
26394         
26395         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26396             _this.fireEvent('markerClick', this, event);
26397             
26398         });
26399
26400         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26401             _this.fireEvent('markerRightClick', this, event);
26402             
26403         });
26404         
26405         this.setPosition(this.gMapContext.location);
26406         
26407         this.fireEvent('initial', this, this.gMapContext.location);
26408     },
26409     
26410     initOverlayView: function()
26411     {
26412         var _this = this;
26413         
26414         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26415             
26416             draw: function()
26417             {
26418                 _this.fireEvent('OverlayViewDraw', _this);
26419             },
26420             
26421             onAdd: function()
26422             {
26423                 _this.fireEvent('OverlayViewOnAdd', _this);
26424             },
26425             
26426             onRemove: function()
26427             {
26428                 _this.fireEvent('OverlayViewOnRemove', _this);
26429             },
26430             
26431             show: function(cpx)
26432             {
26433                 _this.fireEvent('OverlayViewShow', _this, cpx);
26434             },
26435             
26436             hide: function()
26437             {
26438                 _this.fireEvent('OverlayViewHide', _this);
26439             }
26440             
26441         });
26442     },
26443     
26444     fromLatLngToContainerPixel: function(event)
26445     {
26446         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26447     },
26448     
26449     isApplied: function() 
26450     {
26451         return this.getGmapContext() == false ? false : true;
26452     },
26453     
26454     getGmapContext: function() 
26455     {
26456         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26457     },
26458     
26459     GMapContext: function() 
26460     {
26461         var position = new google.maps.LatLng(this.latitude, this.longitude);
26462         
26463         var _map = new google.maps.Map(this.el.dom, {
26464             center: position,
26465             zoom: this.zoom,
26466             mapTypeId: this.mapTypeId,
26467             mapTypeControl: this.mapTypeControl,
26468             disableDoubleClickZoom: this.disableDoubleClickZoom,
26469             scrollwheel: this.scrollwheel,
26470             streetViewControl: this.streetViewControl,
26471             locationName: this.locationName,
26472             draggable: this.draggable,
26473             enableAutocomplete: this.enableAutocomplete,
26474             enableReverseGeocode: this.enableReverseGeocode
26475         });
26476         
26477         var _marker = new google.maps.Marker({
26478             position: position,
26479             map: _map,
26480             title: this.markerTitle,
26481             draggable: this.draggable
26482         });
26483         
26484         return {
26485             map: _map,
26486             marker: _marker,
26487             circle: null,
26488             location: position,
26489             radius: this.radius,
26490             locationName: this.locationName,
26491             addressComponents: {
26492                 formatted_address: null,
26493                 addressLine1: null,
26494                 addressLine2: null,
26495                 streetName: null,
26496                 streetNumber: null,
26497                 city: null,
26498                 district: null,
26499                 state: null,
26500                 stateOrProvince: null
26501             },
26502             settings: this,
26503             domContainer: this.el.dom,
26504             geodecoder: new google.maps.Geocoder()
26505         };
26506     },
26507     
26508     drawCircle: function(center, radius, options) 
26509     {
26510         if (this.gMapContext.circle != null) {
26511             this.gMapContext.circle.setMap(null);
26512         }
26513         if (radius > 0) {
26514             radius *= 1;
26515             options = Roo.apply({}, options, {
26516                 strokeColor: "#0000FF",
26517                 strokeOpacity: .35,
26518                 strokeWeight: 2,
26519                 fillColor: "#0000FF",
26520                 fillOpacity: .2
26521             });
26522             
26523             options.map = this.gMapContext.map;
26524             options.radius = radius;
26525             options.center = center;
26526             this.gMapContext.circle = new google.maps.Circle(options);
26527             return this.gMapContext.circle;
26528         }
26529         
26530         return null;
26531     },
26532     
26533     setPosition: function(location) 
26534     {
26535         this.gMapContext.location = location;
26536         this.gMapContext.marker.setPosition(location);
26537         this.gMapContext.map.panTo(location);
26538         this.drawCircle(location, this.gMapContext.radius, {});
26539         
26540         var _this = this;
26541         
26542         if (this.gMapContext.settings.enableReverseGeocode) {
26543             this.gMapContext.geodecoder.geocode({
26544                 latLng: this.gMapContext.location
26545             }, function(results, status) {
26546                 
26547                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26548                     _this.gMapContext.locationName = results[0].formatted_address;
26549                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26550                     
26551                     _this.fireEvent('positionchanged', this, location);
26552                 }
26553             });
26554             
26555             return;
26556         }
26557         
26558         this.fireEvent('positionchanged', this, location);
26559     },
26560     
26561     resize: function()
26562     {
26563         google.maps.event.trigger(this.gMapContext.map, "resize");
26564         
26565         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26566         
26567         this.fireEvent('resize', this);
26568     },
26569     
26570     setPositionByLatLng: function(latitude, longitude)
26571     {
26572         this.setPosition(new google.maps.LatLng(latitude, longitude));
26573     },
26574     
26575     getCurrentPosition: function() 
26576     {
26577         return {
26578             latitude: this.gMapContext.location.lat(),
26579             longitude: this.gMapContext.location.lng()
26580         };
26581     },
26582     
26583     getAddressName: function() 
26584     {
26585         return this.gMapContext.locationName;
26586     },
26587     
26588     getAddressComponents: function() 
26589     {
26590         return this.gMapContext.addressComponents;
26591     },
26592     
26593     address_component_from_google_geocode: function(address_components) 
26594     {
26595         var result = {};
26596         
26597         for (var i = 0; i < address_components.length; i++) {
26598             var component = address_components[i];
26599             if (component.types.indexOf("postal_code") >= 0) {
26600                 result.postalCode = component.short_name;
26601             } else if (component.types.indexOf("street_number") >= 0) {
26602                 result.streetNumber = component.short_name;
26603             } else if (component.types.indexOf("route") >= 0) {
26604                 result.streetName = component.short_name;
26605             } else if (component.types.indexOf("neighborhood") >= 0) {
26606                 result.city = component.short_name;
26607             } else if (component.types.indexOf("locality") >= 0) {
26608                 result.city = component.short_name;
26609             } else if (component.types.indexOf("sublocality") >= 0) {
26610                 result.district = component.short_name;
26611             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26612                 result.stateOrProvince = component.short_name;
26613             } else if (component.types.indexOf("country") >= 0) {
26614                 result.country = component.short_name;
26615             }
26616         }
26617         
26618         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26619         result.addressLine2 = "";
26620         return result;
26621     },
26622     
26623     setZoomLevel: function(zoom)
26624     {
26625         this.gMapContext.map.setZoom(zoom);
26626     },
26627     
26628     show: function()
26629     {
26630         if(!this.el){
26631             return;
26632         }
26633         
26634         this.el.show();
26635         
26636         this.resize();
26637         
26638         this.fireEvent('show', this);
26639     },
26640     
26641     hide: function()
26642     {
26643         if(!this.el){
26644             return;
26645         }
26646         
26647         this.el.hide();
26648         
26649         this.fireEvent('hide', this);
26650     }
26651     
26652 });
26653
26654 Roo.apply(Roo.bootstrap.LocationPicker, {
26655     
26656     OverlayView : function(map, options)
26657     {
26658         options = options || {};
26659         
26660         this.setMap(map);
26661     }
26662     
26663     
26664 });/*
26665  * - LGPL
26666  *
26667  * Alert
26668  * 
26669  */
26670
26671 /**
26672  * @class Roo.bootstrap.Alert
26673  * @extends Roo.bootstrap.Component
26674  * Bootstrap Alert class
26675  * @cfg {String} title The title of alert
26676  * @cfg {String} html The content of alert
26677  * @cfg {String} weight (  success | info | warning | danger )
26678  * @cfg {String} faicon font-awesomeicon
26679  * 
26680  * @constructor
26681  * Create a new alert
26682  * @param {Object} config The config object
26683  */
26684
26685
26686 Roo.bootstrap.Alert = function(config){
26687     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26688     
26689 };
26690
26691 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26692     
26693     title: '',
26694     html: '',
26695     weight: false,
26696     faicon: false,
26697     
26698     getAutoCreate : function()
26699     {
26700         
26701         var cfg = {
26702             tag : 'div',
26703             cls : 'alert',
26704             cn : [
26705                 {
26706                     tag : 'i',
26707                     cls : 'roo-alert-icon'
26708                     
26709                 },
26710                 {
26711                     tag : 'b',
26712                     cls : 'roo-alert-title',
26713                     html : this.title
26714                 },
26715                 {
26716                     tag : 'span',
26717                     cls : 'roo-alert-text',
26718                     html : this.html
26719                 }
26720             ]
26721         };
26722         
26723         if(this.faicon){
26724             cfg.cn[0].cls += ' fa ' + this.faicon;
26725         }
26726         
26727         if(this.weight){
26728             cfg.cls += ' alert-' + this.weight;
26729         }
26730         
26731         return cfg;
26732     },
26733     
26734     initEvents: function() 
26735     {
26736         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26737     },
26738     
26739     setTitle : function(str)
26740     {
26741         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26742     },
26743     
26744     setText : function(str)
26745     {
26746         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26747     },
26748     
26749     setWeight : function(weight)
26750     {
26751         if(this.weight){
26752             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26753         }
26754         
26755         this.weight = weight;
26756         
26757         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26758     },
26759     
26760     setIcon : function(icon)
26761     {
26762         if(this.faicon){
26763             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26764         }
26765         
26766         this.faicon = icon;
26767         
26768         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26769     },
26770     
26771     hide: function() 
26772     {
26773         this.el.hide();   
26774     },
26775     
26776     show: function() 
26777     {  
26778         this.el.show();   
26779     }
26780     
26781 });
26782
26783  
26784 /*
26785 * Licence: LGPL
26786 */
26787
26788 /**
26789  * @class Roo.bootstrap.UploadCropbox
26790  * @extends Roo.bootstrap.Component
26791  * Bootstrap UploadCropbox class
26792  * @cfg {String} emptyText show when image has been loaded
26793  * @cfg {String} rotateNotify show when image too small to rotate
26794  * @cfg {Number} errorTimeout default 3000
26795  * @cfg {Number} minWidth default 300
26796  * @cfg {Number} minHeight default 300
26797  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26798  * @cfg {Boolean} isDocument (true|false) default false
26799  * @cfg {String} url action url
26800  * @cfg {String} paramName default 'imageUpload'
26801  * @cfg {String} method default POST
26802  * @cfg {Boolean} loadMask (true|false) default true
26803  * @cfg {Boolean} loadingText default 'Loading...'
26804  * 
26805  * @constructor
26806  * Create a new UploadCropbox
26807  * @param {Object} config The config object
26808  */
26809
26810 Roo.bootstrap.UploadCropbox = function(config){
26811     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26812     
26813     this.addEvents({
26814         /**
26815          * @event beforeselectfile
26816          * Fire before select file
26817          * @param {Roo.bootstrap.UploadCropbox} this
26818          */
26819         "beforeselectfile" : true,
26820         /**
26821          * @event initial
26822          * Fire after initEvent
26823          * @param {Roo.bootstrap.UploadCropbox} this
26824          */
26825         "initial" : true,
26826         /**
26827          * @event crop
26828          * Fire after initEvent
26829          * @param {Roo.bootstrap.UploadCropbox} this
26830          * @param {String} data
26831          */
26832         "crop" : true,
26833         /**
26834          * @event prepare
26835          * Fire when preparing the file data
26836          * @param {Roo.bootstrap.UploadCropbox} this
26837          * @param {Object} file
26838          */
26839         "prepare" : true,
26840         /**
26841          * @event exception
26842          * Fire when get exception
26843          * @param {Roo.bootstrap.UploadCropbox} this
26844          * @param {XMLHttpRequest} xhr
26845          */
26846         "exception" : true,
26847         /**
26848          * @event beforeloadcanvas
26849          * Fire before load the canvas
26850          * @param {Roo.bootstrap.UploadCropbox} this
26851          * @param {String} src
26852          */
26853         "beforeloadcanvas" : true,
26854         /**
26855          * @event trash
26856          * Fire when trash image
26857          * @param {Roo.bootstrap.UploadCropbox} this
26858          */
26859         "trash" : true,
26860         /**
26861          * @event download
26862          * Fire when download the image
26863          * @param {Roo.bootstrap.UploadCropbox} this
26864          */
26865         "download" : true,
26866         /**
26867          * @event footerbuttonclick
26868          * Fire when footerbuttonclick
26869          * @param {Roo.bootstrap.UploadCropbox} this
26870          * @param {String} type
26871          */
26872         "footerbuttonclick" : true,
26873         /**
26874          * @event resize
26875          * Fire when resize
26876          * @param {Roo.bootstrap.UploadCropbox} this
26877          */
26878         "resize" : true,
26879         /**
26880          * @event rotate
26881          * Fire when rotate the image
26882          * @param {Roo.bootstrap.UploadCropbox} this
26883          * @param {String} pos
26884          */
26885         "rotate" : true,
26886         /**
26887          * @event inspect
26888          * Fire when inspect the file
26889          * @param {Roo.bootstrap.UploadCropbox} this
26890          * @param {Object} file
26891          */
26892         "inspect" : true,
26893         /**
26894          * @event upload
26895          * Fire when xhr upload the file
26896          * @param {Roo.bootstrap.UploadCropbox} this
26897          * @param {Object} data
26898          */
26899         "upload" : true,
26900         /**
26901          * @event arrange
26902          * Fire when arrange the file data
26903          * @param {Roo.bootstrap.UploadCropbox} this
26904          * @param {Object} formData
26905          */
26906         "arrange" : true
26907     });
26908     
26909     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26910 };
26911
26912 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26913     
26914     emptyText : 'Click to upload image',
26915     rotateNotify : 'Image is too small to rotate',
26916     errorTimeout : 3000,
26917     scale : 0,
26918     baseScale : 1,
26919     rotate : 0,
26920     dragable : false,
26921     pinching : false,
26922     mouseX : 0,
26923     mouseY : 0,
26924     cropData : false,
26925     minWidth : 300,
26926     minHeight : 300,
26927     file : false,
26928     exif : {},
26929     baseRotate : 1,
26930     cropType : 'image/jpeg',
26931     buttons : false,
26932     canvasLoaded : false,
26933     isDocument : false,
26934     method : 'POST',
26935     paramName : 'imageUpload',
26936     loadMask : true,
26937     loadingText : 'Loading...',
26938     maskEl : false,
26939     
26940     getAutoCreate : function()
26941     {
26942         var cfg = {
26943             tag : 'div',
26944             cls : 'roo-upload-cropbox',
26945             cn : [
26946                 {
26947                     tag : 'input',
26948                     cls : 'roo-upload-cropbox-selector',
26949                     type : 'file'
26950                 },
26951                 {
26952                     tag : 'div',
26953                     cls : 'roo-upload-cropbox-body',
26954                     style : 'cursor:pointer',
26955                     cn : [
26956                         {
26957                             tag : 'div',
26958                             cls : 'roo-upload-cropbox-preview'
26959                         },
26960                         {
26961                             tag : 'div',
26962                             cls : 'roo-upload-cropbox-thumb'
26963                         },
26964                         {
26965                             tag : 'div',
26966                             cls : 'roo-upload-cropbox-empty-notify',
26967                             html : this.emptyText
26968                         },
26969                         {
26970                             tag : 'div',
26971                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26972                             html : this.rotateNotify
26973                         }
26974                     ]
26975                 },
26976                 {
26977                     tag : 'div',
26978                     cls : 'roo-upload-cropbox-footer',
26979                     cn : {
26980                         tag : 'div',
26981                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26982                         cn : []
26983                     }
26984                 }
26985             ]
26986         };
26987         
26988         return cfg;
26989     },
26990     
26991     onRender : function(ct, position)
26992     {
26993         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26994         
26995         if (this.buttons.length) {
26996             
26997             Roo.each(this.buttons, function(bb) {
26998                 
26999                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27000                 
27001                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27002                 
27003             }, this);
27004         }
27005         
27006         if(this.loadMask){
27007             this.maskEl = this.el;
27008         }
27009     },
27010     
27011     initEvents : function()
27012     {
27013         this.urlAPI = (window.createObjectURL && window) || 
27014                                 (window.URL && URL.revokeObjectURL && URL) || 
27015                                 (window.webkitURL && webkitURL);
27016                         
27017         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27018         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27019         
27020         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27021         this.selectorEl.hide();
27022         
27023         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27024         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27025         
27026         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27027         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27028         this.thumbEl.hide();
27029         
27030         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27031         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27032         
27033         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27034         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27035         this.errorEl.hide();
27036         
27037         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27038         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27039         this.footerEl.hide();
27040         
27041         this.setThumbBoxSize();
27042         
27043         this.bind();
27044         
27045         this.resize();
27046         
27047         this.fireEvent('initial', this);
27048     },
27049
27050     bind : function()
27051     {
27052         var _this = this;
27053         
27054         window.addEventListener("resize", function() { _this.resize(); } );
27055         
27056         this.bodyEl.on('click', this.beforeSelectFile, this);
27057         
27058         if(Roo.isTouch){
27059             this.bodyEl.on('touchstart', this.onTouchStart, this);
27060             this.bodyEl.on('touchmove', this.onTouchMove, this);
27061             this.bodyEl.on('touchend', this.onTouchEnd, this);
27062         }
27063         
27064         if(!Roo.isTouch){
27065             this.bodyEl.on('mousedown', this.onMouseDown, this);
27066             this.bodyEl.on('mousemove', this.onMouseMove, this);
27067             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27068             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27069             Roo.get(document).on('mouseup', this.onMouseUp, this);
27070         }
27071         
27072         this.selectorEl.on('change', this.onFileSelected, this);
27073     },
27074     
27075     reset : function()
27076     {    
27077         this.scale = 0;
27078         this.baseScale = 1;
27079         this.rotate = 0;
27080         this.baseRotate = 1;
27081         this.dragable = false;
27082         this.pinching = false;
27083         this.mouseX = 0;
27084         this.mouseY = 0;
27085         this.cropData = false;
27086         this.notifyEl.dom.innerHTML = this.emptyText;
27087         
27088         this.selectorEl.dom.value = '';
27089         
27090     },
27091     
27092     resize : function()
27093     {
27094         if(this.fireEvent('resize', this) != false){
27095             this.setThumbBoxPosition();
27096             this.setCanvasPosition();
27097         }
27098     },
27099     
27100     onFooterButtonClick : function(e, el, o, type)
27101     {
27102         switch (type) {
27103             case 'rotate-left' :
27104                 this.onRotateLeft(e);
27105                 break;
27106             case 'rotate-right' :
27107                 this.onRotateRight(e);
27108                 break;
27109             case 'picture' :
27110                 this.beforeSelectFile(e);
27111                 break;
27112             case 'trash' :
27113                 this.trash(e);
27114                 break;
27115             case 'crop' :
27116                 this.crop(e);
27117                 break;
27118             case 'download' :
27119                 this.download(e);
27120                 break;
27121             default :
27122                 break;
27123         }
27124         
27125         this.fireEvent('footerbuttonclick', this, type);
27126     },
27127     
27128     beforeSelectFile : function(e)
27129     {
27130         e.preventDefault();
27131         
27132         if(this.fireEvent('beforeselectfile', this) != false){
27133             this.selectorEl.dom.click();
27134         }
27135     },
27136     
27137     onFileSelected : function(e)
27138     {
27139         e.preventDefault();
27140         
27141         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27142             return;
27143         }
27144         
27145         var file = this.selectorEl.dom.files[0];
27146         
27147         if(this.fireEvent('inspect', this, file) != false){
27148             this.prepare(file);
27149         }
27150         
27151     },
27152     
27153     trash : function(e)
27154     {
27155         this.fireEvent('trash', this);
27156     },
27157     
27158     download : function(e)
27159     {
27160         this.fireEvent('download', this);
27161     },
27162     
27163     loadCanvas : function(src)
27164     {   
27165         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27166             
27167             this.reset();
27168             
27169             this.imageEl = document.createElement('img');
27170             
27171             var _this = this;
27172             
27173             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27174             
27175             this.imageEl.src = src;
27176         }
27177     },
27178     
27179     onLoadCanvas : function()
27180     {   
27181         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27182         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27183         
27184         this.bodyEl.un('click', this.beforeSelectFile, this);
27185         
27186         this.notifyEl.hide();
27187         this.thumbEl.show();
27188         this.footerEl.show();
27189         
27190         this.baseRotateLevel();
27191         
27192         if(this.isDocument){
27193             this.setThumbBoxSize();
27194         }
27195         
27196         this.setThumbBoxPosition();
27197         
27198         this.baseScaleLevel();
27199         
27200         this.draw();
27201         
27202         this.resize();
27203         
27204         this.canvasLoaded = true;
27205         
27206         if(this.loadMask){
27207             this.maskEl.unmask();
27208         }
27209         
27210     },
27211     
27212     setCanvasPosition : function()
27213     {   
27214         if(!this.canvasEl){
27215             return;
27216         }
27217         
27218         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27219         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27220         
27221         this.previewEl.setLeft(pw);
27222         this.previewEl.setTop(ph);
27223         
27224     },
27225     
27226     onMouseDown : function(e)
27227     {   
27228         e.stopEvent();
27229         
27230         this.dragable = true;
27231         this.pinching = false;
27232         
27233         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27234             this.dragable = false;
27235             return;
27236         }
27237         
27238         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27239         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27240         
27241     },
27242     
27243     onMouseMove : function(e)
27244     {   
27245         e.stopEvent();
27246         
27247         if(!this.canvasLoaded){
27248             return;
27249         }
27250         
27251         if (!this.dragable){
27252             return;
27253         }
27254         
27255         var minX = Math.ceil(this.thumbEl.getLeft(true));
27256         var minY = Math.ceil(this.thumbEl.getTop(true));
27257         
27258         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27259         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27260         
27261         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27262         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27263         
27264         x = x - this.mouseX;
27265         y = y - this.mouseY;
27266         
27267         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27268         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27269         
27270         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27271         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27272         
27273         this.previewEl.setLeft(bgX);
27274         this.previewEl.setTop(bgY);
27275         
27276         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27277         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27278     },
27279     
27280     onMouseUp : function(e)
27281     {   
27282         e.stopEvent();
27283         
27284         this.dragable = false;
27285     },
27286     
27287     onMouseWheel : function(e)
27288     {   
27289         e.stopEvent();
27290         
27291         this.startScale = this.scale;
27292         
27293         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27294         
27295         if(!this.zoomable()){
27296             this.scale = this.startScale;
27297             return;
27298         }
27299         
27300         this.draw();
27301         
27302         return;
27303     },
27304     
27305     zoomable : function()
27306     {
27307         var minScale = this.thumbEl.getWidth() / this.minWidth;
27308         
27309         if(this.minWidth < this.minHeight){
27310             minScale = this.thumbEl.getHeight() / this.minHeight;
27311         }
27312         
27313         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27314         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27315         
27316         if(
27317                 this.isDocument &&
27318                 (this.rotate == 0 || this.rotate == 180) && 
27319                 (
27320                     width > this.imageEl.OriginWidth || 
27321                     height > this.imageEl.OriginHeight ||
27322                     (width < this.minWidth && height < this.minHeight)
27323                 )
27324         ){
27325             return false;
27326         }
27327         
27328         if(
27329                 this.isDocument &&
27330                 (this.rotate == 90 || this.rotate == 270) && 
27331                 (
27332                     width > this.imageEl.OriginWidth || 
27333                     height > this.imageEl.OriginHeight ||
27334                     (width < this.minHeight && height < this.minWidth)
27335                 )
27336         ){
27337             return false;
27338         }
27339         
27340         if(
27341                 !this.isDocument &&
27342                 (this.rotate == 0 || this.rotate == 180) && 
27343                 (
27344                     width < this.minWidth || 
27345                     width > this.imageEl.OriginWidth || 
27346                     height < this.minHeight || 
27347                     height > this.imageEl.OriginHeight
27348                 )
27349         ){
27350             return false;
27351         }
27352         
27353         if(
27354                 !this.isDocument &&
27355                 (this.rotate == 90 || this.rotate == 270) && 
27356                 (
27357                     width < this.minHeight || 
27358                     width > this.imageEl.OriginWidth || 
27359                     height < this.minWidth || 
27360                     height > this.imageEl.OriginHeight
27361                 )
27362         ){
27363             return false;
27364         }
27365         
27366         return true;
27367         
27368     },
27369     
27370     onRotateLeft : function(e)
27371     {   
27372         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27373             
27374             var minScale = this.thumbEl.getWidth() / this.minWidth;
27375             
27376             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27377             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27378             
27379             this.startScale = this.scale;
27380             
27381             while (this.getScaleLevel() < minScale){
27382             
27383                 this.scale = this.scale + 1;
27384                 
27385                 if(!this.zoomable()){
27386                     break;
27387                 }
27388                 
27389                 if(
27390                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27391                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27392                 ){
27393                     continue;
27394                 }
27395                 
27396                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27397
27398                 this.draw();
27399                 
27400                 return;
27401             }
27402             
27403             this.scale = this.startScale;
27404             
27405             this.onRotateFail();
27406             
27407             return false;
27408         }
27409         
27410         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27411
27412         if(this.isDocument){
27413             this.setThumbBoxSize();
27414             this.setThumbBoxPosition();
27415             this.setCanvasPosition();
27416         }
27417         
27418         this.draw();
27419         
27420         this.fireEvent('rotate', this, 'left');
27421         
27422     },
27423     
27424     onRotateRight : function(e)
27425     {
27426         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27427             
27428             var minScale = this.thumbEl.getWidth() / this.minWidth;
27429         
27430             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27431             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27432             
27433             this.startScale = this.scale;
27434             
27435             while (this.getScaleLevel() < minScale){
27436             
27437                 this.scale = this.scale + 1;
27438                 
27439                 if(!this.zoomable()){
27440                     break;
27441                 }
27442                 
27443                 if(
27444                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27445                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27446                 ){
27447                     continue;
27448                 }
27449                 
27450                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27451
27452                 this.draw();
27453                 
27454                 return;
27455             }
27456             
27457             this.scale = this.startScale;
27458             
27459             this.onRotateFail();
27460             
27461             return false;
27462         }
27463         
27464         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27465
27466         if(this.isDocument){
27467             this.setThumbBoxSize();
27468             this.setThumbBoxPosition();
27469             this.setCanvasPosition();
27470         }
27471         
27472         this.draw();
27473         
27474         this.fireEvent('rotate', this, 'right');
27475     },
27476     
27477     onRotateFail : function()
27478     {
27479         this.errorEl.show(true);
27480         
27481         var _this = this;
27482         
27483         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27484     },
27485     
27486     draw : function()
27487     {
27488         this.previewEl.dom.innerHTML = '';
27489         
27490         var canvasEl = document.createElement("canvas");
27491         
27492         var contextEl = canvasEl.getContext("2d");
27493         
27494         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27495         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27496         var center = this.imageEl.OriginWidth / 2;
27497         
27498         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27499             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27500             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27501             center = this.imageEl.OriginHeight / 2;
27502         }
27503         
27504         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27505         
27506         contextEl.translate(center, center);
27507         contextEl.rotate(this.rotate * Math.PI / 180);
27508
27509         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27510         
27511         this.canvasEl = document.createElement("canvas");
27512         
27513         this.contextEl = this.canvasEl.getContext("2d");
27514         
27515         switch (this.rotate) {
27516             case 0 :
27517                 
27518                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27519                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27520                 
27521                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27522                 
27523                 break;
27524             case 90 : 
27525                 
27526                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27527                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27528                 
27529                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27530                     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);
27531                     break;
27532                 }
27533                 
27534                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27535                 
27536                 break;
27537             case 180 :
27538                 
27539                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27540                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27541                 
27542                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27543                     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);
27544                     break;
27545                 }
27546                 
27547                 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);
27548                 
27549                 break;
27550             case 270 :
27551                 
27552                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27553                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27554         
27555                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27556                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27557                     break;
27558                 }
27559                 
27560                 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);
27561                 
27562                 break;
27563             default : 
27564                 break;
27565         }
27566         
27567         this.previewEl.appendChild(this.canvasEl);
27568         
27569         this.setCanvasPosition();
27570     },
27571     
27572     crop : function()
27573     {
27574         if(!this.canvasLoaded){
27575             return;
27576         }
27577         
27578         var imageCanvas = document.createElement("canvas");
27579         
27580         var imageContext = imageCanvas.getContext("2d");
27581         
27582         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27583         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27584         
27585         var center = imageCanvas.width / 2;
27586         
27587         imageContext.translate(center, center);
27588         
27589         imageContext.rotate(this.rotate * Math.PI / 180);
27590         
27591         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27592         
27593         var canvas = document.createElement("canvas");
27594         
27595         var context = canvas.getContext("2d");
27596                 
27597         canvas.width = this.minWidth;
27598         canvas.height = this.minHeight;
27599
27600         switch (this.rotate) {
27601             case 0 :
27602                 
27603                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27604                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27605                 
27606                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27607                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27608                 
27609                 var targetWidth = this.minWidth - 2 * x;
27610                 var targetHeight = this.minHeight - 2 * y;
27611                 
27612                 var scale = 1;
27613                 
27614                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27615                     scale = targetWidth / width;
27616                 }
27617                 
27618                 if(x > 0 && y == 0){
27619                     scale = targetHeight / height;
27620                 }
27621                 
27622                 if(x > 0 && y > 0){
27623                     scale = targetWidth / width;
27624                     
27625                     if(width < height){
27626                         scale = targetHeight / height;
27627                     }
27628                 }
27629                 
27630                 context.scale(scale, scale);
27631                 
27632                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27633                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27634
27635                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27636                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27637
27638                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27639                 
27640                 break;
27641             case 90 : 
27642                 
27643                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27644                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27645                 
27646                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27647                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27648                 
27649                 var targetWidth = this.minWidth - 2 * x;
27650                 var targetHeight = this.minHeight - 2 * y;
27651                 
27652                 var scale = 1;
27653                 
27654                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27655                     scale = targetWidth / width;
27656                 }
27657                 
27658                 if(x > 0 && y == 0){
27659                     scale = targetHeight / height;
27660                 }
27661                 
27662                 if(x > 0 && y > 0){
27663                     scale = targetWidth / width;
27664                     
27665                     if(width < height){
27666                         scale = targetHeight / height;
27667                     }
27668                 }
27669                 
27670                 context.scale(scale, scale);
27671                 
27672                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27673                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27674
27675                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27676                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27677                 
27678                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27679                 
27680                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27681                 
27682                 break;
27683             case 180 :
27684                 
27685                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27686                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27687                 
27688                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27689                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27690                 
27691                 var targetWidth = this.minWidth - 2 * x;
27692                 var targetHeight = this.minHeight - 2 * y;
27693                 
27694                 var scale = 1;
27695                 
27696                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27697                     scale = targetWidth / width;
27698                 }
27699                 
27700                 if(x > 0 && y == 0){
27701                     scale = targetHeight / height;
27702                 }
27703                 
27704                 if(x > 0 && y > 0){
27705                     scale = targetWidth / width;
27706                     
27707                     if(width < height){
27708                         scale = targetHeight / height;
27709                     }
27710                 }
27711                 
27712                 context.scale(scale, scale);
27713                 
27714                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27715                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27716
27717                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27718                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27719
27720                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27721                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27722                 
27723                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27724                 
27725                 break;
27726             case 270 :
27727                 
27728                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27729                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27730                 
27731                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27732                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27733                 
27734                 var targetWidth = this.minWidth - 2 * x;
27735                 var targetHeight = this.minHeight - 2 * y;
27736                 
27737                 var scale = 1;
27738                 
27739                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27740                     scale = targetWidth / width;
27741                 }
27742                 
27743                 if(x > 0 && y == 0){
27744                     scale = targetHeight / height;
27745                 }
27746                 
27747                 if(x > 0 && y > 0){
27748                     scale = targetWidth / width;
27749                     
27750                     if(width < height){
27751                         scale = targetHeight / height;
27752                     }
27753                 }
27754                 
27755                 context.scale(scale, scale);
27756                 
27757                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27758                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27759
27760                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27761                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27762                 
27763                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27764                 
27765                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27766                 
27767                 break;
27768             default : 
27769                 break;
27770         }
27771         
27772         this.cropData = canvas.toDataURL(this.cropType);
27773         
27774         if(this.fireEvent('crop', this, this.cropData) !== false){
27775             this.process(this.file, this.cropData);
27776         }
27777         
27778         return;
27779         
27780     },
27781     
27782     setThumbBoxSize : function()
27783     {
27784         var width, height;
27785         
27786         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27787             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27788             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27789             
27790             this.minWidth = width;
27791             this.minHeight = height;
27792             
27793             if(this.rotate == 90 || this.rotate == 270){
27794                 this.minWidth = height;
27795                 this.minHeight = width;
27796             }
27797         }
27798         
27799         height = 300;
27800         width = Math.ceil(this.minWidth * height / this.minHeight);
27801         
27802         if(this.minWidth > this.minHeight){
27803             width = 300;
27804             height = Math.ceil(this.minHeight * width / this.minWidth);
27805         }
27806         
27807         this.thumbEl.setStyle({
27808             width : width + 'px',
27809             height : height + 'px'
27810         });
27811
27812         return;
27813             
27814     },
27815     
27816     setThumbBoxPosition : function()
27817     {
27818         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27819         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27820         
27821         this.thumbEl.setLeft(x);
27822         this.thumbEl.setTop(y);
27823         
27824     },
27825     
27826     baseRotateLevel : function()
27827     {
27828         this.baseRotate = 1;
27829         
27830         if(
27831                 typeof(this.exif) != 'undefined' &&
27832                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27833                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27834         ){
27835             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27836         }
27837         
27838         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27839         
27840     },
27841     
27842     baseScaleLevel : function()
27843     {
27844         var width, height;
27845         
27846         if(this.isDocument){
27847             
27848             if(this.baseRotate == 6 || this.baseRotate == 8){
27849             
27850                 height = this.thumbEl.getHeight();
27851                 this.baseScale = height / this.imageEl.OriginWidth;
27852
27853                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27854                     width = this.thumbEl.getWidth();
27855                     this.baseScale = width / this.imageEl.OriginHeight;
27856                 }
27857
27858                 return;
27859             }
27860
27861             height = this.thumbEl.getHeight();
27862             this.baseScale = height / this.imageEl.OriginHeight;
27863
27864             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27865                 width = this.thumbEl.getWidth();
27866                 this.baseScale = width / this.imageEl.OriginWidth;
27867             }
27868
27869             return;
27870         }
27871         
27872         if(this.baseRotate == 6 || this.baseRotate == 8){
27873             
27874             width = this.thumbEl.getHeight();
27875             this.baseScale = width / this.imageEl.OriginHeight;
27876             
27877             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27878                 height = this.thumbEl.getWidth();
27879                 this.baseScale = height / this.imageEl.OriginHeight;
27880             }
27881             
27882             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27883                 height = this.thumbEl.getWidth();
27884                 this.baseScale = height / this.imageEl.OriginHeight;
27885                 
27886                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27887                     width = this.thumbEl.getHeight();
27888                     this.baseScale = width / this.imageEl.OriginWidth;
27889                 }
27890             }
27891             
27892             return;
27893         }
27894         
27895         width = this.thumbEl.getWidth();
27896         this.baseScale = width / this.imageEl.OriginWidth;
27897         
27898         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27899             height = this.thumbEl.getHeight();
27900             this.baseScale = height / this.imageEl.OriginHeight;
27901         }
27902         
27903         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27904             
27905             height = this.thumbEl.getHeight();
27906             this.baseScale = height / this.imageEl.OriginHeight;
27907             
27908             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27909                 width = this.thumbEl.getWidth();
27910                 this.baseScale = width / this.imageEl.OriginWidth;
27911             }
27912             
27913         }
27914         
27915         return;
27916     },
27917     
27918     getScaleLevel : function()
27919     {
27920         return this.baseScale * Math.pow(1.1, this.scale);
27921     },
27922     
27923     onTouchStart : function(e)
27924     {
27925         if(!this.canvasLoaded){
27926             this.beforeSelectFile(e);
27927             return;
27928         }
27929         
27930         var touches = e.browserEvent.touches;
27931         
27932         if(!touches){
27933             return;
27934         }
27935         
27936         if(touches.length == 1){
27937             this.onMouseDown(e);
27938             return;
27939         }
27940         
27941         if(touches.length != 2){
27942             return;
27943         }
27944         
27945         var coords = [];
27946         
27947         for(var i = 0, finger; finger = touches[i]; i++){
27948             coords.push(finger.pageX, finger.pageY);
27949         }
27950         
27951         var x = Math.pow(coords[0] - coords[2], 2);
27952         var y = Math.pow(coords[1] - coords[3], 2);
27953         
27954         this.startDistance = Math.sqrt(x + y);
27955         
27956         this.startScale = this.scale;
27957         
27958         this.pinching = true;
27959         this.dragable = false;
27960         
27961     },
27962     
27963     onTouchMove : function(e)
27964     {
27965         if(!this.pinching && !this.dragable){
27966             return;
27967         }
27968         
27969         var touches = e.browserEvent.touches;
27970         
27971         if(!touches){
27972             return;
27973         }
27974         
27975         if(this.dragable){
27976             this.onMouseMove(e);
27977             return;
27978         }
27979         
27980         var coords = [];
27981         
27982         for(var i = 0, finger; finger = touches[i]; i++){
27983             coords.push(finger.pageX, finger.pageY);
27984         }
27985         
27986         var x = Math.pow(coords[0] - coords[2], 2);
27987         var y = Math.pow(coords[1] - coords[3], 2);
27988         
27989         this.endDistance = Math.sqrt(x + y);
27990         
27991         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27992         
27993         if(!this.zoomable()){
27994             this.scale = this.startScale;
27995             return;
27996         }
27997         
27998         this.draw();
27999         
28000     },
28001     
28002     onTouchEnd : function(e)
28003     {
28004         this.pinching = false;
28005         this.dragable = false;
28006         
28007     },
28008     
28009     process : function(file, crop)
28010     {
28011         if(this.loadMask){
28012             this.maskEl.mask(this.loadingText);
28013         }
28014         
28015         this.xhr = new XMLHttpRequest();
28016         
28017         file.xhr = this.xhr;
28018
28019         this.xhr.open(this.method, this.url, true);
28020         
28021         var headers = {
28022             "Accept": "application/json",
28023             "Cache-Control": "no-cache",
28024             "X-Requested-With": "XMLHttpRequest"
28025         };
28026         
28027         for (var headerName in headers) {
28028             var headerValue = headers[headerName];
28029             if (headerValue) {
28030                 this.xhr.setRequestHeader(headerName, headerValue);
28031             }
28032         }
28033         
28034         var _this = this;
28035         
28036         this.xhr.onload = function()
28037         {
28038             _this.xhrOnLoad(_this.xhr);
28039         }
28040         
28041         this.xhr.onerror = function()
28042         {
28043             _this.xhrOnError(_this.xhr);
28044         }
28045         
28046         var formData = new FormData();
28047
28048         formData.append('returnHTML', 'NO');
28049         
28050         if(crop){
28051             formData.append('crop', crop);
28052         }
28053         
28054         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28055             formData.append(this.paramName, file, file.name);
28056         }
28057         
28058         if(typeof(file.filename) != 'undefined'){
28059             formData.append('filename', file.filename);
28060         }
28061         
28062         if(typeof(file.mimetype) != 'undefined'){
28063             formData.append('mimetype', file.mimetype);
28064         }
28065         
28066         if(this.fireEvent('arrange', this, formData) != false){
28067             this.xhr.send(formData);
28068         };
28069     },
28070     
28071     xhrOnLoad : function(xhr)
28072     {
28073         if(this.loadMask){
28074             this.maskEl.unmask();
28075         }
28076         
28077         if (xhr.readyState !== 4) {
28078             this.fireEvent('exception', this, xhr);
28079             return;
28080         }
28081
28082         var response = Roo.decode(xhr.responseText);
28083         
28084         if(!response.success){
28085             this.fireEvent('exception', this, xhr);
28086             return;
28087         }
28088         
28089         var response = Roo.decode(xhr.responseText);
28090         
28091         this.fireEvent('upload', this, response);
28092         
28093     },
28094     
28095     xhrOnError : function()
28096     {
28097         if(this.loadMask){
28098             this.maskEl.unmask();
28099         }
28100         
28101         Roo.log('xhr on error');
28102         
28103         var response = Roo.decode(xhr.responseText);
28104           
28105         Roo.log(response);
28106         
28107     },
28108     
28109     prepare : function(file)
28110     {   
28111         if(this.loadMask){
28112             this.maskEl.mask(this.loadingText);
28113         }
28114         
28115         this.file = false;
28116         this.exif = {};
28117         
28118         if(typeof(file) === 'string'){
28119             this.loadCanvas(file);
28120             return;
28121         }
28122         
28123         if(!file || !this.urlAPI){
28124             return;
28125         }
28126         
28127         this.file = file;
28128         this.cropType = file.type;
28129         
28130         var _this = this;
28131         
28132         if(this.fireEvent('prepare', this, this.file) != false){
28133             
28134             var reader = new FileReader();
28135             
28136             reader.onload = function (e) {
28137                 if (e.target.error) {
28138                     Roo.log(e.target.error);
28139                     return;
28140                 }
28141                 
28142                 var buffer = e.target.result,
28143                     dataView = new DataView(buffer),
28144                     offset = 2,
28145                     maxOffset = dataView.byteLength - 4,
28146                     markerBytes,
28147                     markerLength;
28148                 
28149                 if (dataView.getUint16(0) === 0xffd8) {
28150                     while (offset < maxOffset) {
28151                         markerBytes = dataView.getUint16(offset);
28152                         
28153                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28154                             markerLength = dataView.getUint16(offset + 2) + 2;
28155                             if (offset + markerLength > dataView.byteLength) {
28156                                 Roo.log('Invalid meta data: Invalid segment size.');
28157                                 break;
28158                             }
28159                             
28160                             if(markerBytes == 0xffe1){
28161                                 _this.parseExifData(
28162                                     dataView,
28163                                     offset,
28164                                     markerLength
28165                                 );
28166                             }
28167                             
28168                             offset += markerLength;
28169                             
28170                             continue;
28171                         }
28172                         
28173                         break;
28174                     }
28175                     
28176                 }
28177                 
28178                 var url = _this.urlAPI.createObjectURL(_this.file);
28179                 
28180                 _this.loadCanvas(url);
28181                 
28182                 return;
28183             }
28184             
28185             reader.readAsArrayBuffer(this.file);
28186             
28187         }
28188         
28189     },
28190     
28191     parseExifData : function(dataView, offset, length)
28192     {
28193         var tiffOffset = offset + 10,
28194             littleEndian,
28195             dirOffset;
28196     
28197         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28198             // No Exif data, might be XMP data instead
28199             return;
28200         }
28201         
28202         // Check for the ASCII code for "Exif" (0x45786966):
28203         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28204             // No Exif data, might be XMP data instead
28205             return;
28206         }
28207         if (tiffOffset + 8 > dataView.byteLength) {
28208             Roo.log('Invalid Exif data: Invalid segment size.');
28209             return;
28210         }
28211         // Check for the two null bytes:
28212         if (dataView.getUint16(offset + 8) !== 0x0000) {
28213             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28214             return;
28215         }
28216         // Check the byte alignment:
28217         switch (dataView.getUint16(tiffOffset)) {
28218         case 0x4949:
28219             littleEndian = true;
28220             break;
28221         case 0x4D4D:
28222             littleEndian = false;
28223             break;
28224         default:
28225             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28226             return;
28227         }
28228         // Check for the TIFF tag marker (0x002A):
28229         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28230             Roo.log('Invalid Exif data: Missing TIFF marker.');
28231             return;
28232         }
28233         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28234         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28235         
28236         this.parseExifTags(
28237             dataView,
28238             tiffOffset,
28239             tiffOffset + dirOffset,
28240             littleEndian
28241         );
28242     },
28243     
28244     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28245     {
28246         var tagsNumber,
28247             dirEndOffset,
28248             i;
28249         if (dirOffset + 6 > dataView.byteLength) {
28250             Roo.log('Invalid Exif data: Invalid directory offset.');
28251             return;
28252         }
28253         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28254         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28255         if (dirEndOffset + 4 > dataView.byteLength) {
28256             Roo.log('Invalid Exif data: Invalid directory size.');
28257             return;
28258         }
28259         for (i = 0; i < tagsNumber; i += 1) {
28260             this.parseExifTag(
28261                 dataView,
28262                 tiffOffset,
28263                 dirOffset + 2 + 12 * i, // tag offset
28264                 littleEndian
28265             );
28266         }
28267         // Return the offset to the next directory:
28268         return dataView.getUint32(dirEndOffset, littleEndian);
28269     },
28270     
28271     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28272     {
28273         var tag = dataView.getUint16(offset, littleEndian);
28274         
28275         this.exif[tag] = this.getExifValue(
28276             dataView,
28277             tiffOffset,
28278             offset,
28279             dataView.getUint16(offset + 2, littleEndian), // tag type
28280             dataView.getUint32(offset + 4, littleEndian), // tag length
28281             littleEndian
28282         );
28283     },
28284     
28285     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28286     {
28287         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28288             tagSize,
28289             dataOffset,
28290             values,
28291             i,
28292             str,
28293             c;
28294     
28295         if (!tagType) {
28296             Roo.log('Invalid Exif data: Invalid tag type.');
28297             return;
28298         }
28299         
28300         tagSize = tagType.size * length;
28301         // Determine if the value is contained in the dataOffset bytes,
28302         // or if the value at the dataOffset is a pointer to the actual data:
28303         dataOffset = tagSize > 4 ?
28304                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28305         if (dataOffset + tagSize > dataView.byteLength) {
28306             Roo.log('Invalid Exif data: Invalid data offset.');
28307             return;
28308         }
28309         if (length === 1) {
28310             return tagType.getValue(dataView, dataOffset, littleEndian);
28311         }
28312         values = [];
28313         for (i = 0; i < length; i += 1) {
28314             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28315         }
28316         
28317         if (tagType.ascii) {
28318             str = '';
28319             // Concatenate the chars:
28320             for (i = 0; i < values.length; i += 1) {
28321                 c = values[i];
28322                 // Ignore the terminating NULL byte(s):
28323                 if (c === '\u0000') {
28324                     break;
28325                 }
28326                 str += c;
28327             }
28328             return str;
28329         }
28330         return values;
28331     }
28332     
28333 });
28334
28335 Roo.apply(Roo.bootstrap.UploadCropbox, {
28336     tags : {
28337         'Orientation': 0x0112
28338     },
28339     
28340     Orientation: {
28341             1: 0, //'top-left',
28342 //            2: 'top-right',
28343             3: 180, //'bottom-right',
28344 //            4: 'bottom-left',
28345 //            5: 'left-top',
28346             6: 90, //'right-top',
28347 //            7: 'right-bottom',
28348             8: 270 //'left-bottom'
28349     },
28350     
28351     exifTagTypes : {
28352         // byte, 8-bit unsigned int:
28353         1: {
28354             getValue: function (dataView, dataOffset) {
28355                 return dataView.getUint8(dataOffset);
28356             },
28357             size: 1
28358         },
28359         // ascii, 8-bit byte:
28360         2: {
28361             getValue: function (dataView, dataOffset) {
28362                 return String.fromCharCode(dataView.getUint8(dataOffset));
28363             },
28364             size: 1,
28365             ascii: true
28366         },
28367         // short, 16 bit int:
28368         3: {
28369             getValue: function (dataView, dataOffset, littleEndian) {
28370                 return dataView.getUint16(dataOffset, littleEndian);
28371             },
28372             size: 2
28373         },
28374         // long, 32 bit int:
28375         4: {
28376             getValue: function (dataView, dataOffset, littleEndian) {
28377                 return dataView.getUint32(dataOffset, littleEndian);
28378             },
28379             size: 4
28380         },
28381         // rational = two long values, first is numerator, second is denominator:
28382         5: {
28383             getValue: function (dataView, dataOffset, littleEndian) {
28384                 return dataView.getUint32(dataOffset, littleEndian) /
28385                     dataView.getUint32(dataOffset + 4, littleEndian);
28386             },
28387             size: 8
28388         },
28389         // slong, 32 bit signed int:
28390         9: {
28391             getValue: function (dataView, dataOffset, littleEndian) {
28392                 return dataView.getInt32(dataOffset, littleEndian);
28393             },
28394             size: 4
28395         },
28396         // srational, two slongs, first is numerator, second is denominator:
28397         10: {
28398             getValue: function (dataView, dataOffset, littleEndian) {
28399                 return dataView.getInt32(dataOffset, littleEndian) /
28400                     dataView.getInt32(dataOffset + 4, littleEndian);
28401             },
28402             size: 8
28403         }
28404     },
28405     
28406     footer : {
28407         STANDARD : [
28408             {
28409                 tag : 'div',
28410                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28411                 action : 'rotate-left',
28412                 cn : [
28413                     {
28414                         tag : 'button',
28415                         cls : 'btn btn-default',
28416                         html : '<i class="fa fa-undo"></i>'
28417                     }
28418                 ]
28419             },
28420             {
28421                 tag : 'div',
28422                 cls : 'btn-group roo-upload-cropbox-picture',
28423                 action : 'picture',
28424                 cn : [
28425                     {
28426                         tag : 'button',
28427                         cls : 'btn btn-default',
28428                         html : '<i class="fa fa-picture-o"></i>'
28429                     }
28430                 ]
28431             },
28432             {
28433                 tag : 'div',
28434                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28435                 action : 'rotate-right',
28436                 cn : [
28437                     {
28438                         tag : 'button',
28439                         cls : 'btn btn-default',
28440                         html : '<i class="fa fa-repeat"></i>'
28441                     }
28442                 ]
28443             }
28444         ],
28445         DOCUMENT : [
28446             {
28447                 tag : 'div',
28448                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28449                 action : 'rotate-left',
28450                 cn : [
28451                     {
28452                         tag : 'button',
28453                         cls : 'btn btn-default',
28454                         html : '<i class="fa fa-undo"></i>'
28455                     }
28456                 ]
28457             },
28458             {
28459                 tag : 'div',
28460                 cls : 'btn-group roo-upload-cropbox-download',
28461                 action : 'download',
28462                 cn : [
28463                     {
28464                         tag : 'button',
28465                         cls : 'btn btn-default',
28466                         html : '<i class="fa fa-download"></i>'
28467                     }
28468                 ]
28469             },
28470             {
28471                 tag : 'div',
28472                 cls : 'btn-group roo-upload-cropbox-crop',
28473                 action : 'crop',
28474                 cn : [
28475                     {
28476                         tag : 'button',
28477                         cls : 'btn btn-default',
28478                         html : '<i class="fa fa-crop"></i>'
28479                     }
28480                 ]
28481             },
28482             {
28483                 tag : 'div',
28484                 cls : 'btn-group roo-upload-cropbox-trash',
28485                 action : 'trash',
28486                 cn : [
28487                     {
28488                         tag : 'button',
28489                         cls : 'btn btn-default',
28490                         html : '<i class="fa fa-trash"></i>'
28491                     }
28492                 ]
28493             },
28494             {
28495                 tag : 'div',
28496                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28497                 action : 'rotate-right',
28498                 cn : [
28499                     {
28500                         tag : 'button',
28501                         cls : 'btn btn-default',
28502                         html : '<i class="fa fa-repeat"></i>'
28503                     }
28504                 ]
28505             }
28506         ],
28507         ROTATOR : [
28508             {
28509                 tag : 'div',
28510                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28511                 action : 'rotate-left',
28512                 cn : [
28513                     {
28514                         tag : 'button',
28515                         cls : 'btn btn-default',
28516                         html : '<i class="fa fa-undo"></i>'
28517                     }
28518                 ]
28519             },
28520             {
28521                 tag : 'div',
28522                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28523                 action : 'rotate-right',
28524                 cn : [
28525                     {
28526                         tag : 'button',
28527                         cls : 'btn btn-default',
28528                         html : '<i class="fa fa-repeat"></i>'
28529                     }
28530                 ]
28531             }
28532         ]
28533     }
28534 });
28535
28536 /*
28537 * Licence: LGPL
28538 */
28539
28540 /**
28541  * @class Roo.bootstrap.DocumentManager
28542  * @extends Roo.bootstrap.Component
28543  * Bootstrap DocumentManager class
28544  * @cfg {String} paramName default 'imageUpload'
28545  * @cfg {String} toolTipName default 'filename'
28546  * @cfg {String} method default POST
28547  * @cfg {String} url action url
28548  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28549  * @cfg {Boolean} multiple multiple upload default true
28550  * @cfg {Number} thumbSize default 300
28551  * @cfg {String} fieldLabel
28552  * @cfg {Number} labelWidth default 4
28553  * @cfg {String} labelAlign (left|top) default left
28554  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28555 * @cfg {Number} labellg set the width of label (1-12)
28556  * @cfg {Number} labelmd set the width of label (1-12)
28557  * @cfg {Number} labelsm set the width of label (1-12)
28558  * @cfg {Number} labelxs set the width of label (1-12)
28559  * 
28560  * @constructor
28561  * Create a new DocumentManager
28562  * @param {Object} config The config object
28563  */
28564
28565 Roo.bootstrap.DocumentManager = function(config){
28566     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28567     
28568     this.files = [];
28569     this.delegates = [];
28570     
28571     this.addEvents({
28572         /**
28573          * @event initial
28574          * Fire when initial the DocumentManager
28575          * @param {Roo.bootstrap.DocumentManager} this
28576          */
28577         "initial" : true,
28578         /**
28579          * @event inspect
28580          * inspect selected file
28581          * @param {Roo.bootstrap.DocumentManager} this
28582          * @param {File} file
28583          */
28584         "inspect" : true,
28585         /**
28586          * @event exception
28587          * Fire when xhr load exception
28588          * @param {Roo.bootstrap.DocumentManager} this
28589          * @param {XMLHttpRequest} xhr
28590          */
28591         "exception" : true,
28592         /**
28593          * @event afterupload
28594          * Fire when xhr load exception
28595          * @param {Roo.bootstrap.DocumentManager} this
28596          * @param {XMLHttpRequest} xhr
28597          */
28598         "afterupload" : true,
28599         /**
28600          * @event prepare
28601          * prepare the form data
28602          * @param {Roo.bootstrap.DocumentManager} this
28603          * @param {Object} formData
28604          */
28605         "prepare" : true,
28606         /**
28607          * @event remove
28608          * Fire when remove the file
28609          * @param {Roo.bootstrap.DocumentManager} this
28610          * @param {Object} file
28611          */
28612         "remove" : true,
28613         /**
28614          * @event refresh
28615          * Fire after refresh the file
28616          * @param {Roo.bootstrap.DocumentManager} this
28617          */
28618         "refresh" : true,
28619         /**
28620          * @event click
28621          * Fire after click the image
28622          * @param {Roo.bootstrap.DocumentManager} this
28623          * @param {Object} file
28624          */
28625         "click" : true,
28626         /**
28627          * @event edit
28628          * Fire when upload a image and editable set to true
28629          * @param {Roo.bootstrap.DocumentManager} this
28630          * @param {Object} file
28631          */
28632         "edit" : true,
28633         /**
28634          * @event beforeselectfile
28635          * Fire before select file
28636          * @param {Roo.bootstrap.DocumentManager} this
28637          */
28638         "beforeselectfile" : true,
28639         /**
28640          * @event process
28641          * Fire before process file
28642          * @param {Roo.bootstrap.DocumentManager} this
28643          * @param {Object} file
28644          */
28645         "process" : true,
28646         /**
28647          * @event previewrendered
28648          * Fire when preview rendered
28649          * @param {Roo.bootstrap.DocumentManager} this
28650          * @param {Object} file
28651          */
28652         "previewrendered" : true
28653         
28654     });
28655 };
28656
28657 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28658     
28659     boxes : 0,
28660     inputName : '',
28661     thumbSize : 300,
28662     multiple : true,
28663     files : false,
28664     method : 'POST',
28665     url : '',
28666     paramName : 'imageUpload',
28667     toolTipName : 'filename',
28668     fieldLabel : '',
28669     labelWidth : 4,
28670     labelAlign : 'left',
28671     editable : true,
28672     delegates : false,
28673     xhr : false, 
28674     
28675     labellg : 0,
28676     labelmd : 0,
28677     labelsm : 0,
28678     labelxs : 0,
28679     
28680     getAutoCreate : function()
28681     {   
28682         var managerWidget = {
28683             tag : 'div',
28684             cls : 'roo-document-manager',
28685             cn : [
28686                 {
28687                     tag : 'input',
28688                     cls : 'roo-document-manager-selector',
28689                     type : 'file'
28690                 },
28691                 {
28692                     tag : 'div',
28693                     cls : 'roo-document-manager-uploader',
28694                     cn : [
28695                         {
28696                             tag : 'div',
28697                             cls : 'roo-document-manager-upload-btn',
28698                             html : '<i class="fa fa-plus"></i>'
28699                         }
28700                     ]
28701                     
28702                 }
28703             ]
28704         };
28705         
28706         var content = [
28707             {
28708                 tag : 'div',
28709                 cls : 'column col-md-12',
28710                 cn : managerWidget
28711             }
28712         ];
28713         
28714         if(this.fieldLabel.length){
28715             
28716             content = [
28717                 {
28718                     tag : 'div',
28719                     cls : 'column col-md-12',
28720                     html : this.fieldLabel
28721                 },
28722                 {
28723                     tag : 'div',
28724                     cls : 'column col-md-12',
28725                     cn : managerWidget
28726                 }
28727             ];
28728
28729             if(this.labelAlign == 'left'){
28730                 content = [
28731                     {
28732                         tag : 'div',
28733                         cls : 'column',
28734                         html : this.fieldLabel
28735                     },
28736                     {
28737                         tag : 'div',
28738                         cls : 'column',
28739                         cn : managerWidget
28740                     }
28741                 ];
28742                 
28743                 if(this.labelWidth > 12){
28744                     content[0].style = "width: " + this.labelWidth + 'px';
28745                 }
28746
28747                 if(this.labelWidth < 13 && this.labelmd == 0){
28748                     this.labelmd = this.labelWidth;
28749                 }
28750
28751                 if(this.labellg > 0){
28752                     content[0].cls += ' col-lg-' + this.labellg;
28753                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28754                 }
28755
28756                 if(this.labelmd > 0){
28757                     content[0].cls += ' col-md-' + this.labelmd;
28758                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28759                 }
28760
28761                 if(this.labelsm > 0){
28762                     content[0].cls += ' col-sm-' + this.labelsm;
28763                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28764                 }
28765
28766                 if(this.labelxs > 0){
28767                     content[0].cls += ' col-xs-' + this.labelxs;
28768                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28769                 }
28770                 
28771             }
28772         }
28773         
28774         var cfg = {
28775             tag : 'div',
28776             cls : 'row clearfix',
28777             cn : content
28778         };
28779         
28780         return cfg;
28781         
28782     },
28783     
28784     initEvents : function()
28785     {
28786         this.managerEl = this.el.select('.roo-document-manager', true).first();
28787         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28788         
28789         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28790         this.selectorEl.hide();
28791         
28792         if(this.multiple){
28793             this.selectorEl.attr('multiple', 'multiple');
28794         }
28795         
28796         this.selectorEl.on('change', this.onFileSelected, this);
28797         
28798         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28799         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28800         
28801         this.uploader.on('click', this.onUploaderClick, this);
28802         
28803         this.renderProgressDialog();
28804         
28805         var _this = this;
28806         
28807         window.addEventListener("resize", function() { _this.refresh(); } );
28808         
28809         this.fireEvent('initial', this);
28810     },
28811     
28812     renderProgressDialog : function()
28813     {
28814         var _this = this;
28815         
28816         this.progressDialog = new Roo.bootstrap.Modal({
28817             cls : 'roo-document-manager-progress-dialog',
28818             allow_close : false,
28819             title : '',
28820             buttons : [
28821                 {
28822                     name  :'cancel',
28823                     weight : 'danger',
28824                     html : 'Cancel'
28825                 }
28826             ], 
28827             listeners : { 
28828                 btnclick : function() {
28829                     _this.uploadCancel();
28830                     this.hide();
28831                 }
28832             }
28833         });
28834          
28835         this.progressDialog.render(Roo.get(document.body));
28836          
28837         this.progress = new Roo.bootstrap.Progress({
28838             cls : 'roo-document-manager-progress',
28839             active : true,
28840             striped : true
28841         });
28842         
28843         this.progress.render(this.progressDialog.getChildContainer());
28844         
28845         this.progressBar = new Roo.bootstrap.ProgressBar({
28846             cls : 'roo-document-manager-progress-bar',
28847             aria_valuenow : 0,
28848             aria_valuemin : 0,
28849             aria_valuemax : 12,
28850             panel : 'success'
28851         });
28852         
28853         this.progressBar.render(this.progress.getChildContainer());
28854     },
28855     
28856     onUploaderClick : function(e)
28857     {
28858         e.preventDefault();
28859      
28860         if(this.fireEvent('beforeselectfile', this) != false){
28861             this.selectorEl.dom.click();
28862         }
28863         
28864     },
28865     
28866     onFileSelected : function(e)
28867     {
28868         e.preventDefault();
28869         
28870         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28871             return;
28872         }
28873         
28874         Roo.each(this.selectorEl.dom.files, function(file){
28875             if(this.fireEvent('inspect', this, file) != false){
28876                 this.files.push(file);
28877             }
28878         }, this);
28879         
28880         this.queue();
28881         
28882     },
28883     
28884     queue : function()
28885     {
28886         this.selectorEl.dom.value = '';
28887         
28888         if(!this.files || !this.files.length){
28889             return;
28890         }
28891         
28892         if(this.boxes > 0 && this.files.length > this.boxes){
28893             this.files = this.files.slice(0, this.boxes);
28894         }
28895         
28896         this.uploader.show();
28897         
28898         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28899             this.uploader.hide();
28900         }
28901         
28902         var _this = this;
28903         
28904         var files = [];
28905         
28906         var docs = [];
28907         
28908         Roo.each(this.files, function(file){
28909             
28910             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28911                 var f = this.renderPreview(file);
28912                 files.push(f);
28913                 return;
28914             }
28915             
28916             if(file.type.indexOf('image') != -1){
28917                 this.delegates.push(
28918                     (function(){
28919                         _this.process(file);
28920                     }).createDelegate(this)
28921                 );
28922         
28923                 return;
28924             }
28925             
28926             docs.push(
28927                 (function(){
28928                     _this.process(file);
28929                 }).createDelegate(this)
28930             );
28931             
28932         }, this);
28933         
28934         this.files = files;
28935         
28936         this.delegates = this.delegates.concat(docs);
28937         
28938         if(!this.delegates.length){
28939             this.refresh();
28940             return;
28941         }
28942         
28943         this.progressBar.aria_valuemax = this.delegates.length;
28944         
28945         this.arrange();
28946         
28947         return;
28948     },
28949     
28950     arrange : function()
28951     {
28952         if(!this.delegates.length){
28953             this.progressDialog.hide();
28954             this.refresh();
28955             return;
28956         }
28957         
28958         var delegate = this.delegates.shift();
28959         
28960         this.progressDialog.show();
28961         
28962         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28963         
28964         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28965         
28966         delegate();
28967     },
28968     
28969     refresh : function()
28970     {
28971         this.uploader.show();
28972         
28973         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28974             this.uploader.hide();
28975         }
28976         
28977         Roo.isTouch ? this.closable(false) : this.closable(true);
28978         
28979         this.fireEvent('refresh', this);
28980     },
28981     
28982     onRemove : function(e, el, o)
28983     {
28984         e.preventDefault();
28985         
28986         this.fireEvent('remove', this, o);
28987         
28988     },
28989     
28990     remove : function(o)
28991     {
28992         var files = [];
28993         
28994         Roo.each(this.files, function(file){
28995             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28996                 files.push(file);
28997                 return;
28998             }
28999
29000             o.target.remove();
29001
29002         }, this);
29003         
29004         this.files = files;
29005         
29006         this.refresh();
29007     },
29008     
29009     clear : function()
29010     {
29011         Roo.each(this.files, function(file){
29012             if(!file.target){
29013                 return;
29014             }
29015             
29016             file.target.remove();
29017
29018         }, this);
29019         
29020         this.files = [];
29021         
29022         this.refresh();
29023     },
29024     
29025     onClick : function(e, el, o)
29026     {
29027         e.preventDefault();
29028         
29029         this.fireEvent('click', this, o);
29030         
29031     },
29032     
29033     closable : function(closable)
29034     {
29035         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29036             
29037             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29038             
29039             if(closable){
29040                 el.show();
29041                 return;
29042             }
29043             
29044             el.hide();
29045             
29046         }, this);
29047     },
29048     
29049     xhrOnLoad : function(xhr)
29050     {
29051         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29052             el.remove();
29053         }, this);
29054         
29055         if (xhr.readyState !== 4) {
29056             this.arrange();
29057             this.fireEvent('exception', this, xhr);
29058             return;
29059         }
29060
29061         var response = Roo.decode(xhr.responseText);
29062         
29063         if(!response.success){
29064             this.arrange();
29065             this.fireEvent('exception', this, xhr);
29066             return;
29067         }
29068         
29069         var file = this.renderPreview(response.data);
29070         
29071         this.files.push(file);
29072         
29073         this.arrange();
29074         
29075         this.fireEvent('afterupload', this, xhr);
29076         
29077     },
29078     
29079     xhrOnError : function(xhr)
29080     {
29081         Roo.log('xhr on error');
29082         
29083         var response = Roo.decode(xhr.responseText);
29084           
29085         Roo.log(response);
29086         
29087         this.arrange();
29088     },
29089     
29090     process : function(file)
29091     {
29092         if(this.fireEvent('process', this, file) !== false){
29093             if(this.editable && file.type.indexOf('image') != -1){
29094                 this.fireEvent('edit', this, file);
29095                 return;
29096             }
29097
29098             this.uploadStart(file, false);
29099
29100             return;
29101         }
29102         
29103     },
29104     
29105     uploadStart : function(file, crop)
29106     {
29107         this.xhr = new XMLHttpRequest();
29108         
29109         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29110             this.arrange();
29111             return;
29112         }
29113         
29114         file.xhr = this.xhr;
29115             
29116         this.managerEl.createChild({
29117             tag : 'div',
29118             cls : 'roo-document-manager-loading',
29119             cn : [
29120                 {
29121                     tag : 'div',
29122                     tooltip : file.name,
29123                     cls : 'roo-document-manager-thumb',
29124                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29125                 }
29126             ]
29127
29128         });
29129
29130         this.xhr.open(this.method, this.url, true);
29131         
29132         var headers = {
29133             "Accept": "application/json",
29134             "Cache-Control": "no-cache",
29135             "X-Requested-With": "XMLHttpRequest"
29136         };
29137         
29138         for (var headerName in headers) {
29139             var headerValue = headers[headerName];
29140             if (headerValue) {
29141                 this.xhr.setRequestHeader(headerName, headerValue);
29142             }
29143         }
29144         
29145         var _this = this;
29146         
29147         this.xhr.onload = function()
29148         {
29149             _this.xhrOnLoad(_this.xhr);
29150         }
29151         
29152         this.xhr.onerror = function()
29153         {
29154             _this.xhrOnError(_this.xhr);
29155         }
29156         
29157         var formData = new FormData();
29158
29159         formData.append('returnHTML', 'NO');
29160         
29161         if(crop){
29162             formData.append('crop', crop);
29163         }
29164         
29165         formData.append(this.paramName, file, file.name);
29166         
29167         var options = {
29168             file : file, 
29169             manually : false
29170         };
29171         
29172         if(this.fireEvent('prepare', this, formData, options) != false){
29173             
29174             if(options.manually){
29175                 return;
29176             }
29177             
29178             this.xhr.send(formData);
29179             return;
29180         };
29181         
29182         this.uploadCancel();
29183     },
29184     
29185     uploadCancel : function()
29186     {
29187         if (this.xhr) {
29188             this.xhr.abort();
29189         }
29190         
29191         this.delegates = [];
29192         
29193         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29194             el.remove();
29195         }, this);
29196         
29197         this.arrange();
29198     },
29199     
29200     renderPreview : function(file)
29201     {
29202         if(typeof(file.target) != 'undefined' && file.target){
29203             return file;
29204         }
29205         
29206         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29207         
29208         var previewEl = this.managerEl.createChild({
29209             tag : 'div',
29210             cls : 'roo-document-manager-preview',
29211             cn : [
29212                 {
29213                     tag : 'div',
29214                     tooltip : file[this.toolTipName],
29215                     cls : 'roo-document-manager-thumb',
29216                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29217                 },
29218                 {
29219                     tag : 'button',
29220                     cls : 'close',
29221                     html : '<i class="fa fa-times-circle"></i>'
29222                 }
29223             ]
29224         });
29225
29226         var close = previewEl.select('button.close', true).first();
29227
29228         close.on('click', this.onRemove, this, file);
29229
29230         file.target = previewEl;
29231
29232         var image = previewEl.select('img', true).first();
29233         
29234         var _this = this;
29235         
29236         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29237         
29238         image.on('click', this.onClick, this, file);
29239         
29240         this.fireEvent('previewrendered', this, file);
29241         
29242         return file;
29243         
29244     },
29245     
29246     onPreviewLoad : function(file, image)
29247     {
29248         if(typeof(file.target) == 'undefined' || !file.target){
29249             return;
29250         }
29251         
29252         var width = image.dom.naturalWidth || image.dom.width;
29253         var height = image.dom.naturalHeight || image.dom.height;
29254         
29255         if(width > height){
29256             file.target.addClass('wide');
29257             return;
29258         }
29259         
29260         file.target.addClass('tall');
29261         return;
29262         
29263     },
29264     
29265     uploadFromSource : function(file, crop)
29266     {
29267         this.xhr = new XMLHttpRequest();
29268         
29269         this.managerEl.createChild({
29270             tag : 'div',
29271             cls : 'roo-document-manager-loading',
29272             cn : [
29273                 {
29274                     tag : 'div',
29275                     tooltip : file.name,
29276                     cls : 'roo-document-manager-thumb',
29277                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29278                 }
29279             ]
29280
29281         });
29282
29283         this.xhr.open(this.method, this.url, true);
29284         
29285         var headers = {
29286             "Accept": "application/json",
29287             "Cache-Control": "no-cache",
29288             "X-Requested-With": "XMLHttpRequest"
29289         };
29290         
29291         for (var headerName in headers) {
29292             var headerValue = headers[headerName];
29293             if (headerValue) {
29294                 this.xhr.setRequestHeader(headerName, headerValue);
29295             }
29296         }
29297         
29298         var _this = this;
29299         
29300         this.xhr.onload = function()
29301         {
29302             _this.xhrOnLoad(_this.xhr);
29303         }
29304         
29305         this.xhr.onerror = function()
29306         {
29307             _this.xhrOnError(_this.xhr);
29308         }
29309         
29310         var formData = new FormData();
29311
29312         formData.append('returnHTML', 'NO');
29313         
29314         formData.append('crop', crop);
29315         
29316         if(typeof(file.filename) != 'undefined'){
29317             formData.append('filename', file.filename);
29318         }
29319         
29320         if(typeof(file.mimetype) != 'undefined'){
29321             formData.append('mimetype', file.mimetype);
29322         }
29323         
29324         Roo.log(formData);
29325         
29326         if(this.fireEvent('prepare', this, formData) != false){
29327             this.xhr.send(formData);
29328         };
29329     }
29330 });
29331
29332 /*
29333 * Licence: LGPL
29334 */
29335
29336 /**
29337  * @class Roo.bootstrap.DocumentViewer
29338  * @extends Roo.bootstrap.Component
29339  * Bootstrap DocumentViewer class
29340  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29341  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29342  * 
29343  * @constructor
29344  * Create a new DocumentViewer
29345  * @param {Object} config The config object
29346  */
29347
29348 Roo.bootstrap.DocumentViewer = function(config){
29349     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29350     
29351     this.addEvents({
29352         /**
29353          * @event initial
29354          * Fire after initEvent
29355          * @param {Roo.bootstrap.DocumentViewer} this
29356          */
29357         "initial" : true,
29358         /**
29359          * @event click
29360          * Fire after click
29361          * @param {Roo.bootstrap.DocumentViewer} this
29362          */
29363         "click" : true,
29364         /**
29365          * @event download
29366          * Fire after download button
29367          * @param {Roo.bootstrap.DocumentViewer} this
29368          */
29369         "download" : true,
29370         /**
29371          * @event trash
29372          * Fire after trash button
29373          * @param {Roo.bootstrap.DocumentViewer} this
29374          */
29375         "trash" : true
29376         
29377     });
29378 };
29379
29380 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29381     
29382     showDownload : true,
29383     
29384     showTrash : true,
29385     
29386     getAutoCreate : function()
29387     {
29388         var cfg = {
29389             tag : 'div',
29390             cls : 'roo-document-viewer',
29391             cn : [
29392                 {
29393                     tag : 'div',
29394                     cls : 'roo-document-viewer-body',
29395                     cn : [
29396                         {
29397                             tag : 'div',
29398                             cls : 'roo-document-viewer-thumb',
29399                             cn : [
29400                                 {
29401                                     tag : 'img',
29402                                     cls : 'roo-document-viewer-image'
29403                                 }
29404                             ]
29405                         }
29406                     ]
29407                 },
29408                 {
29409                     tag : 'div',
29410                     cls : 'roo-document-viewer-footer',
29411                     cn : {
29412                         tag : 'div',
29413                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29414                         cn : [
29415                             {
29416                                 tag : 'div',
29417                                 cls : 'btn-group roo-document-viewer-download',
29418                                 cn : [
29419                                     {
29420                                         tag : 'button',
29421                                         cls : 'btn btn-default',
29422                                         html : '<i class="fa fa-download"></i>'
29423                                     }
29424                                 ]
29425                             },
29426                             {
29427                                 tag : 'div',
29428                                 cls : 'btn-group roo-document-viewer-trash',
29429                                 cn : [
29430                                     {
29431                                         tag : 'button',
29432                                         cls : 'btn btn-default',
29433                                         html : '<i class="fa fa-trash"></i>'
29434                                     }
29435                                 ]
29436                             }
29437                         ]
29438                     }
29439                 }
29440             ]
29441         };
29442         
29443         return cfg;
29444     },
29445     
29446     initEvents : function()
29447     {
29448         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29449         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29450         
29451         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29452         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29453         
29454         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29455         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29456         
29457         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29458         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29459         
29460         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29461         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29462         
29463         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29464         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29465         
29466         this.bodyEl.on('click', this.onClick, this);
29467         this.downloadBtn.on('click', this.onDownload, this);
29468         this.trashBtn.on('click', this.onTrash, this);
29469         
29470         this.downloadBtn.hide();
29471         this.trashBtn.hide();
29472         
29473         if(this.showDownload){
29474             this.downloadBtn.show();
29475         }
29476         
29477         if(this.showTrash){
29478             this.trashBtn.show();
29479         }
29480         
29481         if(!this.showDownload && !this.showTrash) {
29482             this.footerEl.hide();
29483         }
29484         
29485     },
29486     
29487     initial : function()
29488     {
29489         this.fireEvent('initial', this);
29490         
29491     },
29492     
29493     onClick : function(e)
29494     {
29495         e.preventDefault();
29496         
29497         this.fireEvent('click', this);
29498     },
29499     
29500     onDownload : function(e)
29501     {
29502         e.preventDefault();
29503         
29504         this.fireEvent('download', this);
29505     },
29506     
29507     onTrash : function(e)
29508     {
29509         e.preventDefault();
29510         
29511         this.fireEvent('trash', this);
29512     }
29513     
29514 });
29515 /*
29516  * - LGPL
29517  *
29518  * nav progress bar
29519  * 
29520  */
29521
29522 /**
29523  * @class Roo.bootstrap.NavProgressBar
29524  * @extends Roo.bootstrap.Component
29525  * Bootstrap NavProgressBar class
29526  * 
29527  * @constructor
29528  * Create a new nav progress bar
29529  * @param {Object} config The config object
29530  */
29531
29532 Roo.bootstrap.NavProgressBar = function(config){
29533     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29534
29535     this.bullets = this.bullets || [];
29536    
29537 //    Roo.bootstrap.NavProgressBar.register(this);
29538      this.addEvents({
29539         /**
29540              * @event changed
29541              * Fires when the active item changes
29542              * @param {Roo.bootstrap.NavProgressBar} this
29543              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29544              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29545          */
29546         'changed': true
29547      });
29548     
29549 };
29550
29551 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29552     
29553     bullets : [],
29554     barItems : [],
29555     
29556     getAutoCreate : function()
29557     {
29558         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29559         
29560         cfg = {
29561             tag : 'div',
29562             cls : 'roo-navigation-bar-group',
29563             cn : [
29564                 {
29565                     tag : 'div',
29566                     cls : 'roo-navigation-top-bar'
29567                 },
29568                 {
29569                     tag : 'div',
29570                     cls : 'roo-navigation-bullets-bar',
29571                     cn : [
29572                         {
29573                             tag : 'ul',
29574                             cls : 'roo-navigation-bar'
29575                         }
29576                     ]
29577                 },
29578                 
29579                 {
29580                     tag : 'div',
29581                     cls : 'roo-navigation-bottom-bar'
29582                 }
29583             ]
29584             
29585         };
29586         
29587         return cfg;
29588         
29589     },
29590     
29591     initEvents: function() 
29592     {
29593         
29594     },
29595     
29596     onRender : function(ct, position) 
29597     {
29598         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29599         
29600         if(this.bullets.length){
29601             Roo.each(this.bullets, function(b){
29602                this.addItem(b);
29603             }, this);
29604         }
29605         
29606         this.format();
29607         
29608     },
29609     
29610     addItem : function(cfg)
29611     {
29612         var item = new Roo.bootstrap.NavProgressItem(cfg);
29613         
29614         item.parentId = this.id;
29615         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29616         
29617         if(cfg.html){
29618             var top = new Roo.bootstrap.Element({
29619                 tag : 'div',
29620                 cls : 'roo-navigation-bar-text'
29621             });
29622             
29623             var bottom = new Roo.bootstrap.Element({
29624                 tag : 'div',
29625                 cls : 'roo-navigation-bar-text'
29626             });
29627             
29628             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29629             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29630             
29631             var topText = new Roo.bootstrap.Element({
29632                 tag : 'span',
29633                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29634             });
29635             
29636             var bottomText = new Roo.bootstrap.Element({
29637                 tag : 'span',
29638                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29639             });
29640             
29641             topText.onRender(top.el, null);
29642             bottomText.onRender(bottom.el, null);
29643             
29644             item.topEl = top;
29645             item.bottomEl = bottom;
29646         }
29647         
29648         this.barItems.push(item);
29649         
29650         return item;
29651     },
29652     
29653     getActive : function()
29654     {
29655         var active = false;
29656         
29657         Roo.each(this.barItems, function(v){
29658             
29659             if (!v.isActive()) {
29660                 return;
29661             }
29662             
29663             active = v;
29664             return false;
29665             
29666         });
29667         
29668         return active;
29669     },
29670     
29671     setActiveItem : function(item)
29672     {
29673         var prev = false;
29674         
29675         Roo.each(this.barItems, function(v){
29676             if (v.rid == item.rid) {
29677                 return ;
29678             }
29679             
29680             if (v.isActive()) {
29681                 v.setActive(false);
29682                 prev = v;
29683             }
29684         });
29685
29686         item.setActive(true);
29687         
29688         this.fireEvent('changed', this, item, prev);
29689     },
29690     
29691     getBarItem: function(rid)
29692     {
29693         var ret = false;
29694         
29695         Roo.each(this.barItems, function(e) {
29696             if (e.rid != rid) {
29697                 return;
29698             }
29699             
29700             ret =  e;
29701             return false;
29702         });
29703         
29704         return ret;
29705     },
29706     
29707     indexOfItem : function(item)
29708     {
29709         var index = false;
29710         
29711         Roo.each(this.barItems, function(v, i){
29712             
29713             if (v.rid != item.rid) {
29714                 return;
29715             }
29716             
29717             index = i;
29718             return false
29719         });
29720         
29721         return index;
29722     },
29723     
29724     setActiveNext : function()
29725     {
29726         var i = this.indexOfItem(this.getActive());
29727         
29728         if (i > this.barItems.length) {
29729             return;
29730         }
29731         
29732         this.setActiveItem(this.barItems[i+1]);
29733     },
29734     
29735     setActivePrev : function()
29736     {
29737         var i = this.indexOfItem(this.getActive());
29738         
29739         if (i  < 1) {
29740             return;
29741         }
29742         
29743         this.setActiveItem(this.barItems[i-1]);
29744     },
29745     
29746     format : function()
29747     {
29748         if(!this.barItems.length){
29749             return;
29750         }
29751      
29752         var width = 100 / this.barItems.length;
29753         
29754         Roo.each(this.barItems, function(i){
29755             i.el.setStyle('width', width + '%');
29756             i.topEl.el.setStyle('width', width + '%');
29757             i.bottomEl.el.setStyle('width', width + '%');
29758         }, this);
29759         
29760     }
29761     
29762 });
29763 /*
29764  * - LGPL
29765  *
29766  * Nav Progress Item
29767  * 
29768  */
29769
29770 /**
29771  * @class Roo.bootstrap.NavProgressItem
29772  * @extends Roo.bootstrap.Component
29773  * Bootstrap NavProgressItem class
29774  * @cfg {String} rid the reference id
29775  * @cfg {Boolean} active (true|false) Is item active default false
29776  * @cfg {Boolean} disabled (true|false) Is item active default false
29777  * @cfg {String} html
29778  * @cfg {String} position (top|bottom) text position default bottom
29779  * @cfg {String} icon show icon instead of number
29780  * 
29781  * @constructor
29782  * Create a new NavProgressItem
29783  * @param {Object} config The config object
29784  */
29785 Roo.bootstrap.NavProgressItem = function(config){
29786     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29787     this.addEvents({
29788         // raw events
29789         /**
29790          * @event click
29791          * The raw click event for the entire grid.
29792          * @param {Roo.bootstrap.NavProgressItem} this
29793          * @param {Roo.EventObject} e
29794          */
29795         "click" : true
29796     });
29797    
29798 };
29799
29800 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29801     
29802     rid : '',
29803     active : false,
29804     disabled : false,
29805     html : '',
29806     position : 'bottom',
29807     icon : false,
29808     
29809     getAutoCreate : function()
29810     {
29811         var iconCls = 'roo-navigation-bar-item-icon';
29812         
29813         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29814         
29815         var cfg = {
29816             tag: 'li',
29817             cls: 'roo-navigation-bar-item',
29818             cn : [
29819                 {
29820                     tag : 'i',
29821                     cls : iconCls
29822                 }
29823             ]
29824         };
29825         
29826         if(this.active){
29827             cfg.cls += ' active';
29828         }
29829         if(this.disabled){
29830             cfg.cls += ' disabled';
29831         }
29832         
29833         return cfg;
29834     },
29835     
29836     disable : function()
29837     {
29838         this.setDisabled(true);
29839     },
29840     
29841     enable : function()
29842     {
29843         this.setDisabled(false);
29844     },
29845     
29846     initEvents: function() 
29847     {
29848         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29849         
29850         this.iconEl.on('click', this.onClick, this);
29851     },
29852     
29853     onClick : function(e)
29854     {
29855         e.preventDefault();
29856         
29857         if(this.disabled){
29858             return;
29859         }
29860         
29861         if(this.fireEvent('click', this, e) === false){
29862             return;
29863         };
29864         
29865         this.parent().setActiveItem(this);
29866     },
29867     
29868     isActive: function () 
29869     {
29870         return this.active;
29871     },
29872     
29873     setActive : function(state)
29874     {
29875         if(this.active == state){
29876             return;
29877         }
29878         
29879         this.active = state;
29880         
29881         if (state) {
29882             this.el.addClass('active');
29883             return;
29884         }
29885         
29886         this.el.removeClass('active');
29887         
29888         return;
29889     },
29890     
29891     setDisabled : function(state)
29892     {
29893         if(this.disabled == state){
29894             return;
29895         }
29896         
29897         this.disabled = state;
29898         
29899         if (state) {
29900             this.el.addClass('disabled');
29901             return;
29902         }
29903         
29904         this.el.removeClass('disabled');
29905     },
29906     
29907     tooltipEl : function()
29908     {
29909         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29910     }
29911 });
29912  
29913
29914  /*
29915  * - LGPL
29916  *
29917  * FieldLabel
29918  * 
29919  */
29920
29921 /**
29922  * @class Roo.bootstrap.FieldLabel
29923  * @extends Roo.bootstrap.Component
29924  * Bootstrap FieldLabel class
29925  * @cfg {String} html contents of the element
29926  * @cfg {String} tag tag of the element default label
29927  * @cfg {String} cls class of the element
29928  * @cfg {String} target label target 
29929  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29930  * @cfg {String} invalidClass default "text-warning"
29931  * @cfg {String} validClass default "text-success"
29932  * @cfg {String} iconTooltip default "This field is required"
29933  * @cfg {String} indicatorpos (left|right) default left
29934  * 
29935  * @constructor
29936  * Create a new FieldLabel
29937  * @param {Object} config The config object
29938  */
29939
29940 Roo.bootstrap.FieldLabel = function(config){
29941     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29942     
29943     this.addEvents({
29944             /**
29945              * @event invalid
29946              * Fires after the field has been marked as invalid.
29947              * @param {Roo.form.FieldLabel} this
29948              * @param {String} msg The validation message
29949              */
29950             invalid : true,
29951             /**
29952              * @event valid
29953              * Fires after the field has been validated with no errors.
29954              * @param {Roo.form.FieldLabel} this
29955              */
29956             valid : true
29957         });
29958 };
29959
29960 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29961     
29962     tag: 'label',
29963     cls: '',
29964     html: '',
29965     target: '',
29966     allowBlank : true,
29967     invalidClass : 'has-warning',
29968     validClass : 'has-success',
29969     iconTooltip : 'This field is required',
29970     indicatorpos : 'left',
29971     
29972     getAutoCreate : function(){
29973         
29974         var cfg = {
29975             tag : this.tag,
29976             cls : 'roo-bootstrap-field-label ' + this.cls,
29977             for : this.target,
29978             cn : [
29979                 {
29980                     tag : 'i',
29981                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29982                     tooltip : this.iconTooltip
29983                 },
29984                 {
29985                     tag : 'span',
29986                     html : this.html
29987                 }
29988             ] 
29989         };
29990         
29991         if(this.indicatorpos == 'right'){
29992             var cfg = {
29993                 tag : this.tag,
29994                 cls : 'roo-bootstrap-field-label ' + this.cls,
29995                 for : this.target,
29996                 cn : [
29997                     {
29998                         tag : 'span',
29999                         html : this.html
30000                     },
30001                     {
30002                         tag : 'i',
30003                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
30004                         tooltip : this.iconTooltip
30005                     }
30006                 ] 
30007             };
30008         }
30009         
30010         return cfg;
30011     },
30012     
30013     initEvents: function() 
30014     {
30015         Roo.bootstrap.Element.superclass.initEvents.call(this);
30016         
30017         this.indicator = this.indicatorEl();
30018         
30019         if(this.indicator){
30020             this.indicator.removeClass('visible');
30021             this.indicator.addClass('invisible');
30022         }
30023         
30024         Roo.bootstrap.FieldLabel.register(this);
30025     },
30026     
30027     indicatorEl : function()
30028     {
30029         var indicator = this.el.select('i.roo-required-indicator',true).first();
30030         
30031         if(!indicator){
30032             return false;
30033         }
30034         
30035         return indicator;
30036         
30037     },
30038     
30039     /**
30040      * Mark this field as valid
30041      */
30042     markValid : function()
30043     {
30044         if(this.indicator){
30045             this.indicator.removeClass('visible');
30046             this.indicator.addClass('invisible');
30047         }
30048         
30049         this.el.removeClass(this.invalidClass);
30050         
30051         this.el.addClass(this.validClass);
30052         
30053         this.fireEvent('valid', this);
30054     },
30055     
30056     /**
30057      * Mark this field as invalid
30058      * @param {String} msg The validation message
30059      */
30060     markInvalid : function(msg)
30061     {
30062         if(this.indicator){
30063             this.indicator.removeClass('invisible');
30064             this.indicator.addClass('visible');
30065         }
30066         
30067         this.el.removeClass(this.validClass);
30068         
30069         this.el.addClass(this.invalidClass);
30070         
30071         this.fireEvent('invalid', this, msg);
30072     }
30073     
30074    
30075 });
30076
30077 Roo.apply(Roo.bootstrap.FieldLabel, {
30078     
30079     groups: {},
30080     
30081      /**
30082     * register a FieldLabel Group
30083     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30084     */
30085     register : function(label)
30086     {
30087         if(this.groups.hasOwnProperty(label.target)){
30088             return;
30089         }
30090      
30091         this.groups[label.target] = label;
30092         
30093     },
30094     /**
30095     * fetch a FieldLabel Group based on the target
30096     * @param {string} target
30097     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30098     */
30099     get: function(target) {
30100         if (typeof(this.groups[target]) == 'undefined') {
30101             return false;
30102         }
30103         
30104         return this.groups[target] ;
30105     }
30106 });
30107
30108  
30109
30110  /*
30111  * - LGPL
30112  *
30113  * page DateSplitField.
30114  * 
30115  */
30116
30117
30118 /**
30119  * @class Roo.bootstrap.DateSplitField
30120  * @extends Roo.bootstrap.Component
30121  * Bootstrap DateSplitField class
30122  * @cfg {string} fieldLabel - the label associated
30123  * @cfg {Number} labelWidth set the width of label (0-12)
30124  * @cfg {String} labelAlign (top|left)
30125  * @cfg {Boolean} dayAllowBlank (true|false) default false
30126  * @cfg {Boolean} monthAllowBlank (true|false) default false
30127  * @cfg {Boolean} yearAllowBlank (true|false) default false
30128  * @cfg {string} dayPlaceholder 
30129  * @cfg {string} monthPlaceholder
30130  * @cfg {string} yearPlaceholder
30131  * @cfg {string} dayFormat default 'd'
30132  * @cfg {string} monthFormat default 'm'
30133  * @cfg {string} yearFormat default 'Y'
30134  * @cfg {Number} labellg set the width of label (1-12)
30135  * @cfg {Number} labelmd set the width of label (1-12)
30136  * @cfg {Number} labelsm set the width of label (1-12)
30137  * @cfg {Number} labelxs set the width of label (1-12)
30138
30139  *     
30140  * @constructor
30141  * Create a new DateSplitField
30142  * @param {Object} config The config object
30143  */
30144
30145 Roo.bootstrap.DateSplitField = function(config){
30146     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30147     
30148     this.addEvents({
30149         // raw events
30150          /**
30151          * @event years
30152          * getting the data of years
30153          * @param {Roo.bootstrap.DateSplitField} this
30154          * @param {Object} years
30155          */
30156         "years" : true,
30157         /**
30158          * @event days
30159          * getting the data of days
30160          * @param {Roo.bootstrap.DateSplitField} this
30161          * @param {Object} days
30162          */
30163         "days" : true,
30164         /**
30165          * @event invalid
30166          * Fires after the field has been marked as invalid.
30167          * @param {Roo.form.Field} this
30168          * @param {String} msg The validation message
30169          */
30170         invalid : true,
30171        /**
30172          * @event valid
30173          * Fires after the field has been validated with no errors.
30174          * @param {Roo.form.Field} this
30175          */
30176         valid : true
30177     });
30178 };
30179
30180 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30181     
30182     fieldLabel : '',
30183     labelAlign : 'top',
30184     labelWidth : 3,
30185     dayAllowBlank : false,
30186     monthAllowBlank : false,
30187     yearAllowBlank : false,
30188     dayPlaceholder : '',
30189     monthPlaceholder : '',
30190     yearPlaceholder : '',
30191     dayFormat : 'd',
30192     monthFormat : 'm',
30193     yearFormat : 'Y',
30194     isFormField : true,
30195     labellg : 0,
30196     labelmd : 0,
30197     labelsm : 0,
30198     labelxs : 0,
30199     
30200     getAutoCreate : function()
30201     {
30202         var cfg = {
30203             tag : 'div',
30204             cls : 'row roo-date-split-field-group',
30205             cn : [
30206                 {
30207                     tag : 'input',
30208                     type : 'hidden',
30209                     cls : 'form-hidden-field roo-date-split-field-group-value',
30210                     name : this.name
30211                 }
30212             ]
30213         };
30214         
30215         var labelCls = 'col-md-12';
30216         var contentCls = 'col-md-4';
30217         
30218         if(this.fieldLabel){
30219             
30220             var label = {
30221                 tag : 'div',
30222                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30223                 cn : [
30224                     {
30225                         tag : 'label',
30226                         html : this.fieldLabel
30227                     }
30228                 ]
30229             };
30230             
30231             if(this.labelAlign == 'left'){
30232             
30233                 if(this.labelWidth > 12){
30234                     label.style = "width: " + this.labelWidth + 'px';
30235                 }
30236
30237                 if(this.labelWidth < 13 && this.labelmd == 0){
30238                     this.labelmd = this.labelWidth;
30239                 }
30240
30241                 if(this.labellg > 0){
30242                     labelCls = ' col-lg-' + this.labellg;
30243                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30244                 }
30245
30246                 if(this.labelmd > 0){
30247                     labelCls = ' col-md-' + this.labelmd;
30248                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30249                 }
30250
30251                 if(this.labelsm > 0){
30252                     labelCls = ' col-sm-' + this.labelsm;
30253                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30254                 }
30255
30256                 if(this.labelxs > 0){
30257                     labelCls = ' col-xs-' + this.labelxs;
30258                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30259                 }
30260             }
30261             
30262             label.cls += ' ' + labelCls;
30263             
30264             cfg.cn.push(label);
30265         }
30266         
30267         Roo.each(['day', 'month', 'year'], function(t){
30268             cfg.cn.push({
30269                 tag : 'div',
30270                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30271             });
30272         }, this);
30273         
30274         return cfg;
30275     },
30276     
30277     inputEl: function ()
30278     {
30279         return this.el.select('.roo-date-split-field-group-value', true).first();
30280     },
30281     
30282     onRender : function(ct, position) 
30283     {
30284         var _this = this;
30285         
30286         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30287         
30288         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30289         
30290         this.dayField = new Roo.bootstrap.ComboBox({
30291             allowBlank : this.dayAllowBlank,
30292             alwaysQuery : true,
30293             displayField : 'value',
30294             editable : false,
30295             fieldLabel : '',
30296             forceSelection : true,
30297             mode : 'local',
30298             placeholder : this.dayPlaceholder,
30299             selectOnFocus : true,
30300             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30301             triggerAction : 'all',
30302             typeAhead : true,
30303             valueField : 'value',
30304             store : new Roo.data.SimpleStore({
30305                 data : (function() {    
30306                     var days = [];
30307                     _this.fireEvent('days', _this, days);
30308                     return days;
30309                 })(),
30310                 fields : [ 'value' ]
30311             }),
30312             listeners : {
30313                 select : function (_self, record, index)
30314                 {
30315                     _this.setValue(_this.getValue());
30316                 }
30317             }
30318         });
30319
30320         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30321         
30322         this.monthField = new Roo.bootstrap.MonthField({
30323             after : '<i class=\"fa fa-calendar\"></i>',
30324             allowBlank : this.monthAllowBlank,
30325             placeholder : this.monthPlaceholder,
30326             readOnly : true,
30327             listeners : {
30328                 render : function (_self)
30329                 {
30330                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30331                         e.preventDefault();
30332                         _self.focus();
30333                     });
30334                 },
30335                 select : function (_self, oldvalue, newvalue)
30336                 {
30337                     _this.setValue(_this.getValue());
30338                 }
30339             }
30340         });
30341         
30342         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30343         
30344         this.yearField = new Roo.bootstrap.ComboBox({
30345             allowBlank : this.yearAllowBlank,
30346             alwaysQuery : true,
30347             displayField : 'value',
30348             editable : false,
30349             fieldLabel : '',
30350             forceSelection : true,
30351             mode : 'local',
30352             placeholder : this.yearPlaceholder,
30353             selectOnFocus : true,
30354             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30355             triggerAction : 'all',
30356             typeAhead : true,
30357             valueField : 'value',
30358             store : new Roo.data.SimpleStore({
30359                 data : (function() {
30360                     var years = [];
30361                     _this.fireEvent('years', _this, years);
30362                     return years;
30363                 })(),
30364                 fields : [ 'value' ]
30365             }),
30366             listeners : {
30367                 select : function (_self, record, index)
30368                 {
30369                     _this.setValue(_this.getValue());
30370                 }
30371             }
30372         });
30373
30374         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30375     },
30376     
30377     setValue : function(v, format)
30378     {
30379         this.inputEl.dom.value = v;
30380         
30381         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30382         
30383         var d = Date.parseDate(v, f);
30384         
30385         if(!d){
30386             this.validate();
30387             return;
30388         }
30389         
30390         this.setDay(d.format(this.dayFormat));
30391         this.setMonth(d.format(this.monthFormat));
30392         this.setYear(d.format(this.yearFormat));
30393         
30394         this.validate();
30395         
30396         return;
30397     },
30398     
30399     setDay : function(v)
30400     {
30401         this.dayField.setValue(v);
30402         this.inputEl.dom.value = this.getValue();
30403         this.validate();
30404         return;
30405     },
30406     
30407     setMonth : function(v)
30408     {
30409         this.monthField.setValue(v, true);
30410         this.inputEl.dom.value = this.getValue();
30411         this.validate();
30412         return;
30413     },
30414     
30415     setYear : function(v)
30416     {
30417         this.yearField.setValue(v);
30418         this.inputEl.dom.value = this.getValue();
30419         this.validate();
30420         return;
30421     },
30422     
30423     getDay : function()
30424     {
30425         return this.dayField.getValue();
30426     },
30427     
30428     getMonth : function()
30429     {
30430         return this.monthField.getValue();
30431     },
30432     
30433     getYear : function()
30434     {
30435         return this.yearField.getValue();
30436     },
30437     
30438     getValue : function()
30439     {
30440         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30441         
30442         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30443         
30444         return date;
30445     },
30446     
30447     reset : function()
30448     {
30449         this.setDay('');
30450         this.setMonth('');
30451         this.setYear('');
30452         this.inputEl.dom.value = '';
30453         this.validate();
30454         return;
30455     },
30456     
30457     validate : function()
30458     {
30459         var d = this.dayField.validate();
30460         var m = this.monthField.validate();
30461         var y = this.yearField.validate();
30462         
30463         var valid = true;
30464         
30465         if(
30466                 (!this.dayAllowBlank && !d) ||
30467                 (!this.monthAllowBlank && !m) ||
30468                 (!this.yearAllowBlank && !y)
30469         ){
30470             valid = false;
30471         }
30472         
30473         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30474             return valid;
30475         }
30476         
30477         if(valid){
30478             this.markValid();
30479             return valid;
30480         }
30481         
30482         this.markInvalid();
30483         
30484         return valid;
30485     },
30486     
30487     markValid : function()
30488     {
30489         
30490         var label = this.el.select('label', true).first();
30491         var icon = this.el.select('i.fa-star', true).first();
30492
30493         if(label && icon){
30494             icon.remove();
30495         }
30496         
30497         this.fireEvent('valid', this);
30498     },
30499     
30500      /**
30501      * Mark this field as invalid
30502      * @param {String} msg The validation message
30503      */
30504     markInvalid : function(msg)
30505     {
30506         
30507         var label = this.el.select('label', true).first();
30508         var icon = this.el.select('i.fa-star', true).first();
30509
30510         if(label && !icon){
30511             this.el.select('.roo-date-split-field-label', true).createChild({
30512                 tag : 'i',
30513                 cls : 'text-danger fa fa-lg fa-star',
30514                 tooltip : 'This field is required',
30515                 style : 'margin-right:5px;'
30516             }, label, true);
30517         }
30518         
30519         this.fireEvent('invalid', this, msg);
30520     },
30521     
30522     clearInvalid : function()
30523     {
30524         var label = this.el.select('label', true).first();
30525         var icon = this.el.select('i.fa-star', true).first();
30526
30527         if(label && icon){
30528             icon.remove();
30529         }
30530         
30531         this.fireEvent('valid', this);
30532     },
30533     
30534     getName: function()
30535     {
30536         return this.name;
30537     }
30538     
30539 });
30540
30541  /**
30542  *
30543  * This is based on 
30544  * http://masonry.desandro.com
30545  *
30546  * The idea is to render all the bricks based on vertical width...
30547  *
30548  * The original code extends 'outlayer' - we might need to use that....
30549  * 
30550  */
30551
30552
30553 /**
30554  * @class Roo.bootstrap.LayoutMasonry
30555  * @extends Roo.bootstrap.Component
30556  * Bootstrap Layout Masonry class
30557  * 
30558  * @constructor
30559  * Create a new Element
30560  * @param {Object} config The config object
30561  */
30562
30563 Roo.bootstrap.LayoutMasonry = function(config){
30564     
30565     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30566     
30567     this.bricks = [];
30568     
30569     Roo.bootstrap.LayoutMasonry.register(this);
30570     
30571     this.addEvents({
30572         // raw events
30573         /**
30574          * @event layout
30575          * Fire after layout the items
30576          * @param {Roo.bootstrap.LayoutMasonry} this
30577          * @param {Roo.EventObject} e
30578          */
30579         "layout" : true
30580     });
30581     
30582 };
30583
30584 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30585     
30586     /**
30587      * @cfg {Boolean} isLayoutInstant = no animation?
30588      */   
30589     isLayoutInstant : false, // needed?
30590    
30591     /**
30592      * @cfg {Number} boxWidth  width of the columns
30593      */   
30594     boxWidth : 450,
30595     
30596       /**
30597      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30598      */   
30599     boxHeight : 0,
30600     
30601     /**
30602      * @cfg {Number} padWidth padding below box..
30603      */   
30604     padWidth : 10, 
30605     
30606     /**
30607      * @cfg {Number} gutter gutter width..
30608      */   
30609     gutter : 10,
30610     
30611      /**
30612      * @cfg {Number} maxCols maximum number of columns
30613      */   
30614     
30615     maxCols: 0,
30616     
30617     /**
30618      * @cfg {Boolean} isAutoInitial defalut true
30619      */   
30620     isAutoInitial : true, 
30621     
30622     containerWidth: 0,
30623     
30624     /**
30625      * @cfg {Boolean} isHorizontal defalut false
30626      */   
30627     isHorizontal : false, 
30628
30629     currentSize : null,
30630     
30631     tag: 'div',
30632     
30633     cls: '',
30634     
30635     bricks: null, //CompositeElement
30636     
30637     cols : 1,
30638     
30639     _isLayoutInited : false,
30640     
30641 //    isAlternative : false, // only use for vertical layout...
30642     
30643     /**
30644      * @cfg {Number} alternativePadWidth padding below box..
30645      */   
30646     alternativePadWidth : 50,
30647     
30648     selectedBrick : [],
30649     
30650     getAutoCreate : function(){
30651         
30652         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30653         
30654         var cfg = {
30655             tag: this.tag,
30656             cls: 'blog-masonary-wrapper ' + this.cls,
30657             cn : {
30658                 cls : 'mas-boxes masonary'
30659             }
30660         };
30661         
30662         return cfg;
30663     },
30664     
30665     getChildContainer: function( )
30666     {
30667         if (this.boxesEl) {
30668             return this.boxesEl;
30669         }
30670         
30671         this.boxesEl = this.el.select('.mas-boxes').first();
30672         
30673         return this.boxesEl;
30674     },
30675     
30676     
30677     initEvents : function()
30678     {
30679         var _this = this;
30680         
30681         if(this.isAutoInitial){
30682             Roo.log('hook children rendered');
30683             this.on('childrenrendered', function() {
30684                 Roo.log('children rendered');
30685                 _this.initial();
30686             } ,this);
30687         }
30688     },
30689     
30690     initial : function()
30691     {
30692         this.selectedBrick = [];
30693         
30694         this.currentSize = this.el.getBox(true);
30695         
30696         Roo.EventManager.onWindowResize(this.resize, this); 
30697
30698         if(!this.isAutoInitial){
30699             this.layout();
30700             return;
30701         }
30702         
30703         this.layout();
30704         
30705         return;
30706         //this.layout.defer(500,this);
30707         
30708     },
30709     
30710     resize : function()
30711     {
30712         var cs = this.el.getBox(true);
30713         
30714         if (
30715                 this.currentSize.width == cs.width && 
30716                 this.currentSize.x == cs.x && 
30717                 this.currentSize.height == cs.height && 
30718                 this.currentSize.y == cs.y 
30719         ) {
30720             Roo.log("no change in with or X or Y");
30721             return;
30722         }
30723         
30724         this.currentSize = cs;
30725         
30726         this.layout();
30727         
30728     },
30729     
30730     layout : function()
30731     {   
30732         this._resetLayout();
30733         
30734         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30735         
30736         this.layoutItems( isInstant );
30737       
30738         this._isLayoutInited = true;
30739         
30740         this.fireEvent('layout', this);
30741         
30742     },
30743     
30744     _resetLayout : function()
30745     {
30746         if(this.isHorizontal){
30747             this.horizontalMeasureColumns();
30748             return;
30749         }
30750         
30751         this.verticalMeasureColumns();
30752         
30753     },
30754     
30755     verticalMeasureColumns : function()
30756     {
30757         this.getContainerWidth();
30758         
30759 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30760 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30761 //            return;
30762 //        }
30763         
30764         var boxWidth = this.boxWidth + this.padWidth;
30765         
30766         if(this.containerWidth < this.boxWidth){
30767             boxWidth = this.containerWidth
30768         }
30769         
30770         var containerWidth = this.containerWidth;
30771         
30772         var cols = Math.floor(containerWidth / boxWidth);
30773         
30774         this.cols = Math.max( cols, 1 );
30775         
30776         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30777         
30778         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30779         
30780         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30781         
30782         this.colWidth = boxWidth + avail - this.padWidth;
30783         
30784         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30785         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30786     },
30787     
30788     horizontalMeasureColumns : function()
30789     {
30790         this.getContainerWidth();
30791         
30792         var boxWidth = this.boxWidth;
30793         
30794         if(this.containerWidth < boxWidth){
30795             boxWidth = this.containerWidth;
30796         }
30797         
30798         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30799         
30800         this.el.setHeight(boxWidth);
30801         
30802     },
30803     
30804     getContainerWidth : function()
30805     {
30806         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30807     },
30808     
30809     layoutItems : function( isInstant )
30810     {
30811         Roo.log(this.bricks);
30812         
30813         var items = Roo.apply([], this.bricks);
30814         
30815         if(this.isHorizontal){
30816             this._horizontalLayoutItems( items , isInstant );
30817             return;
30818         }
30819         
30820 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30821 //            this._verticalAlternativeLayoutItems( items , isInstant );
30822 //            return;
30823 //        }
30824         
30825         this._verticalLayoutItems( items , isInstant );
30826         
30827     },
30828     
30829     _verticalLayoutItems : function ( items , isInstant)
30830     {
30831         if ( !items || !items.length ) {
30832             return;
30833         }
30834         
30835         var standard = [
30836             ['xs', 'xs', 'xs', 'tall'],
30837             ['xs', 'xs', 'tall'],
30838             ['xs', 'xs', 'sm'],
30839             ['xs', 'xs', 'xs'],
30840             ['xs', 'tall'],
30841             ['xs', 'sm'],
30842             ['xs', 'xs'],
30843             ['xs'],
30844             
30845             ['sm', 'xs', 'xs'],
30846             ['sm', 'xs'],
30847             ['sm'],
30848             
30849             ['tall', 'xs', 'xs', 'xs'],
30850             ['tall', 'xs', 'xs'],
30851             ['tall', 'xs'],
30852             ['tall']
30853             
30854         ];
30855         
30856         var queue = [];
30857         
30858         var boxes = [];
30859         
30860         var box = [];
30861         
30862         Roo.each(items, function(item, k){
30863             
30864             switch (item.size) {
30865                 // these layouts take up a full box,
30866                 case 'md' :
30867                 case 'md-left' :
30868                 case 'md-right' :
30869                 case 'wide' :
30870                     
30871                     if(box.length){
30872                         boxes.push(box);
30873                         box = [];
30874                     }
30875                     
30876                     boxes.push([item]);
30877                     
30878                     break;
30879                     
30880                 case 'xs' :
30881                 case 'sm' :
30882                 case 'tall' :
30883                     
30884                     box.push(item);
30885                     
30886                     break;
30887                 default :
30888                     break;
30889                     
30890             }
30891             
30892         }, this);
30893         
30894         if(box.length){
30895             boxes.push(box);
30896             box = [];
30897         }
30898         
30899         var filterPattern = function(box, length)
30900         {
30901             if(!box.length){
30902                 return;
30903             }
30904             
30905             var match = false;
30906             
30907             var pattern = box.slice(0, length);
30908             
30909             var format = [];
30910             
30911             Roo.each(pattern, function(i){
30912                 format.push(i.size);
30913             }, this);
30914             
30915             Roo.each(standard, function(s){
30916                 
30917                 if(String(s) != String(format)){
30918                     return;
30919                 }
30920                 
30921                 match = true;
30922                 return false;
30923                 
30924             }, this);
30925             
30926             if(!match && length == 1){
30927                 return;
30928             }
30929             
30930             if(!match){
30931                 filterPattern(box, length - 1);
30932                 return;
30933             }
30934                 
30935             queue.push(pattern);
30936
30937             box = box.slice(length, box.length);
30938
30939             filterPattern(box, 4);
30940
30941             return;
30942             
30943         }
30944         
30945         Roo.each(boxes, function(box, k){
30946             
30947             if(!box.length){
30948                 return;
30949             }
30950             
30951             if(box.length == 1){
30952                 queue.push(box);
30953                 return;
30954             }
30955             
30956             filterPattern(box, 4);
30957             
30958         }, this);
30959         
30960         this._processVerticalLayoutQueue( queue, isInstant );
30961         
30962     },
30963     
30964 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30965 //    {
30966 //        if ( !items || !items.length ) {
30967 //            return;
30968 //        }
30969 //
30970 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30971 //        
30972 //    },
30973     
30974     _horizontalLayoutItems : function ( items , isInstant)
30975     {
30976         if ( !items || !items.length || items.length < 3) {
30977             return;
30978         }
30979         
30980         items.reverse();
30981         
30982         var eItems = items.slice(0, 3);
30983         
30984         items = items.slice(3, items.length);
30985         
30986         var standard = [
30987             ['xs', 'xs', 'xs', 'wide'],
30988             ['xs', 'xs', 'wide'],
30989             ['xs', 'xs', 'sm'],
30990             ['xs', 'xs', 'xs'],
30991             ['xs', 'wide'],
30992             ['xs', 'sm'],
30993             ['xs', 'xs'],
30994             ['xs'],
30995             
30996             ['sm', 'xs', 'xs'],
30997             ['sm', 'xs'],
30998             ['sm'],
30999             
31000             ['wide', 'xs', 'xs', 'xs'],
31001             ['wide', 'xs', 'xs'],
31002             ['wide', 'xs'],
31003             ['wide'],
31004             
31005             ['wide-thin']
31006         ];
31007         
31008         var queue = [];
31009         
31010         var boxes = [];
31011         
31012         var box = [];
31013         
31014         Roo.each(items, function(item, k){
31015             
31016             switch (item.size) {
31017                 case 'md' :
31018                 case 'md-left' :
31019                 case 'md-right' :
31020                 case 'tall' :
31021                     
31022                     if(box.length){
31023                         boxes.push(box);
31024                         box = [];
31025                     }
31026                     
31027                     boxes.push([item]);
31028                     
31029                     break;
31030                     
31031                 case 'xs' :
31032                 case 'sm' :
31033                 case 'wide' :
31034                 case 'wide-thin' :
31035                     
31036                     box.push(item);
31037                     
31038                     break;
31039                 default :
31040                     break;
31041                     
31042             }
31043             
31044         }, this);
31045         
31046         if(box.length){
31047             boxes.push(box);
31048             box = [];
31049         }
31050         
31051         var filterPattern = function(box, length)
31052         {
31053             if(!box.length){
31054                 return;
31055             }
31056             
31057             var match = false;
31058             
31059             var pattern = box.slice(0, length);
31060             
31061             var format = [];
31062             
31063             Roo.each(pattern, function(i){
31064                 format.push(i.size);
31065             }, this);
31066             
31067             Roo.each(standard, function(s){
31068                 
31069                 if(String(s) != String(format)){
31070                     return;
31071                 }
31072                 
31073                 match = true;
31074                 return false;
31075                 
31076             }, this);
31077             
31078             if(!match && length == 1){
31079                 return;
31080             }
31081             
31082             if(!match){
31083                 filterPattern(box, length - 1);
31084                 return;
31085             }
31086                 
31087             queue.push(pattern);
31088
31089             box = box.slice(length, box.length);
31090
31091             filterPattern(box, 4);
31092
31093             return;
31094             
31095         }
31096         
31097         Roo.each(boxes, function(box, k){
31098             
31099             if(!box.length){
31100                 return;
31101             }
31102             
31103             if(box.length == 1){
31104                 queue.push(box);
31105                 return;
31106             }
31107             
31108             filterPattern(box, 4);
31109             
31110         }, this);
31111         
31112         
31113         var prune = [];
31114         
31115         var pos = this.el.getBox(true);
31116         
31117         var minX = pos.x;
31118         
31119         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31120         
31121         var hit_end = false;
31122         
31123         Roo.each(queue, function(box){
31124             
31125             if(hit_end){
31126                 
31127                 Roo.each(box, function(b){
31128                 
31129                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31130                     b.el.hide();
31131
31132                 }, this);
31133
31134                 return;
31135             }
31136             
31137             var mx = 0;
31138             
31139             Roo.each(box, function(b){
31140                 
31141                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31142                 b.el.show();
31143
31144                 mx = Math.max(mx, b.x);
31145                 
31146             }, this);
31147             
31148             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31149             
31150             if(maxX < minX){
31151                 
31152                 Roo.each(box, function(b){
31153                 
31154                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31155                     b.el.hide();
31156                     
31157                 }, this);
31158                 
31159                 hit_end = true;
31160                 
31161                 return;
31162             }
31163             
31164             prune.push(box);
31165             
31166         }, this);
31167         
31168         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31169     },
31170     
31171     /** Sets position of item in DOM
31172     * @param {Element} item
31173     * @param {Number} x - horizontal position
31174     * @param {Number} y - vertical position
31175     * @param {Boolean} isInstant - disables transitions
31176     */
31177     _processVerticalLayoutQueue : function( queue, isInstant )
31178     {
31179         var pos = this.el.getBox(true);
31180         var x = pos.x;
31181         var y = pos.y;
31182         var maxY = [];
31183         
31184         for (var i = 0; i < this.cols; i++){
31185             maxY[i] = pos.y;
31186         }
31187         
31188         Roo.each(queue, function(box, k){
31189             
31190             var col = k % this.cols;
31191             
31192             Roo.each(box, function(b,kk){
31193                 
31194                 b.el.position('absolute');
31195                 
31196                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31197                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31198                 
31199                 if(b.size == 'md-left' || b.size == 'md-right'){
31200                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31201                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31202                 }
31203                 
31204                 b.el.setWidth(width);
31205                 b.el.setHeight(height);
31206                 // iframe?
31207                 b.el.select('iframe',true).setSize(width,height);
31208                 
31209             }, this);
31210             
31211             for (var i = 0; i < this.cols; i++){
31212                 
31213                 if(maxY[i] < maxY[col]){
31214                     col = i;
31215                     continue;
31216                 }
31217                 
31218                 col = Math.min(col, i);
31219                 
31220             }
31221             
31222             x = pos.x + col * (this.colWidth + this.padWidth);
31223             
31224             y = maxY[col];
31225             
31226             var positions = [];
31227             
31228             switch (box.length){
31229                 case 1 :
31230                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31231                     break;
31232                 case 2 :
31233                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31234                     break;
31235                 case 3 :
31236                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31237                     break;
31238                 case 4 :
31239                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31240                     break;
31241                 default :
31242                     break;
31243             }
31244             
31245             Roo.each(box, function(b,kk){
31246                 
31247                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31248                 
31249                 var sz = b.el.getSize();
31250                 
31251                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31252                 
31253             }, this);
31254             
31255         }, this);
31256         
31257         var mY = 0;
31258         
31259         for (var i = 0; i < this.cols; i++){
31260             mY = Math.max(mY, maxY[i]);
31261         }
31262         
31263         this.el.setHeight(mY - pos.y);
31264         
31265     },
31266     
31267 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31268 //    {
31269 //        var pos = this.el.getBox(true);
31270 //        var x = pos.x;
31271 //        var y = pos.y;
31272 //        var maxX = pos.right;
31273 //        
31274 //        var maxHeight = 0;
31275 //        
31276 //        Roo.each(items, function(item, k){
31277 //            
31278 //            var c = k % 2;
31279 //            
31280 //            item.el.position('absolute');
31281 //                
31282 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31283 //
31284 //            item.el.setWidth(width);
31285 //
31286 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31287 //
31288 //            item.el.setHeight(height);
31289 //            
31290 //            if(c == 0){
31291 //                item.el.setXY([x, y], isInstant ? false : true);
31292 //            } else {
31293 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31294 //            }
31295 //            
31296 //            y = y + height + this.alternativePadWidth;
31297 //            
31298 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31299 //            
31300 //        }, this);
31301 //        
31302 //        this.el.setHeight(maxHeight);
31303 //        
31304 //    },
31305     
31306     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31307     {
31308         var pos = this.el.getBox(true);
31309         
31310         var minX = pos.x;
31311         var minY = pos.y;
31312         
31313         var maxX = pos.right;
31314         
31315         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31316         
31317         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31318         
31319         Roo.each(queue, function(box, k){
31320             
31321             Roo.each(box, function(b, kk){
31322                 
31323                 b.el.position('absolute');
31324                 
31325                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31326                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31327                 
31328                 if(b.size == 'md-left' || b.size == 'md-right'){
31329                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31330                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31331                 }
31332                 
31333                 b.el.setWidth(width);
31334                 b.el.setHeight(height);
31335                 
31336             }, this);
31337             
31338             if(!box.length){
31339                 return;
31340             }
31341             
31342             var positions = [];
31343             
31344             switch (box.length){
31345                 case 1 :
31346                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31347                     break;
31348                 case 2 :
31349                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31350                     break;
31351                 case 3 :
31352                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31353                     break;
31354                 case 4 :
31355                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31356                     break;
31357                 default :
31358                     break;
31359             }
31360             
31361             Roo.each(box, function(b,kk){
31362                 
31363                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31364                 
31365                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31366                 
31367             }, this);
31368             
31369         }, this);
31370         
31371     },
31372     
31373     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31374     {
31375         Roo.each(eItems, function(b,k){
31376             
31377             b.size = (k == 0) ? 'sm' : 'xs';
31378             b.x = (k == 0) ? 2 : 1;
31379             b.y = (k == 0) ? 2 : 1;
31380             
31381             b.el.position('absolute');
31382             
31383             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31384                 
31385             b.el.setWidth(width);
31386             
31387             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31388             
31389             b.el.setHeight(height);
31390             
31391         }, this);
31392
31393         var positions = [];
31394         
31395         positions.push({
31396             x : maxX - this.unitWidth * 2 - this.gutter,
31397             y : minY
31398         });
31399         
31400         positions.push({
31401             x : maxX - this.unitWidth,
31402             y : minY + (this.unitWidth + this.gutter) * 2
31403         });
31404         
31405         positions.push({
31406             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31407             y : minY
31408         });
31409         
31410         Roo.each(eItems, function(b,k){
31411             
31412             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31413
31414         }, this);
31415         
31416     },
31417     
31418     getVerticalOneBoxColPositions : function(x, y, box)
31419     {
31420         var pos = [];
31421         
31422         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31423         
31424         if(box[0].size == 'md-left'){
31425             rand = 0;
31426         }
31427         
31428         if(box[0].size == 'md-right'){
31429             rand = 1;
31430         }
31431         
31432         pos.push({
31433             x : x + (this.unitWidth + this.gutter) * rand,
31434             y : y
31435         });
31436         
31437         return pos;
31438     },
31439     
31440     getVerticalTwoBoxColPositions : function(x, y, box)
31441     {
31442         var pos = [];
31443         
31444         if(box[0].size == 'xs'){
31445             
31446             pos.push({
31447                 x : x,
31448                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31449             });
31450
31451             pos.push({
31452                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31453                 y : y
31454             });
31455             
31456             return pos;
31457             
31458         }
31459         
31460         pos.push({
31461             x : x,
31462             y : y
31463         });
31464
31465         pos.push({
31466             x : x + (this.unitWidth + this.gutter) * 2,
31467             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31468         });
31469         
31470         return pos;
31471         
31472     },
31473     
31474     getVerticalThreeBoxColPositions : function(x, y, box)
31475     {
31476         var pos = [];
31477         
31478         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31479             
31480             pos.push({
31481                 x : x,
31482                 y : y
31483             });
31484
31485             pos.push({
31486                 x : x + (this.unitWidth + this.gutter) * 1,
31487                 y : y
31488             });
31489             
31490             pos.push({
31491                 x : x + (this.unitWidth + this.gutter) * 2,
31492                 y : y
31493             });
31494             
31495             return pos;
31496             
31497         }
31498         
31499         if(box[0].size == 'xs' && box[1].size == 'xs'){
31500             
31501             pos.push({
31502                 x : x,
31503                 y : y
31504             });
31505
31506             pos.push({
31507                 x : x,
31508                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31509             });
31510             
31511             pos.push({
31512                 x : x + (this.unitWidth + this.gutter) * 1,
31513                 y : y
31514             });
31515             
31516             return pos;
31517             
31518         }
31519         
31520         pos.push({
31521             x : x,
31522             y : y
31523         });
31524
31525         pos.push({
31526             x : x + (this.unitWidth + this.gutter) * 2,
31527             y : y
31528         });
31529
31530         pos.push({
31531             x : x + (this.unitWidth + this.gutter) * 2,
31532             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31533         });
31534             
31535         return pos;
31536         
31537     },
31538     
31539     getVerticalFourBoxColPositions : function(x, y, box)
31540     {
31541         var pos = [];
31542         
31543         if(box[0].size == 'xs'){
31544             
31545             pos.push({
31546                 x : x,
31547                 y : y
31548             });
31549
31550             pos.push({
31551                 x : x,
31552                 y : y + (this.unitHeight + this.gutter) * 1
31553             });
31554             
31555             pos.push({
31556                 x : x,
31557                 y : y + (this.unitHeight + this.gutter) * 2
31558             });
31559             
31560             pos.push({
31561                 x : x + (this.unitWidth + this.gutter) * 1,
31562                 y : y
31563             });
31564             
31565             return pos;
31566             
31567         }
31568         
31569         pos.push({
31570             x : x,
31571             y : y
31572         });
31573
31574         pos.push({
31575             x : x + (this.unitWidth + this.gutter) * 2,
31576             y : y
31577         });
31578
31579         pos.push({
31580             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31581             y : y + (this.unitHeight + this.gutter) * 1
31582         });
31583
31584         pos.push({
31585             x : x + (this.unitWidth + this.gutter) * 2,
31586             y : y + (this.unitWidth + this.gutter) * 2
31587         });
31588
31589         return pos;
31590         
31591     },
31592     
31593     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31594     {
31595         var pos = [];
31596         
31597         if(box[0].size == 'md-left'){
31598             pos.push({
31599                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31600                 y : minY
31601             });
31602             
31603             return pos;
31604         }
31605         
31606         if(box[0].size == 'md-right'){
31607             pos.push({
31608                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31609                 y : minY + (this.unitWidth + this.gutter) * 1
31610             });
31611             
31612             return pos;
31613         }
31614         
31615         var rand = Math.floor(Math.random() * (4 - box[0].y));
31616         
31617         pos.push({
31618             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31619             y : minY + (this.unitWidth + this.gutter) * rand
31620         });
31621         
31622         return pos;
31623         
31624     },
31625     
31626     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31627     {
31628         var pos = [];
31629         
31630         if(box[0].size == 'xs'){
31631             
31632             pos.push({
31633                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31634                 y : minY
31635             });
31636
31637             pos.push({
31638                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31639                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31640             });
31641             
31642             return pos;
31643             
31644         }
31645         
31646         pos.push({
31647             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31648             y : minY
31649         });
31650
31651         pos.push({
31652             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31653             y : minY + (this.unitWidth + this.gutter) * 2
31654         });
31655         
31656         return pos;
31657         
31658     },
31659     
31660     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31661     {
31662         var pos = [];
31663         
31664         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31665             
31666             pos.push({
31667                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31668                 y : minY
31669             });
31670
31671             pos.push({
31672                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31673                 y : minY + (this.unitWidth + this.gutter) * 1
31674             });
31675             
31676             pos.push({
31677                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31678                 y : minY + (this.unitWidth + this.gutter) * 2
31679             });
31680             
31681             return pos;
31682             
31683         }
31684         
31685         if(box[0].size == 'xs' && box[1].size == 'xs'){
31686             
31687             pos.push({
31688                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31689                 y : minY
31690             });
31691
31692             pos.push({
31693                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31694                 y : minY
31695             });
31696             
31697             pos.push({
31698                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31699                 y : minY + (this.unitWidth + this.gutter) * 1
31700             });
31701             
31702             return pos;
31703             
31704         }
31705         
31706         pos.push({
31707             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31708             y : minY
31709         });
31710
31711         pos.push({
31712             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31713             y : minY + (this.unitWidth + this.gutter) * 2
31714         });
31715
31716         pos.push({
31717             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31718             y : minY + (this.unitWidth + this.gutter) * 2
31719         });
31720             
31721         return pos;
31722         
31723     },
31724     
31725     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31726     {
31727         var pos = [];
31728         
31729         if(box[0].size == 'xs'){
31730             
31731             pos.push({
31732                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31733                 y : minY
31734             });
31735
31736             pos.push({
31737                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31738                 y : minY
31739             });
31740             
31741             pos.push({
31742                 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),
31743                 y : minY
31744             });
31745             
31746             pos.push({
31747                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31748                 y : minY + (this.unitWidth + this.gutter) * 1
31749             });
31750             
31751             return pos;
31752             
31753         }
31754         
31755         pos.push({
31756             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31757             y : minY
31758         });
31759         
31760         pos.push({
31761             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31762             y : minY + (this.unitWidth + this.gutter) * 2
31763         });
31764         
31765         pos.push({
31766             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31767             y : minY + (this.unitWidth + this.gutter) * 2
31768         });
31769         
31770         pos.push({
31771             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),
31772             y : minY + (this.unitWidth + this.gutter) * 2
31773         });
31774
31775         return pos;
31776         
31777     },
31778     
31779     /**
31780     * remove a Masonry Brick
31781     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31782     */
31783     removeBrick : function(brick_id)
31784     {
31785         if (!brick_id) {
31786             return;
31787         }
31788         
31789         for (var i = 0; i<this.bricks.length; i++) {
31790             if (this.bricks[i].id == brick_id) {
31791                 this.bricks.splice(i,1);
31792                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31793                 this.initial();
31794             }
31795         }
31796     },
31797     
31798     /**
31799     * adds a Masonry Brick
31800     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31801     */
31802     addBrick : function(cfg)
31803     {
31804         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31805         //this.register(cn);
31806         cn.parentId = this.id;
31807         cn.onRender(this.el, null);
31808         return cn;
31809     },
31810     
31811     /**
31812     * register a Masonry Brick
31813     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31814     */
31815     
31816     register : function(brick)
31817     {
31818         this.bricks.push(brick);
31819         brick.masonryId = this.id;
31820     },
31821     
31822     /**
31823     * clear all the Masonry Brick
31824     */
31825     clearAll : function()
31826     {
31827         this.bricks = [];
31828         //this.getChildContainer().dom.innerHTML = "";
31829         this.el.dom.innerHTML = '';
31830     },
31831     
31832     getSelected : function()
31833     {
31834         if (!this.selectedBrick) {
31835             return false;
31836         }
31837         
31838         return this.selectedBrick;
31839     }
31840 });
31841
31842 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31843     
31844     groups: {},
31845      /**
31846     * register a Masonry Layout
31847     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31848     */
31849     
31850     register : function(layout)
31851     {
31852         this.groups[layout.id] = layout;
31853     },
31854     /**
31855     * fetch a  Masonry Layout based on the masonry layout ID
31856     * @param {string} the masonry layout to add
31857     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31858     */
31859     
31860     get: function(layout_id) {
31861         if (typeof(this.groups[layout_id]) == 'undefined') {
31862             return false;
31863         }
31864         return this.groups[layout_id] ;
31865     }
31866     
31867     
31868     
31869 });
31870
31871  
31872
31873  /**
31874  *
31875  * This is based on 
31876  * http://masonry.desandro.com
31877  *
31878  * The idea is to render all the bricks based on vertical width...
31879  *
31880  * The original code extends 'outlayer' - we might need to use that....
31881  * 
31882  */
31883
31884
31885 /**
31886  * @class Roo.bootstrap.LayoutMasonryAuto
31887  * @extends Roo.bootstrap.Component
31888  * Bootstrap Layout Masonry class
31889  * 
31890  * @constructor
31891  * Create a new Element
31892  * @param {Object} config The config object
31893  */
31894
31895 Roo.bootstrap.LayoutMasonryAuto = function(config){
31896     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31897 };
31898
31899 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31900     
31901       /**
31902      * @cfg {Boolean} isFitWidth  - resize the width..
31903      */   
31904     isFitWidth : false,  // options..
31905     /**
31906      * @cfg {Boolean} isOriginLeft = left align?
31907      */   
31908     isOriginLeft : true,
31909     /**
31910      * @cfg {Boolean} isOriginTop = top align?
31911      */   
31912     isOriginTop : false,
31913     /**
31914      * @cfg {Boolean} isLayoutInstant = no animation?
31915      */   
31916     isLayoutInstant : false, // needed?
31917     /**
31918      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31919      */   
31920     isResizingContainer : true,
31921     /**
31922      * @cfg {Number} columnWidth  width of the columns 
31923      */   
31924     
31925     columnWidth : 0,
31926     
31927     /**
31928      * @cfg {Number} maxCols maximum number of columns
31929      */   
31930     
31931     maxCols: 0,
31932     /**
31933      * @cfg {Number} padHeight padding below box..
31934      */   
31935     
31936     padHeight : 10, 
31937     
31938     /**
31939      * @cfg {Boolean} isAutoInitial defalut true
31940      */   
31941     
31942     isAutoInitial : true, 
31943     
31944     // private?
31945     gutter : 0,
31946     
31947     containerWidth: 0,
31948     initialColumnWidth : 0,
31949     currentSize : null,
31950     
31951     colYs : null, // array.
31952     maxY : 0,
31953     padWidth: 10,
31954     
31955     
31956     tag: 'div',
31957     cls: '',
31958     bricks: null, //CompositeElement
31959     cols : 0, // array?
31960     // element : null, // wrapped now this.el
31961     _isLayoutInited : null, 
31962     
31963     
31964     getAutoCreate : function(){
31965         
31966         var cfg = {
31967             tag: this.tag,
31968             cls: 'blog-masonary-wrapper ' + this.cls,
31969             cn : {
31970                 cls : 'mas-boxes masonary'
31971             }
31972         };
31973         
31974         return cfg;
31975     },
31976     
31977     getChildContainer: function( )
31978     {
31979         if (this.boxesEl) {
31980             return this.boxesEl;
31981         }
31982         
31983         this.boxesEl = this.el.select('.mas-boxes').first();
31984         
31985         return this.boxesEl;
31986     },
31987     
31988     
31989     initEvents : function()
31990     {
31991         var _this = this;
31992         
31993         if(this.isAutoInitial){
31994             Roo.log('hook children rendered');
31995             this.on('childrenrendered', function() {
31996                 Roo.log('children rendered');
31997                 _this.initial();
31998             } ,this);
31999         }
32000         
32001     },
32002     
32003     initial : function()
32004     {
32005         this.reloadItems();
32006
32007         this.currentSize = this.el.getBox(true);
32008
32009         /// was window resize... - let's see if this works..
32010         Roo.EventManager.onWindowResize(this.resize, this); 
32011
32012         if(!this.isAutoInitial){
32013             this.layout();
32014             return;
32015         }
32016         
32017         this.layout.defer(500,this);
32018     },
32019     
32020     reloadItems: function()
32021     {
32022         this.bricks = this.el.select('.masonry-brick', true);
32023         
32024         this.bricks.each(function(b) {
32025             //Roo.log(b.getSize());
32026             if (!b.attr('originalwidth')) {
32027                 b.attr('originalwidth',  b.getSize().width);
32028             }
32029             
32030         });
32031         
32032         Roo.log(this.bricks.elements.length);
32033     },
32034     
32035     resize : function()
32036     {
32037         Roo.log('resize');
32038         var cs = this.el.getBox(true);
32039         
32040         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32041             Roo.log("no change in with or X");
32042             return;
32043         }
32044         this.currentSize = cs;
32045         this.layout();
32046     },
32047     
32048     layout : function()
32049     {
32050          Roo.log('layout');
32051         this._resetLayout();
32052         //this._manageStamps();
32053       
32054         // don't animate first layout
32055         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32056         this.layoutItems( isInstant );
32057       
32058         // flag for initalized
32059         this._isLayoutInited = true;
32060     },
32061     
32062     layoutItems : function( isInstant )
32063     {
32064         //var items = this._getItemsForLayout( this.items );
32065         // original code supports filtering layout items.. we just ignore it..
32066         
32067         this._layoutItems( this.bricks , isInstant );
32068       
32069         this._postLayout();
32070     },
32071     _layoutItems : function ( items , isInstant)
32072     {
32073        //this.fireEvent( 'layout', this, items );
32074     
32075
32076         if ( !items || !items.elements.length ) {
32077           // no items, emit event with empty array
32078             return;
32079         }
32080
32081         var queue = [];
32082         items.each(function(item) {
32083             Roo.log("layout item");
32084             Roo.log(item);
32085             // get x/y object from method
32086             var position = this._getItemLayoutPosition( item );
32087             // enqueue
32088             position.item = item;
32089             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32090             queue.push( position );
32091         }, this);
32092       
32093         this._processLayoutQueue( queue );
32094     },
32095     /** Sets position of item in DOM
32096     * @param {Element} item
32097     * @param {Number} x - horizontal position
32098     * @param {Number} y - vertical position
32099     * @param {Boolean} isInstant - disables transitions
32100     */
32101     _processLayoutQueue : function( queue )
32102     {
32103         for ( var i=0, len = queue.length; i < len; i++ ) {
32104             var obj = queue[i];
32105             obj.item.position('absolute');
32106             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32107         }
32108     },
32109       
32110     
32111     /**
32112     * Any logic you want to do after each layout,
32113     * i.e. size the container
32114     */
32115     _postLayout : function()
32116     {
32117         this.resizeContainer();
32118     },
32119     
32120     resizeContainer : function()
32121     {
32122         if ( !this.isResizingContainer ) {
32123             return;
32124         }
32125         var size = this._getContainerSize();
32126         if ( size ) {
32127             this.el.setSize(size.width,size.height);
32128             this.boxesEl.setSize(size.width,size.height);
32129         }
32130     },
32131     
32132     
32133     
32134     _resetLayout : function()
32135     {
32136         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32137         this.colWidth = this.el.getWidth();
32138         //this.gutter = this.el.getWidth(); 
32139         
32140         this.measureColumns();
32141
32142         // reset column Y
32143         var i = this.cols;
32144         this.colYs = [];
32145         while (i--) {
32146             this.colYs.push( 0 );
32147         }
32148     
32149         this.maxY = 0;
32150     },
32151
32152     measureColumns : function()
32153     {
32154         this.getContainerWidth();
32155       // if columnWidth is 0, default to outerWidth of first item
32156         if ( !this.columnWidth ) {
32157             var firstItem = this.bricks.first();
32158             Roo.log(firstItem);
32159             this.columnWidth  = this.containerWidth;
32160             if (firstItem && firstItem.attr('originalwidth') ) {
32161                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32162             }
32163             // columnWidth fall back to item of first element
32164             Roo.log("set column width?");
32165                         this.initialColumnWidth = this.columnWidth  ;
32166
32167             // if first elem has no width, default to size of container
32168             
32169         }
32170         
32171         
32172         if (this.initialColumnWidth) {
32173             this.columnWidth = this.initialColumnWidth;
32174         }
32175         
32176         
32177             
32178         // column width is fixed at the top - however if container width get's smaller we should
32179         // reduce it...
32180         
32181         // this bit calcs how man columns..
32182             
32183         var columnWidth = this.columnWidth += this.gutter;
32184       
32185         // calculate columns
32186         var containerWidth = this.containerWidth + this.gutter;
32187         
32188         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32189         // fix rounding errors, typically with gutters
32190         var excess = columnWidth - containerWidth % columnWidth;
32191         
32192         
32193         // if overshoot is less than a pixel, round up, otherwise floor it
32194         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32195         cols = Math[ mathMethod ]( cols );
32196         this.cols = Math.max( cols, 1 );
32197         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32198         
32199          // padding positioning..
32200         var totalColWidth = this.cols * this.columnWidth;
32201         var padavail = this.containerWidth - totalColWidth;
32202         // so for 2 columns - we need 3 'pads'
32203         
32204         var padNeeded = (1+this.cols) * this.padWidth;
32205         
32206         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32207         
32208         this.columnWidth += padExtra
32209         //this.padWidth = Math.floor(padavail /  ( this.cols));
32210         
32211         // adjust colum width so that padding is fixed??
32212         
32213         // we have 3 columns ... total = width * 3
32214         // we have X left over... that should be used by 
32215         
32216         //if (this.expandC) {
32217             
32218         //}
32219         
32220         
32221         
32222     },
32223     
32224     getContainerWidth : function()
32225     {
32226        /* // container is parent if fit width
32227         var container = this.isFitWidth ? this.element.parentNode : this.element;
32228         // check that this.size and size are there
32229         // IE8 triggers resize on body size change, so they might not be
32230         
32231         var size = getSize( container );  //FIXME
32232         this.containerWidth = size && size.innerWidth; //FIXME
32233         */
32234          
32235         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32236         
32237     },
32238     
32239     _getItemLayoutPosition : function( item )  // what is item?
32240     {
32241         // we resize the item to our columnWidth..
32242       
32243         item.setWidth(this.columnWidth);
32244         item.autoBoxAdjust  = false;
32245         
32246         var sz = item.getSize();
32247  
32248         // how many columns does this brick span
32249         var remainder = this.containerWidth % this.columnWidth;
32250         
32251         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32252         // round if off by 1 pixel, otherwise use ceil
32253         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32254         colSpan = Math.min( colSpan, this.cols );
32255         
32256         // normally this should be '1' as we dont' currently allow multi width columns..
32257         
32258         var colGroup = this._getColGroup( colSpan );
32259         // get the minimum Y value from the columns
32260         var minimumY = Math.min.apply( Math, colGroup );
32261         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32262         
32263         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32264          
32265         // position the brick
32266         var position = {
32267             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32268             y: this.currentSize.y + minimumY + this.padHeight
32269         };
32270         
32271         Roo.log(position);
32272         // apply setHeight to necessary columns
32273         var setHeight = minimumY + sz.height + this.padHeight;
32274         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32275         
32276         var setSpan = this.cols + 1 - colGroup.length;
32277         for ( var i = 0; i < setSpan; i++ ) {
32278           this.colYs[ shortColIndex + i ] = setHeight ;
32279         }
32280       
32281         return position;
32282     },
32283     
32284     /**
32285      * @param {Number} colSpan - number of columns the element spans
32286      * @returns {Array} colGroup
32287      */
32288     _getColGroup : function( colSpan )
32289     {
32290         if ( colSpan < 2 ) {
32291           // if brick spans only one column, use all the column Ys
32292           return this.colYs;
32293         }
32294       
32295         var colGroup = [];
32296         // how many different places could this brick fit horizontally
32297         var groupCount = this.cols + 1 - colSpan;
32298         // for each group potential horizontal position
32299         for ( var i = 0; i < groupCount; i++ ) {
32300           // make an array of colY values for that one group
32301           var groupColYs = this.colYs.slice( i, i + colSpan );
32302           // and get the max value of the array
32303           colGroup[i] = Math.max.apply( Math, groupColYs );
32304         }
32305         return colGroup;
32306     },
32307     /*
32308     _manageStamp : function( stamp )
32309     {
32310         var stampSize =  stamp.getSize();
32311         var offset = stamp.getBox();
32312         // get the columns that this stamp affects
32313         var firstX = this.isOriginLeft ? offset.x : offset.right;
32314         var lastX = firstX + stampSize.width;
32315         var firstCol = Math.floor( firstX / this.columnWidth );
32316         firstCol = Math.max( 0, firstCol );
32317         
32318         var lastCol = Math.floor( lastX / this.columnWidth );
32319         // lastCol should not go over if multiple of columnWidth #425
32320         lastCol -= lastX % this.columnWidth ? 0 : 1;
32321         lastCol = Math.min( this.cols - 1, lastCol );
32322         
32323         // set colYs to bottom of the stamp
32324         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32325             stampSize.height;
32326             
32327         for ( var i = firstCol; i <= lastCol; i++ ) {
32328           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32329         }
32330     },
32331     */
32332     
32333     _getContainerSize : function()
32334     {
32335         this.maxY = Math.max.apply( Math, this.colYs );
32336         var size = {
32337             height: this.maxY
32338         };
32339       
32340         if ( this.isFitWidth ) {
32341             size.width = this._getContainerFitWidth();
32342         }
32343       
32344         return size;
32345     },
32346     
32347     _getContainerFitWidth : function()
32348     {
32349         var unusedCols = 0;
32350         // count unused columns
32351         var i = this.cols;
32352         while ( --i ) {
32353           if ( this.colYs[i] !== 0 ) {
32354             break;
32355           }
32356           unusedCols++;
32357         }
32358         // fit container to columns that have been used
32359         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32360     },
32361     
32362     needsResizeLayout : function()
32363     {
32364         var previousWidth = this.containerWidth;
32365         this.getContainerWidth();
32366         return previousWidth !== this.containerWidth;
32367     }
32368  
32369 });
32370
32371  
32372
32373  /*
32374  * - LGPL
32375  *
32376  * element
32377  * 
32378  */
32379
32380 /**
32381  * @class Roo.bootstrap.MasonryBrick
32382  * @extends Roo.bootstrap.Component
32383  * Bootstrap MasonryBrick class
32384  * 
32385  * @constructor
32386  * Create a new MasonryBrick
32387  * @param {Object} config The config object
32388  */
32389
32390 Roo.bootstrap.MasonryBrick = function(config){
32391     
32392     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32393     
32394     Roo.bootstrap.MasonryBrick.register(this);
32395     
32396     this.addEvents({
32397         // raw events
32398         /**
32399          * @event click
32400          * When a MasonryBrick is clcik
32401          * @param {Roo.bootstrap.MasonryBrick} this
32402          * @param {Roo.EventObject} e
32403          */
32404         "click" : true
32405     });
32406 };
32407
32408 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32409     
32410     /**
32411      * @cfg {String} title
32412      */   
32413     title : '',
32414     /**
32415      * @cfg {String} html
32416      */   
32417     html : '',
32418     /**
32419      * @cfg {String} bgimage
32420      */   
32421     bgimage : '',
32422     /**
32423      * @cfg {String} videourl
32424      */   
32425     videourl : '',
32426     /**
32427      * @cfg {String} cls
32428      */   
32429     cls : '',
32430     /**
32431      * @cfg {String} href
32432      */   
32433     href : '',
32434     /**
32435      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32436      */   
32437     size : 'xs',
32438     
32439     /**
32440      * @cfg {String} placetitle (center|bottom)
32441      */   
32442     placetitle : '',
32443     
32444     /**
32445      * @cfg {Boolean} isFitContainer defalut true
32446      */   
32447     isFitContainer : true, 
32448     
32449     /**
32450      * @cfg {Boolean} preventDefault defalut false
32451      */   
32452     preventDefault : false, 
32453     
32454     /**
32455      * @cfg {Boolean} inverse defalut false
32456      */   
32457     maskInverse : false, 
32458     
32459     getAutoCreate : function()
32460     {
32461         if(!this.isFitContainer){
32462             return this.getSplitAutoCreate();
32463         }
32464         
32465         var cls = 'masonry-brick masonry-brick-full';
32466         
32467         if(this.href.length){
32468             cls += ' masonry-brick-link';
32469         }
32470         
32471         if(this.bgimage.length){
32472             cls += ' masonry-brick-image';
32473         }
32474         
32475         if(this.maskInverse){
32476             cls += ' mask-inverse';
32477         }
32478         
32479         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32480             cls += ' enable-mask';
32481         }
32482         
32483         if(this.size){
32484             cls += ' masonry-' + this.size + '-brick';
32485         }
32486         
32487         if(this.placetitle.length){
32488             
32489             switch (this.placetitle) {
32490                 case 'center' :
32491                     cls += ' masonry-center-title';
32492                     break;
32493                 case 'bottom' :
32494                     cls += ' masonry-bottom-title';
32495                     break;
32496                 default:
32497                     break;
32498             }
32499             
32500         } else {
32501             if(!this.html.length && !this.bgimage.length){
32502                 cls += ' masonry-center-title';
32503             }
32504
32505             if(!this.html.length && this.bgimage.length){
32506                 cls += ' masonry-bottom-title';
32507             }
32508         }
32509         
32510         if(this.cls){
32511             cls += ' ' + this.cls;
32512         }
32513         
32514         var cfg = {
32515             tag: (this.href.length) ? 'a' : 'div',
32516             cls: cls,
32517             cn: [
32518                 {
32519                     tag: 'div',
32520                     cls: 'masonry-brick-mask'
32521                 },
32522                 {
32523                     tag: 'div',
32524                     cls: 'masonry-brick-paragraph',
32525                     cn: []
32526                 }
32527             ]
32528         };
32529         
32530         if(this.href.length){
32531             cfg.href = this.href;
32532         }
32533         
32534         var cn = cfg.cn[1].cn;
32535         
32536         if(this.title.length){
32537             cn.push({
32538                 tag: 'h4',
32539                 cls: 'masonry-brick-title',
32540                 html: this.title
32541             });
32542         }
32543         
32544         if(this.html.length){
32545             cn.push({
32546                 tag: 'p',
32547                 cls: 'masonry-brick-text',
32548                 html: this.html
32549             });
32550         }
32551         
32552         if (!this.title.length && !this.html.length) {
32553             cfg.cn[1].cls += ' hide';
32554         }
32555         
32556         if(this.bgimage.length){
32557             cfg.cn.push({
32558                 tag: 'img',
32559                 cls: 'masonry-brick-image-view',
32560                 src: this.bgimage
32561             });
32562         }
32563         
32564         if(this.videourl.length){
32565             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32566             // youtube support only?
32567             cfg.cn.push({
32568                 tag: 'iframe',
32569                 cls: 'masonry-brick-image-view',
32570                 src: vurl,
32571                 frameborder : 0,
32572                 allowfullscreen : true
32573             });
32574         }
32575         
32576         return cfg;
32577         
32578     },
32579     
32580     getSplitAutoCreate : function()
32581     {
32582         var cls = 'masonry-brick masonry-brick-split';
32583         
32584         if(this.href.length){
32585             cls += ' masonry-brick-link';
32586         }
32587         
32588         if(this.bgimage.length){
32589             cls += ' masonry-brick-image';
32590         }
32591         
32592         if(this.size){
32593             cls += ' masonry-' + this.size + '-brick';
32594         }
32595         
32596         switch (this.placetitle) {
32597             case 'center' :
32598                 cls += ' masonry-center-title';
32599                 break;
32600             case 'bottom' :
32601                 cls += ' masonry-bottom-title';
32602                 break;
32603             default:
32604                 if(!this.bgimage.length){
32605                     cls += ' masonry-center-title';
32606                 }
32607
32608                 if(this.bgimage.length){
32609                     cls += ' masonry-bottom-title';
32610                 }
32611                 break;
32612         }
32613         
32614         if(this.cls){
32615             cls += ' ' + this.cls;
32616         }
32617         
32618         var cfg = {
32619             tag: (this.href.length) ? 'a' : 'div',
32620             cls: cls,
32621             cn: [
32622                 {
32623                     tag: 'div',
32624                     cls: 'masonry-brick-split-head',
32625                     cn: [
32626                         {
32627                             tag: 'div',
32628                             cls: 'masonry-brick-paragraph',
32629                             cn: []
32630                         }
32631                     ]
32632                 },
32633                 {
32634                     tag: 'div',
32635                     cls: 'masonry-brick-split-body',
32636                     cn: []
32637                 }
32638             ]
32639         };
32640         
32641         if(this.href.length){
32642             cfg.href = this.href;
32643         }
32644         
32645         if(this.title.length){
32646             cfg.cn[0].cn[0].cn.push({
32647                 tag: 'h4',
32648                 cls: 'masonry-brick-title',
32649                 html: this.title
32650             });
32651         }
32652         
32653         if(this.html.length){
32654             cfg.cn[1].cn.push({
32655                 tag: 'p',
32656                 cls: 'masonry-brick-text',
32657                 html: this.html
32658             });
32659         }
32660
32661         if(this.bgimage.length){
32662             cfg.cn[0].cn.push({
32663                 tag: 'img',
32664                 cls: 'masonry-brick-image-view',
32665                 src: this.bgimage
32666             });
32667         }
32668         
32669         if(this.videourl.length){
32670             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32671             // youtube support only?
32672             cfg.cn[0].cn.cn.push({
32673                 tag: 'iframe',
32674                 cls: 'masonry-brick-image-view',
32675                 src: vurl,
32676                 frameborder : 0,
32677                 allowfullscreen : true
32678             });
32679         }
32680         
32681         return cfg;
32682     },
32683     
32684     initEvents: function() 
32685     {
32686         switch (this.size) {
32687             case 'xs' :
32688                 this.x = 1;
32689                 this.y = 1;
32690                 break;
32691             case 'sm' :
32692                 this.x = 2;
32693                 this.y = 2;
32694                 break;
32695             case 'md' :
32696             case 'md-left' :
32697             case 'md-right' :
32698                 this.x = 3;
32699                 this.y = 3;
32700                 break;
32701             case 'tall' :
32702                 this.x = 2;
32703                 this.y = 3;
32704                 break;
32705             case 'wide' :
32706                 this.x = 3;
32707                 this.y = 2;
32708                 break;
32709             case 'wide-thin' :
32710                 this.x = 3;
32711                 this.y = 1;
32712                 break;
32713                         
32714             default :
32715                 break;
32716         }
32717         
32718         if(Roo.isTouch){
32719             this.el.on('touchstart', this.onTouchStart, this);
32720             this.el.on('touchmove', this.onTouchMove, this);
32721             this.el.on('touchend', this.onTouchEnd, this);
32722             this.el.on('contextmenu', this.onContextMenu, this);
32723         } else {
32724             this.el.on('mouseenter'  ,this.enter, this);
32725             this.el.on('mouseleave', this.leave, this);
32726             this.el.on('click', this.onClick, this);
32727         }
32728         
32729         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32730             this.parent().bricks.push(this);   
32731         }
32732         
32733     },
32734     
32735     onClick: function(e, el)
32736     {
32737         var time = this.endTimer - this.startTimer;
32738         // Roo.log(e.preventDefault());
32739         if(Roo.isTouch){
32740             if(time > 1000){
32741                 e.preventDefault();
32742                 return;
32743             }
32744         }
32745         
32746         if(!this.preventDefault){
32747             return;
32748         }
32749         
32750         e.preventDefault();
32751         
32752         if (this.activcClass != '') {
32753             this.selectBrick();
32754         }
32755         
32756         this.fireEvent('click', this);
32757     },
32758     
32759     enter: function(e, el)
32760     {
32761         e.preventDefault();
32762         
32763         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32764             return;
32765         }
32766         
32767         if(this.bgimage.length && this.html.length){
32768             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32769         }
32770     },
32771     
32772     leave: function(e, el)
32773     {
32774         e.preventDefault();
32775         
32776         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32777             return;
32778         }
32779         
32780         if(this.bgimage.length && this.html.length){
32781             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32782         }
32783     },
32784     
32785     onTouchStart: function(e, el)
32786     {
32787 //        e.preventDefault();
32788         
32789         this.touchmoved = false;
32790         
32791         if(!this.isFitContainer){
32792             return;
32793         }
32794         
32795         if(!this.bgimage.length || !this.html.length){
32796             return;
32797         }
32798         
32799         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32800         
32801         this.timer = new Date().getTime();
32802         
32803     },
32804     
32805     onTouchMove: function(e, el)
32806     {
32807         this.touchmoved = true;
32808     },
32809     
32810     onContextMenu : function(e,el)
32811     {
32812         e.preventDefault();
32813         e.stopPropagation();
32814         return false;
32815     },
32816     
32817     onTouchEnd: function(e, el)
32818     {
32819 //        e.preventDefault();
32820         
32821         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32822         
32823             this.leave(e,el);
32824             
32825             return;
32826         }
32827         
32828         if(!this.bgimage.length || !this.html.length){
32829             
32830             if(this.href.length){
32831                 window.location.href = this.href;
32832             }
32833             
32834             return;
32835         }
32836         
32837         if(!this.isFitContainer){
32838             return;
32839         }
32840         
32841         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32842         
32843         window.location.href = this.href;
32844     },
32845     
32846     //selection on single brick only
32847     selectBrick : function() {
32848         
32849         if (!this.parentId) {
32850             return;
32851         }
32852         
32853         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32854         var index = m.selectedBrick.indexOf(this.id);
32855         
32856         if ( index > -1) {
32857             m.selectedBrick.splice(index,1);
32858             this.el.removeClass(this.activeClass);
32859             return;
32860         }
32861         
32862         for(var i = 0; i < m.selectedBrick.length; i++) {
32863             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32864             b.el.removeClass(b.activeClass);
32865         }
32866         
32867         m.selectedBrick = [];
32868         
32869         m.selectedBrick.push(this.id);
32870         this.el.addClass(this.activeClass);
32871         return;
32872     }
32873     
32874 });
32875
32876 Roo.apply(Roo.bootstrap.MasonryBrick, {
32877     
32878     //groups: {},
32879     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32880      /**
32881     * register a Masonry Brick
32882     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32883     */
32884     
32885     register : function(brick)
32886     {
32887         //this.groups[brick.id] = brick;
32888         this.groups.add(brick.id, brick);
32889     },
32890     /**
32891     * fetch a  masonry brick based on the masonry brick ID
32892     * @param {string} the masonry brick to add
32893     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32894     */
32895     
32896     get: function(brick_id) 
32897     {
32898         // if (typeof(this.groups[brick_id]) == 'undefined') {
32899         //     return false;
32900         // }
32901         // return this.groups[brick_id] ;
32902         
32903         if(this.groups.key(brick_id)) {
32904             return this.groups.key(brick_id);
32905         }
32906         
32907         return false;
32908     }
32909     
32910     
32911     
32912 });
32913
32914  /*
32915  * - LGPL
32916  *
32917  * element
32918  * 
32919  */
32920
32921 /**
32922  * @class Roo.bootstrap.Brick
32923  * @extends Roo.bootstrap.Component
32924  * Bootstrap Brick class
32925  * 
32926  * @constructor
32927  * Create a new Brick
32928  * @param {Object} config The config object
32929  */
32930
32931 Roo.bootstrap.Brick = function(config){
32932     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32933     
32934     this.addEvents({
32935         // raw events
32936         /**
32937          * @event click
32938          * When a Brick is click
32939          * @param {Roo.bootstrap.Brick} this
32940          * @param {Roo.EventObject} e
32941          */
32942         "click" : true
32943     });
32944 };
32945
32946 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32947     
32948     /**
32949      * @cfg {String} title
32950      */   
32951     title : '',
32952     /**
32953      * @cfg {String} html
32954      */   
32955     html : '',
32956     /**
32957      * @cfg {String} bgimage
32958      */   
32959     bgimage : '',
32960     /**
32961      * @cfg {String} cls
32962      */   
32963     cls : '',
32964     /**
32965      * @cfg {String} href
32966      */   
32967     href : '',
32968     /**
32969      * @cfg {String} video
32970      */   
32971     video : '',
32972     /**
32973      * @cfg {Boolean} square
32974      */   
32975     square : true,
32976     
32977     getAutoCreate : function()
32978     {
32979         var cls = 'roo-brick';
32980         
32981         if(this.href.length){
32982             cls += ' roo-brick-link';
32983         }
32984         
32985         if(this.bgimage.length){
32986             cls += ' roo-brick-image';
32987         }
32988         
32989         if(!this.html.length && !this.bgimage.length){
32990             cls += ' roo-brick-center-title';
32991         }
32992         
32993         if(!this.html.length && this.bgimage.length){
32994             cls += ' roo-brick-bottom-title';
32995         }
32996         
32997         if(this.cls){
32998             cls += ' ' + this.cls;
32999         }
33000         
33001         var cfg = {
33002             tag: (this.href.length) ? 'a' : 'div',
33003             cls: cls,
33004             cn: [
33005                 {
33006                     tag: 'div',
33007                     cls: 'roo-brick-paragraph',
33008                     cn: []
33009                 }
33010             ]
33011         };
33012         
33013         if(this.href.length){
33014             cfg.href = this.href;
33015         }
33016         
33017         var cn = cfg.cn[0].cn;
33018         
33019         if(this.title.length){
33020             cn.push({
33021                 tag: 'h4',
33022                 cls: 'roo-brick-title',
33023                 html: this.title
33024             });
33025         }
33026         
33027         if(this.html.length){
33028             cn.push({
33029                 tag: 'p',
33030                 cls: 'roo-brick-text',
33031                 html: this.html
33032             });
33033         } else {
33034             cn.cls += ' hide';
33035         }
33036         
33037         if(this.bgimage.length){
33038             cfg.cn.push({
33039                 tag: 'img',
33040                 cls: 'roo-brick-image-view',
33041                 src: this.bgimage
33042             });
33043         }
33044         
33045         return cfg;
33046     },
33047     
33048     initEvents: function() 
33049     {
33050         if(this.title.length || this.html.length){
33051             this.el.on('mouseenter'  ,this.enter, this);
33052             this.el.on('mouseleave', this.leave, this);
33053         }
33054         
33055         Roo.EventManager.onWindowResize(this.resize, this); 
33056         
33057         if(this.bgimage.length){
33058             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33059             this.imageEl.on('load', this.onImageLoad, this);
33060             return;
33061         }
33062         
33063         this.resize();
33064     },
33065     
33066     onImageLoad : function()
33067     {
33068         this.resize();
33069     },
33070     
33071     resize : function()
33072     {
33073         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33074         
33075         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33076         
33077         if(this.bgimage.length){
33078             var image = this.el.select('.roo-brick-image-view', true).first();
33079             
33080             image.setWidth(paragraph.getWidth());
33081             
33082             if(this.square){
33083                 image.setHeight(paragraph.getWidth());
33084             }
33085             
33086             this.el.setHeight(image.getHeight());
33087             paragraph.setHeight(image.getHeight());
33088             
33089         }
33090         
33091     },
33092     
33093     enter: function(e, el)
33094     {
33095         e.preventDefault();
33096         
33097         if(this.bgimage.length){
33098             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33099             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33100         }
33101     },
33102     
33103     leave: function(e, el)
33104     {
33105         e.preventDefault();
33106         
33107         if(this.bgimage.length){
33108             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33109             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33110         }
33111     }
33112     
33113 });
33114
33115  
33116
33117  /*
33118  * - LGPL
33119  *
33120  * Number field 
33121  */
33122
33123 /**
33124  * @class Roo.bootstrap.NumberField
33125  * @extends Roo.bootstrap.Input
33126  * Bootstrap NumberField class
33127  * 
33128  * 
33129  * 
33130  * 
33131  * @constructor
33132  * Create a new NumberField
33133  * @param {Object} config The config object
33134  */
33135
33136 Roo.bootstrap.NumberField = function(config){
33137     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33138 };
33139
33140 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33141     
33142     /**
33143      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33144      */
33145     allowDecimals : true,
33146     /**
33147      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33148      */
33149     decimalSeparator : ".",
33150     /**
33151      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33152      */
33153     decimalPrecision : 2,
33154     /**
33155      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33156      */
33157     allowNegative : true,
33158     
33159     /**
33160      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33161      */
33162     allowZero: true,
33163     /**
33164      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33165      */
33166     minValue : Number.NEGATIVE_INFINITY,
33167     /**
33168      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33169      */
33170     maxValue : Number.MAX_VALUE,
33171     /**
33172      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33173      */
33174     minText : "The minimum value for this field is {0}",
33175     /**
33176      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33177      */
33178     maxText : "The maximum value for this field is {0}",
33179     /**
33180      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33181      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33182      */
33183     nanText : "{0} is not a valid number",
33184     /**
33185      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33186      */
33187     castInt : true,
33188     /**
33189      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33190      */
33191     thousandsDelimiter : false,
33192     /**
33193      * @cfg {String} valueAlign alignment of value
33194      */
33195     valueAlign : "left",
33196
33197     getAutoCreate : function()
33198     {
33199         var hiddenInput = {
33200             tag: 'input',
33201             type: 'hidden',
33202             id: Roo.id(),
33203             cls: 'hidden-number-input'
33204         };
33205         
33206         if (this.name) {
33207             hiddenInput.name = this.name;
33208         }
33209         
33210         this.name = '';
33211         
33212         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33213         
33214         this.name = hiddenInput.name;
33215         
33216         if(cfg.cn.length > 0) {
33217             cfg.cn.push(hiddenInput);
33218         }
33219         
33220         return cfg;
33221     },
33222
33223     // private
33224     initEvents : function()
33225     {   
33226         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33227         
33228         var allowed = "0123456789";
33229         
33230         if(this.allowDecimals){
33231             allowed += this.decimalSeparator;
33232         }
33233         
33234         if(this.allowNegative){
33235             allowed += "-";
33236         }
33237         
33238         if(this.thousandsDelimiter) {
33239             allowed += ",";
33240         }
33241         
33242         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33243         
33244         var keyPress = function(e){
33245             
33246             var k = e.getKey();
33247             
33248             var c = e.getCharCode();
33249             
33250             if(
33251                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33252                     allowed.indexOf(String.fromCharCode(c)) === -1
33253             ){
33254                 e.stopEvent();
33255                 return;
33256             }
33257             
33258             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33259                 return;
33260             }
33261             
33262             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33263                 e.stopEvent();
33264             }
33265         };
33266         
33267         this.el.on("keypress", keyPress, this);
33268     },
33269     
33270     validateValue : function(value)
33271     {
33272         
33273         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33274             return false;
33275         }
33276         
33277         var num = this.parseValue(value);
33278         
33279         if(isNaN(num)){
33280             this.markInvalid(String.format(this.nanText, value));
33281             return false;
33282         }
33283         
33284         if(num < this.minValue){
33285             this.markInvalid(String.format(this.minText, this.minValue));
33286             return false;
33287         }
33288         
33289         if(num > this.maxValue){
33290             this.markInvalid(String.format(this.maxText, this.maxValue));
33291             return false;
33292         }
33293         
33294         return true;
33295     },
33296
33297     getValue : function()
33298     {
33299         var v = this.hiddenEl().getValue();
33300         
33301         return this.fixPrecision(this.parseValue(v));
33302     },
33303
33304     parseValue : function(value)
33305     {
33306         if(this.thousandsDelimiter) {
33307             value += "";
33308             r = new RegExp(",", "g");
33309             value = value.replace(r, "");
33310         }
33311         
33312         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33313         return isNaN(value) ? '' : value;
33314     },
33315
33316     fixPrecision : function(value)
33317     {
33318         if(this.thousandsDelimiter) {
33319             value += "";
33320             r = new RegExp(",", "g");
33321             value = value.replace(r, "");
33322         }
33323         
33324         var nan = isNaN(value);
33325         
33326         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33327             return nan ? '' : value;
33328         }
33329         return parseFloat(value).toFixed(this.decimalPrecision);
33330     },
33331
33332     setValue : function(v)
33333     {
33334         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33335         
33336         this.value = v;
33337         
33338         if(this.rendered){
33339             
33340             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33341             
33342             this.inputEl().dom.value = (v == '') ? '' :
33343                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33344             
33345             if(!this.allowZero && v === '0') {
33346                 this.hiddenEl().dom.value = '';
33347                 this.inputEl().dom.value = '';
33348             }
33349             
33350             this.validate();
33351         }
33352     },
33353
33354     decimalPrecisionFcn : function(v)
33355     {
33356         return Math.floor(v);
33357     },
33358
33359     beforeBlur : function()
33360     {
33361         if(!this.castInt){
33362             return;
33363         }
33364         
33365         var v = this.parseValue(this.getRawValue());
33366         
33367         if(v || v === 0){
33368             this.setValue(v);
33369         }
33370     },
33371     
33372     hiddenEl : function()
33373     {
33374         return this.el.select('input.hidden-number-input',true).first();
33375     }
33376     
33377 });
33378
33379  
33380
33381 /*
33382 * Licence: LGPL
33383 */
33384
33385 /**
33386  * @class Roo.bootstrap.DocumentSlider
33387  * @extends Roo.bootstrap.Component
33388  * Bootstrap DocumentSlider class
33389  * 
33390  * @constructor
33391  * Create a new DocumentViewer
33392  * @param {Object} config The config object
33393  */
33394
33395 Roo.bootstrap.DocumentSlider = function(config){
33396     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33397     
33398     this.files = [];
33399     
33400     this.addEvents({
33401         /**
33402          * @event initial
33403          * Fire after initEvent
33404          * @param {Roo.bootstrap.DocumentSlider} this
33405          */
33406         "initial" : true,
33407         /**
33408          * @event update
33409          * Fire after update
33410          * @param {Roo.bootstrap.DocumentSlider} this
33411          */
33412         "update" : true,
33413         /**
33414          * @event click
33415          * Fire after click
33416          * @param {Roo.bootstrap.DocumentSlider} this
33417          */
33418         "click" : true
33419     });
33420 };
33421
33422 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33423     
33424     files : false,
33425     
33426     indicator : 0,
33427     
33428     getAutoCreate : function()
33429     {
33430         var cfg = {
33431             tag : 'div',
33432             cls : 'roo-document-slider',
33433             cn : [
33434                 {
33435                     tag : 'div',
33436                     cls : 'roo-document-slider-header',
33437                     cn : [
33438                         {
33439                             tag : 'div',
33440                             cls : 'roo-document-slider-header-title'
33441                         }
33442                     ]
33443                 },
33444                 {
33445                     tag : 'div',
33446                     cls : 'roo-document-slider-body',
33447                     cn : [
33448                         {
33449                             tag : 'div',
33450                             cls : 'roo-document-slider-prev',
33451                             cn : [
33452                                 {
33453                                     tag : 'i',
33454                                     cls : 'fa fa-chevron-left'
33455                                 }
33456                             ]
33457                         },
33458                         {
33459                             tag : 'div',
33460                             cls : 'roo-document-slider-thumb',
33461                             cn : [
33462                                 {
33463                                     tag : 'img',
33464                                     cls : 'roo-document-slider-image'
33465                                 }
33466                             ]
33467                         },
33468                         {
33469                             tag : 'div',
33470                             cls : 'roo-document-slider-next',
33471                             cn : [
33472                                 {
33473                                     tag : 'i',
33474                                     cls : 'fa fa-chevron-right'
33475                                 }
33476                             ]
33477                         }
33478                     ]
33479                 }
33480             ]
33481         };
33482         
33483         return cfg;
33484     },
33485     
33486     initEvents : function()
33487     {
33488         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33489         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33490         
33491         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33492         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33493         
33494         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33495         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33496         
33497         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33498         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33499         
33500         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33501         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33502         
33503         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33504         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33505         
33506         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33507         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33508         
33509         this.thumbEl.on('click', this.onClick, this);
33510         
33511         this.prevIndicator.on('click', this.prev, this);
33512         
33513         this.nextIndicator.on('click', this.next, this);
33514         
33515     },
33516     
33517     initial : function()
33518     {
33519         if(this.files.length){
33520             this.indicator = 1;
33521             this.update()
33522         }
33523         
33524         this.fireEvent('initial', this);
33525     },
33526     
33527     update : function()
33528     {
33529         this.imageEl.attr('src', this.files[this.indicator - 1]);
33530         
33531         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33532         
33533         this.prevIndicator.show();
33534         
33535         if(this.indicator == 1){
33536             this.prevIndicator.hide();
33537         }
33538         
33539         this.nextIndicator.show();
33540         
33541         if(this.indicator == this.files.length){
33542             this.nextIndicator.hide();
33543         }
33544         
33545         this.thumbEl.scrollTo('top');
33546         
33547         this.fireEvent('update', this);
33548     },
33549     
33550     onClick : function(e)
33551     {
33552         e.preventDefault();
33553         
33554         this.fireEvent('click', this);
33555     },
33556     
33557     prev : function(e)
33558     {
33559         e.preventDefault();
33560         
33561         this.indicator = Math.max(1, this.indicator - 1);
33562         
33563         this.update();
33564     },
33565     
33566     next : function(e)
33567     {
33568         e.preventDefault();
33569         
33570         this.indicator = Math.min(this.files.length, this.indicator + 1);
33571         
33572         this.update();
33573     }
33574 });
33575 /*
33576  * - LGPL
33577  *
33578  * RadioSet
33579  *
33580  *
33581  */
33582
33583 /**
33584  * @class Roo.bootstrap.RadioSet
33585  * @extends Roo.bootstrap.Input
33586  * Bootstrap RadioSet class
33587  * @cfg {String} indicatorpos (left|right) default left
33588  * @cfg {Boolean} inline (true|false) inline the element (default true)
33589  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33590  * @constructor
33591  * Create a new RadioSet
33592  * @param {Object} config The config object
33593  */
33594
33595 Roo.bootstrap.RadioSet = function(config){
33596     
33597     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33598     
33599     this.radioes = [];
33600     
33601     Roo.bootstrap.RadioSet.register(this);
33602     
33603     this.addEvents({
33604         /**
33605         * @event check
33606         * Fires when the element is checked or unchecked.
33607         * @param {Roo.bootstrap.RadioSet} this This radio
33608         * @param {Roo.bootstrap.Radio} item The checked item
33609         */
33610        check : true,
33611        /**
33612         * @event click
33613         * Fires when the element is click.
33614         * @param {Roo.bootstrap.RadioSet} this This radio set
33615         * @param {Roo.bootstrap.Radio} item The checked item
33616         * @param {Roo.EventObject} e The event object
33617         */
33618        click : true
33619     });
33620     
33621 };
33622
33623 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33624
33625     radioes : false,
33626     
33627     inline : true,
33628     
33629     weight : '',
33630     
33631     indicatorpos : 'left',
33632     
33633     getAutoCreate : function()
33634     {
33635         var label = {
33636             tag : 'label',
33637             cls : 'roo-radio-set-label',
33638             cn : [
33639                 {
33640                     tag : 'span',
33641                     html : this.fieldLabel
33642                 }
33643             ]
33644         };
33645         
33646         if(this.indicatorpos == 'left'){
33647             label.cn.unshift({
33648                 tag : 'i',
33649                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33650                 tooltip : 'This field is required'
33651             });
33652         } else {
33653             label.cn.push({
33654                 tag : 'i',
33655                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33656                 tooltip : 'This field is required'
33657             });
33658         }
33659         
33660         var items = {
33661             tag : 'div',
33662             cls : 'roo-radio-set-items'
33663         };
33664         
33665         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33666         
33667         if (align === 'left' && this.fieldLabel.length) {
33668             
33669             items = {
33670                 cls : "roo-radio-set-right", 
33671                 cn: [
33672                     items
33673                 ]
33674             };
33675             
33676             if(this.labelWidth > 12){
33677                 label.style = "width: " + this.labelWidth + 'px';
33678             }
33679             
33680             if(this.labelWidth < 13 && this.labelmd == 0){
33681                 this.labelmd = this.labelWidth;
33682             }
33683             
33684             if(this.labellg > 0){
33685                 label.cls += ' col-lg-' + this.labellg;
33686                 items.cls += ' col-lg-' + (12 - this.labellg);
33687             }
33688             
33689             if(this.labelmd > 0){
33690                 label.cls += ' col-md-' + this.labelmd;
33691                 items.cls += ' col-md-' + (12 - this.labelmd);
33692             }
33693             
33694             if(this.labelsm > 0){
33695                 label.cls += ' col-sm-' + this.labelsm;
33696                 items.cls += ' col-sm-' + (12 - this.labelsm);
33697             }
33698             
33699             if(this.labelxs > 0){
33700                 label.cls += ' col-xs-' + this.labelxs;
33701                 items.cls += ' col-xs-' + (12 - this.labelxs);
33702             }
33703         }
33704         
33705         var cfg = {
33706             tag : 'div',
33707             cls : 'roo-radio-set',
33708             cn : [
33709                 {
33710                     tag : 'input',
33711                     cls : 'roo-radio-set-input',
33712                     type : 'hidden',
33713                     name : this.name,
33714                     value : this.value ? this.value :  ''
33715                 },
33716                 label,
33717                 items
33718             ]
33719         };
33720         
33721         if(this.weight.length){
33722             cfg.cls += ' roo-radio-' + this.weight;
33723         }
33724         
33725         if(this.inline) {
33726             cfg.cls += ' roo-radio-set-inline';
33727         }
33728         
33729         var settings=this;
33730         ['xs','sm','md','lg'].map(function(size){
33731             if (settings[size]) {
33732                 cfg.cls += ' col-' + size + '-' + settings[size];
33733             }
33734         });
33735         
33736         return cfg;
33737         
33738     },
33739
33740     initEvents : function()
33741     {
33742         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33743         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33744         
33745         if(!this.fieldLabel.length){
33746             this.labelEl.hide();
33747         }
33748         
33749         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33750         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33751         
33752         this.indicatorEl().addClass('invisible');
33753         
33754         this.originalValue = this.getValue();
33755         
33756     },
33757     
33758     inputEl: function ()
33759     {
33760         return this.el.select('.roo-radio-set-input', true).first();
33761     },
33762     
33763     getChildContainer : function()
33764     {
33765         return this.itemsEl;
33766     },
33767     
33768     register : function(item)
33769     {
33770         this.radioes.push(item);
33771         
33772     },
33773     
33774     validate : function()
33775     {   
33776         if(this.getVisibilityEl().hasClass('hidden')){
33777             return true;
33778         }
33779         
33780         var valid = false;
33781         
33782         Roo.each(this.radioes, function(i){
33783             if(!i.checked){
33784                 return;
33785             }
33786             
33787             valid = true;
33788             return false;
33789         });
33790         
33791         if(this.allowBlank) {
33792             return true;
33793         }
33794         
33795         if(this.disabled || valid){
33796             this.markValid();
33797             return true;
33798         }
33799         
33800         this.markInvalid();
33801         return false;
33802         
33803     },
33804     
33805     markValid : function()
33806     {
33807         if(this.labelEl.isVisible(true)){
33808             this.indicatorEl().removeClass('visible');
33809             this.indicatorEl().addClass('invisible');
33810         }
33811         
33812         this.el.removeClass([this.invalidClass, this.validClass]);
33813         this.el.addClass(this.validClass);
33814         
33815         this.fireEvent('valid', this);
33816     },
33817     
33818     markInvalid : function(msg)
33819     {
33820         if(this.allowBlank || this.disabled){
33821             return;
33822         }
33823         
33824         if(this.labelEl.isVisible(true)){
33825             this.indicatorEl().removeClass('invisible');
33826             this.indicatorEl().addClass('visible');
33827         }
33828         
33829         this.el.removeClass([this.invalidClass, this.validClass]);
33830         this.el.addClass(this.invalidClass);
33831         
33832         this.fireEvent('invalid', this, msg);
33833         
33834     },
33835     
33836     setValue : function(v, suppressEvent)
33837     {   
33838         if(this.value === v){
33839             return;
33840         }
33841         
33842         this.value = v;
33843         
33844         if(this.rendered){
33845             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33846         }
33847         
33848         Roo.each(this.radioes, function(i){
33849             i.checked = false;
33850             i.el.removeClass('checked');
33851         });
33852         
33853         Roo.each(this.radioes, function(i){
33854             
33855             if(i.value === v || i.value.toString() === v.toString()){
33856                 i.checked = true;
33857                 i.el.addClass('checked');
33858                 
33859                 if(suppressEvent !== true){
33860                     this.fireEvent('check', this, i);
33861                 }
33862                 
33863                 return false;
33864             }
33865             
33866         }, this);
33867         
33868         this.validate();
33869     },
33870     
33871     clearInvalid : function(){
33872         
33873         if(!this.el || this.preventMark){
33874             return;
33875         }
33876         
33877         this.el.removeClass([this.invalidClass]);
33878         
33879         this.fireEvent('valid', this);
33880     }
33881     
33882 });
33883
33884 Roo.apply(Roo.bootstrap.RadioSet, {
33885     
33886     groups: {},
33887     
33888     register : function(set)
33889     {
33890         this.groups[set.name] = set;
33891     },
33892     
33893     get: function(name) 
33894     {
33895         if (typeof(this.groups[name]) == 'undefined') {
33896             return false;
33897         }
33898         
33899         return this.groups[name] ;
33900     }
33901     
33902 });
33903 /*
33904  * Based on:
33905  * Ext JS Library 1.1.1
33906  * Copyright(c) 2006-2007, Ext JS, LLC.
33907  *
33908  * Originally Released Under LGPL - original licence link has changed is not relivant.
33909  *
33910  * Fork - LGPL
33911  * <script type="text/javascript">
33912  */
33913
33914
33915 /**
33916  * @class Roo.bootstrap.SplitBar
33917  * @extends Roo.util.Observable
33918  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33919  * <br><br>
33920  * Usage:
33921  * <pre><code>
33922 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33923                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33924 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33925 split.minSize = 100;
33926 split.maxSize = 600;
33927 split.animate = true;
33928 split.on('moved', splitterMoved);
33929 </code></pre>
33930  * @constructor
33931  * Create a new SplitBar
33932  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33933  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33934  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33935  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33936                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33937                         position of the SplitBar).
33938  */
33939 Roo.bootstrap.SplitBar = function(cfg){
33940     
33941     /** @private */
33942     
33943     //{
33944     //  dragElement : elm
33945     //  resizingElement: el,
33946         // optional..
33947     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33948     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33949         // existingProxy ???
33950     //}
33951     
33952     this.el = Roo.get(cfg.dragElement, true);
33953     this.el.dom.unselectable = "on";
33954     /** @private */
33955     this.resizingEl = Roo.get(cfg.resizingElement, true);
33956
33957     /**
33958      * @private
33959      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33960      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33961      * @type Number
33962      */
33963     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33964     
33965     /**
33966      * The minimum size of the resizing element. (Defaults to 0)
33967      * @type Number
33968      */
33969     this.minSize = 0;
33970     
33971     /**
33972      * The maximum size of the resizing element. (Defaults to 2000)
33973      * @type Number
33974      */
33975     this.maxSize = 2000;
33976     
33977     /**
33978      * Whether to animate the transition to the new size
33979      * @type Boolean
33980      */
33981     this.animate = false;
33982     
33983     /**
33984      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33985      * @type Boolean
33986      */
33987     this.useShim = false;
33988     
33989     /** @private */
33990     this.shim = null;
33991     
33992     if(!cfg.existingProxy){
33993         /** @private */
33994         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33995     }else{
33996         this.proxy = Roo.get(cfg.existingProxy).dom;
33997     }
33998     /** @private */
33999     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34000     
34001     /** @private */
34002     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34003     
34004     /** @private */
34005     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34006     
34007     /** @private */
34008     this.dragSpecs = {};
34009     
34010     /**
34011      * @private The adapter to use to positon and resize elements
34012      */
34013     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34014     this.adapter.init(this);
34015     
34016     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34017         /** @private */
34018         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34019         this.el.addClass("roo-splitbar-h");
34020     }else{
34021         /** @private */
34022         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34023         this.el.addClass("roo-splitbar-v");
34024     }
34025     
34026     this.addEvents({
34027         /**
34028          * @event resize
34029          * Fires when the splitter is moved (alias for {@link #event-moved})
34030          * @param {Roo.bootstrap.SplitBar} this
34031          * @param {Number} newSize the new width or height
34032          */
34033         "resize" : true,
34034         /**
34035          * @event moved
34036          * Fires when the splitter is moved
34037          * @param {Roo.bootstrap.SplitBar} this
34038          * @param {Number} newSize the new width or height
34039          */
34040         "moved" : true,
34041         /**
34042          * @event beforeresize
34043          * Fires before the splitter is dragged
34044          * @param {Roo.bootstrap.SplitBar} this
34045          */
34046         "beforeresize" : true,
34047
34048         "beforeapply" : true
34049     });
34050
34051     Roo.util.Observable.call(this);
34052 };
34053
34054 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34055     onStartProxyDrag : function(x, y){
34056         this.fireEvent("beforeresize", this);
34057         if(!this.overlay){
34058             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34059             o.unselectable();
34060             o.enableDisplayMode("block");
34061             // all splitbars share the same overlay
34062             Roo.bootstrap.SplitBar.prototype.overlay = o;
34063         }
34064         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34065         this.overlay.show();
34066         Roo.get(this.proxy).setDisplayed("block");
34067         var size = this.adapter.getElementSize(this);
34068         this.activeMinSize = this.getMinimumSize();;
34069         this.activeMaxSize = this.getMaximumSize();;
34070         var c1 = size - this.activeMinSize;
34071         var c2 = Math.max(this.activeMaxSize - size, 0);
34072         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34073             this.dd.resetConstraints();
34074             this.dd.setXConstraint(
34075                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34076                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34077             );
34078             this.dd.setYConstraint(0, 0);
34079         }else{
34080             this.dd.resetConstraints();
34081             this.dd.setXConstraint(0, 0);
34082             this.dd.setYConstraint(
34083                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34084                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34085             );
34086          }
34087         this.dragSpecs.startSize = size;
34088         this.dragSpecs.startPoint = [x, y];
34089         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34090     },
34091     
34092     /** 
34093      * @private Called after the drag operation by the DDProxy
34094      */
34095     onEndProxyDrag : function(e){
34096         Roo.get(this.proxy).setDisplayed(false);
34097         var endPoint = Roo.lib.Event.getXY(e);
34098         if(this.overlay){
34099             this.overlay.hide();
34100         }
34101         var newSize;
34102         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34103             newSize = this.dragSpecs.startSize + 
34104                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34105                     endPoint[0] - this.dragSpecs.startPoint[0] :
34106                     this.dragSpecs.startPoint[0] - endPoint[0]
34107                 );
34108         }else{
34109             newSize = this.dragSpecs.startSize + 
34110                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34111                     endPoint[1] - this.dragSpecs.startPoint[1] :
34112                     this.dragSpecs.startPoint[1] - endPoint[1]
34113                 );
34114         }
34115         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34116         if(newSize != this.dragSpecs.startSize){
34117             if(this.fireEvent('beforeapply', this, newSize) !== false){
34118                 this.adapter.setElementSize(this, newSize);
34119                 this.fireEvent("moved", this, newSize);
34120                 this.fireEvent("resize", this, newSize);
34121             }
34122         }
34123     },
34124     
34125     /**
34126      * Get the adapter this SplitBar uses
34127      * @return The adapter object
34128      */
34129     getAdapter : function(){
34130         return this.adapter;
34131     },
34132     
34133     /**
34134      * Set the adapter this SplitBar uses
34135      * @param {Object} adapter A SplitBar adapter object
34136      */
34137     setAdapter : function(adapter){
34138         this.adapter = adapter;
34139         this.adapter.init(this);
34140     },
34141     
34142     /**
34143      * Gets the minimum size for the resizing element
34144      * @return {Number} The minimum size
34145      */
34146     getMinimumSize : function(){
34147         return this.minSize;
34148     },
34149     
34150     /**
34151      * Sets the minimum size for the resizing element
34152      * @param {Number} minSize The minimum size
34153      */
34154     setMinimumSize : function(minSize){
34155         this.minSize = minSize;
34156     },
34157     
34158     /**
34159      * Gets the maximum size for the resizing element
34160      * @return {Number} The maximum size
34161      */
34162     getMaximumSize : function(){
34163         return this.maxSize;
34164     },
34165     
34166     /**
34167      * Sets the maximum size for the resizing element
34168      * @param {Number} maxSize The maximum size
34169      */
34170     setMaximumSize : function(maxSize){
34171         this.maxSize = maxSize;
34172     },
34173     
34174     /**
34175      * Sets the initialize size for the resizing element
34176      * @param {Number} size The initial size
34177      */
34178     setCurrentSize : function(size){
34179         var oldAnimate = this.animate;
34180         this.animate = false;
34181         this.adapter.setElementSize(this, size);
34182         this.animate = oldAnimate;
34183     },
34184     
34185     /**
34186      * Destroy this splitbar. 
34187      * @param {Boolean} removeEl True to remove the element
34188      */
34189     destroy : function(removeEl){
34190         if(this.shim){
34191             this.shim.remove();
34192         }
34193         this.dd.unreg();
34194         this.proxy.parentNode.removeChild(this.proxy);
34195         if(removeEl){
34196             this.el.remove();
34197         }
34198     }
34199 });
34200
34201 /**
34202  * @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.
34203  */
34204 Roo.bootstrap.SplitBar.createProxy = function(dir){
34205     var proxy = new Roo.Element(document.createElement("div"));
34206     proxy.unselectable();
34207     var cls = 'roo-splitbar-proxy';
34208     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34209     document.body.appendChild(proxy.dom);
34210     return proxy.dom;
34211 };
34212
34213 /** 
34214  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34215  * Default Adapter. It assumes the splitter and resizing element are not positioned
34216  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34217  */
34218 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34219 };
34220
34221 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34222     // do nothing for now
34223     init : function(s){
34224     
34225     },
34226     /**
34227      * Called before drag operations to get the current size of the resizing element. 
34228      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34229      */
34230      getElementSize : function(s){
34231         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34232             return s.resizingEl.getWidth();
34233         }else{
34234             return s.resizingEl.getHeight();
34235         }
34236     },
34237     
34238     /**
34239      * Called after drag operations to set the size of the resizing element.
34240      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34241      * @param {Number} newSize The new size to set
34242      * @param {Function} onComplete A function to be invoked when resizing is complete
34243      */
34244     setElementSize : function(s, newSize, onComplete){
34245         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34246             if(!s.animate){
34247                 s.resizingEl.setWidth(newSize);
34248                 if(onComplete){
34249                     onComplete(s, newSize);
34250                 }
34251             }else{
34252                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34253             }
34254         }else{
34255             
34256             if(!s.animate){
34257                 s.resizingEl.setHeight(newSize);
34258                 if(onComplete){
34259                     onComplete(s, newSize);
34260                 }
34261             }else{
34262                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34263             }
34264         }
34265     }
34266 };
34267
34268 /** 
34269  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34270  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34271  * Adapter that  moves the splitter element to align with the resized sizing element. 
34272  * Used with an absolute positioned SplitBar.
34273  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34274  * document.body, make sure you assign an id to the body element.
34275  */
34276 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34277     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34278     this.container = Roo.get(container);
34279 };
34280
34281 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34282     init : function(s){
34283         this.basic.init(s);
34284     },
34285     
34286     getElementSize : function(s){
34287         return this.basic.getElementSize(s);
34288     },
34289     
34290     setElementSize : function(s, newSize, onComplete){
34291         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34292     },
34293     
34294     moveSplitter : function(s){
34295         var yes = Roo.bootstrap.SplitBar;
34296         switch(s.placement){
34297             case yes.LEFT:
34298                 s.el.setX(s.resizingEl.getRight());
34299                 break;
34300             case yes.RIGHT:
34301                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34302                 break;
34303             case yes.TOP:
34304                 s.el.setY(s.resizingEl.getBottom());
34305                 break;
34306             case yes.BOTTOM:
34307                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34308                 break;
34309         }
34310     }
34311 };
34312
34313 /**
34314  * Orientation constant - Create a vertical SplitBar
34315  * @static
34316  * @type Number
34317  */
34318 Roo.bootstrap.SplitBar.VERTICAL = 1;
34319
34320 /**
34321  * Orientation constant - Create a horizontal SplitBar
34322  * @static
34323  * @type Number
34324  */
34325 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34326
34327 /**
34328  * Placement constant - The resizing element is to the left of the splitter element
34329  * @static
34330  * @type Number
34331  */
34332 Roo.bootstrap.SplitBar.LEFT = 1;
34333
34334 /**
34335  * Placement constant - The resizing element is to the right of the splitter element
34336  * @static
34337  * @type Number
34338  */
34339 Roo.bootstrap.SplitBar.RIGHT = 2;
34340
34341 /**
34342  * Placement constant - The resizing element is positioned above the splitter element
34343  * @static
34344  * @type Number
34345  */
34346 Roo.bootstrap.SplitBar.TOP = 3;
34347
34348 /**
34349  * Placement constant - The resizing element is positioned under splitter element
34350  * @static
34351  * @type Number
34352  */
34353 Roo.bootstrap.SplitBar.BOTTOM = 4;
34354 Roo.namespace("Roo.bootstrap.layout");/*
34355  * Based on:
34356  * Ext JS Library 1.1.1
34357  * Copyright(c) 2006-2007, Ext JS, LLC.
34358  *
34359  * Originally Released Under LGPL - original licence link has changed is not relivant.
34360  *
34361  * Fork - LGPL
34362  * <script type="text/javascript">
34363  */
34364
34365 /**
34366  * @class Roo.bootstrap.layout.Manager
34367  * @extends Roo.bootstrap.Component
34368  * Base class for layout managers.
34369  */
34370 Roo.bootstrap.layout.Manager = function(config)
34371 {
34372     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34373
34374
34375
34376
34377
34378     /** false to disable window resize monitoring @type Boolean */
34379     this.monitorWindowResize = true;
34380     this.regions = {};
34381     this.addEvents({
34382         /**
34383          * @event layout
34384          * Fires when a layout is performed.
34385          * @param {Roo.LayoutManager} this
34386          */
34387         "layout" : true,
34388         /**
34389          * @event regionresized
34390          * Fires when the user resizes a region.
34391          * @param {Roo.LayoutRegion} region The resized region
34392          * @param {Number} newSize The new size (width for east/west, height for north/south)
34393          */
34394         "regionresized" : true,
34395         /**
34396          * @event regioncollapsed
34397          * Fires when a region is collapsed.
34398          * @param {Roo.LayoutRegion} region The collapsed region
34399          */
34400         "regioncollapsed" : true,
34401         /**
34402          * @event regionexpanded
34403          * Fires when a region is expanded.
34404          * @param {Roo.LayoutRegion} region The expanded region
34405          */
34406         "regionexpanded" : true
34407     });
34408     this.updating = false;
34409
34410     if (config.el) {
34411         this.el = Roo.get(config.el);
34412         this.initEvents();
34413     }
34414
34415 };
34416
34417 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34418
34419
34420     regions : null,
34421
34422     monitorWindowResize : true,
34423
34424
34425     updating : false,
34426
34427
34428     onRender : function(ct, position)
34429     {
34430         if(!this.el){
34431             this.el = Roo.get(ct);
34432             this.initEvents();
34433         }
34434         //this.fireEvent('render',this);
34435     },
34436
34437
34438     initEvents: function()
34439     {
34440
34441
34442         // ie scrollbar fix
34443         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34444             document.body.scroll = "no";
34445         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34446             this.el.position('relative');
34447         }
34448         this.id = this.el.id;
34449         this.el.addClass("roo-layout-container");
34450         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34451         if(this.el.dom != document.body ) {
34452             this.el.on('resize', this.layout,this);
34453             this.el.on('show', this.layout,this);
34454         }
34455
34456     },
34457
34458     /**
34459      * Returns true if this layout is currently being updated
34460      * @return {Boolean}
34461      */
34462     isUpdating : function(){
34463         return this.updating;
34464     },
34465
34466     /**
34467      * Suspend the LayoutManager from doing auto-layouts while
34468      * making multiple add or remove calls
34469      */
34470     beginUpdate : function(){
34471         this.updating = true;
34472     },
34473
34474     /**
34475      * Restore auto-layouts and optionally disable the manager from performing a layout
34476      * @param {Boolean} noLayout true to disable a layout update
34477      */
34478     endUpdate : function(noLayout){
34479         this.updating = false;
34480         if(!noLayout){
34481             this.layout();
34482         }
34483     },
34484
34485     layout: function(){
34486         // abstract...
34487     },
34488
34489     onRegionResized : function(region, newSize){
34490         this.fireEvent("regionresized", region, newSize);
34491         this.layout();
34492     },
34493
34494     onRegionCollapsed : function(region){
34495         this.fireEvent("regioncollapsed", region);
34496     },
34497
34498     onRegionExpanded : function(region){
34499         this.fireEvent("regionexpanded", region);
34500     },
34501
34502     /**
34503      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34504      * performs box-model adjustments.
34505      * @return {Object} The size as an object {width: (the width), height: (the height)}
34506      */
34507     getViewSize : function()
34508     {
34509         var size;
34510         if(this.el.dom != document.body){
34511             size = this.el.getSize();
34512         }else{
34513             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34514         }
34515         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34516         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34517         return size;
34518     },
34519
34520     /**
34521      * Returns the Element this layout is bound to.
34522      * @return {Roo.Element}
34523      */
34524     getEl : function(){
34525         return this.el;
34526     },
34527
34528     /**
34529      * Returns the specified region.
34530      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34531      * @return {Roo.LayoutRegion}
34532      */
34533     getRegion : function(target){
34534         return this.regions[target.toLowerCase()];
34535     },
34536
34537     onWindowResize : function(){
34538         if(this.monitorWindowResize){
34539             this.layout();
34540         }
34541     }
34542 });
34543 /*
34544  * Based on:
34545  * Ext JS Library 1.1.1
34546  * Copyright(c) 2006-2007, Ext JS, LLC.
34547  *
34548  * Originally Released Under LGPL - original licence link has changed is not relivant.
34549  *
34550  * Fork - LGPL
34551  * <script type="text/javascript">
34552  */
34553 /**
34554  * @class Roo.bootstrap.layout.Border
34555  * @extends Roo.bootstrap.layout.Manager
34556  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34557  * please see: examples/bootstrap/nested.html<br><br>
34558  
34559 <b>The container the layout is rendered into can be either the body element or any other element.
34560 If it is not the body element, the container needs to either be an absolute positioned element,
34561 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34562 the container size if it is not the body element.</b>
34563
34564 * @constructor
34565 * Create a new Border
34566 * @param {Object} config Configuration options
34567  */
34568 Roo.bootstrap.layout.Border = function(config){
34569     config = config || {};
34570     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34571     
34572     
34573     
34574     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34575         if(config[region]){
34576             config[region].region = region;
34577             this.addRegion(config[region]);
34578         }
34579     },this);
34580     
34581 };
34582
34583 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34584
34585 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34586     /**
34587      * Creates and adds a new region if it doesn't already exist.
34588      * @param {String} target The target region key (north, south, east, west or center).
34589      * @param {Object} config The regions config object
34590      * @return {BorderLayoutRegion} The new region
34591      */
34592     addRegion : function(config)
34593     {
34594         if(!this.regions[config.region]){
34595             var r = this.factory(config);
34596             this.bindRegion(r);
34597         }
34598         return this.regions[config.region];
34599     },
34600
34601     // private (kinda)
34602     bindRegion : function(r){
34603         this.regions[r.config.region] = r;
34604         
34605         r.on("visibilitychange",    this.layout, this);
34606         r.on("paneladded",          this.layout, this);
34607         r.on("panelremoved",        this.layout, this);
34608         r.on("invalidated",         this.layout, this);
34609         r.on("resized",             this.onRegionResized, this);
34610         r.on("collapsed",           this.onRegionCollapsed, this);
34611         r.on("expanded",            this.onRegionExpanded, this);
34612     },
34613
34614     /**
34615      * Performs a layout update.
34616      */
34617     layout : function()
34618     {
34619         if(this.updating) {
34620             return;
34621         }
34622         
34623         // render all the rebions if they have not been done alreayd?
34624         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34625             if(this.regions[region] && !this.regions[region].bodyEl){
34626                 this.regions[region].onRender(this.el)
34627             }
34628         },this);
34629         
34630         var size = this.getViewSize();
34631         var w = size.width;
34632         var h = size.height;
34633         var centerW = w;
34634         var centerH = h;
34635         var centerY = 0;
34636         var centerX = 0;
34637         //var x = 0, y = 0;
34638
34639         var rs = this.regions;
34640         var north = rs["north"];
34641         var south = rs["south"]; 
34642         var west = rs["west"];
34643         var east = rs["east"];
34644         var center = rs["center"];
34645         //if(this.hideOnLayout){ // not supported anymore
34646             //c.el.setStyle("display", "none");
34647         //}
34648         if(north && north.isVisible()){
34649             var b = north.getBox();
34650             var m = north.getMargins();
34651             b.width = w - (m.left+m.right);
34652             b.x = m.left;
34653             b.y = m.top;
34654             centerY = b.height + b.y + m.bottom;
34655             centerH -= centerY;
34656             north.updateBox(this.safeBox(b));
34657         }
34658         if(south && south.isVisible()){
34659             var b = south.getBox();
34660             var m = south.getMargins();
34661             b.width = w - (m.left+m.right);
34662             b.x = m.left;
34663             var totalHeight = (b.height + m.top + m.bottom);
34664             b.y = h - totalHeight + m.top;
34665             centerH -= totalHeight;
34666             south.updateBox(this.safeBox(b));
34667         }
34668         if(west && west.isVisible()){
34669             var b = west.getBox();
34670             var m = west.getMargins();
34671             b.height = centerH - (m.top+m.bottom);
34672             b.x = m.left;
34673             b.y = centerY + m.top;
34674             var totalWidth = (b.width + m.left + m.right);
34675             centerX += totalWidth;
34676             centerW -= totalWidth;
34677             west.updateBox(this.safeBox(b));
34678         }
34679         if(east && east.isVisible()){
34680             var b = east.getBox();
34681             var m = east.getMargins();
34682             b.height = centerH - (m.top+m.bottom);
34683             var totalWidth = (b.width + m.left + m.right);
34684             b.x = w - totalWidth + m.left;
34685             b.y = centerY + m.top;
34686             centerW -= totalWidth;
34687             east.updateBox(this.safeBox(b));
34688         }
34689         if(center){
34690             var m = center.getMargins();
34691             var centerBox = {
34692                 x: centerX + m.left,
34693                 y: centerY + m.top,
34694                 width: centerW - (m.left+m.right),
34695                 height: centerH - (m.top+m.bottom)
34696             };
34697             //if(this.hideOnLayout){
34698                 //center.el.setStyle("display", "block");
34699             //}
34700             center.updateBox(this.safeBox(centerBox));
34701         }
34702         this.el.repaint();
34703         this.fireEvent("layout", this);
34704     },
34705
34706     // private
34707     safeBox : function(box){
34708         box.width = Math.max(0, box.width);
34709         box.height = Math.max(0, box.height);
34710         return box;
34711     },
34712
34713     /**
34714      * Adds a ContentPanel (or subclass) to this layout.
34715      * @param {String} target The target region key (north, south, east, west or center).
34716      * @param {Roo.ContentPanel} panel The panel to add
34717      * @return {Roo.ContentPanel} The added panel
34718      */
34719     add : function(target, panel){
34720          
34721         target = target.toLowerCase();
34722         return this.regions[target].add(panel);
34723     },
34724
34725     /**
34726      * Remove a ContentPanel (or subclass) to this layout.
34727      * @param {String} target The target region key (north, south, east, west or center).
34728      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34729      * @return {Roo.ContentPanel} The removed panel
34730      */
34731     remove : function(target, panel){
34732         target = target.toLowerCase();
34733         return this.regions[target].remove(panel);
34734     },
34735
34736     /**
34737      * Searches all regions for a panel with the specified id
34738      * @param {String} panelId
34739      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34740      */
34741     findPanel : function(panelId){
34742         var rs = this.regions;
34743         for(var target in rs){
34744             if(typeof rs[target] != "function"){
34745                 var p = rs[target].getPanel(panelId);
34746                 if(p){
34747                     return p;
34748                 }
34749             }
34750         }
34751         return null;
34752     },
34753
34754     /**
34755      * Searches all regions for a panel with the specified id and activates (shows) it.
34756      * @param {String/ContentPanel} panelId The panels id or the panel itself
34757      * @return {Roo.ContentPanel} The shown panel or null
34758      */
34759     showPanel : function(panelId) {
34760       var rs = this.regions;
34761       for(var target in rs){
34762          var r = rs[target];
34763          if(typeof r != "function"){
34764             if(r.hasPanel(panelId)){
34765                return r.showPanel(panelId);
34766             }
34767          }
34768       }
34769       return null;
34770    },
34771
34772    /**
34773      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34774      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34775      */
34776    /*
34777     restoreState : function(provider){
34778         if(!provider){
34779             provider = Roo.state.Manager;
34780         }
34781         var sm = new Roo.LayoutStateManager();
34782         sm.init(this, provider);
34783     },
34784 */
34785  
34786  
34787     /**
34788      * Adds a xtype elements to the layout.
34789      * <pre><code>
34790
34791 layout.addxtype({
34792        xtype : 'ContentPanel',
34793        region: 'west',
34794        items: [ .... ]
34795    }
34796 );
34797
34798 layout.addxtype({
34799         xtype : 'NestedLayoutPanel',
34800         region: 'west',
34801         layout: {
34802            center: { },
34803            west: { }   
34804         },
34805         items : [ ... list of content panels or nested layout panels.. ]
34806    }
34807 );
34808 </code></pre>
34809      * @param {Object} cfg Xtype definition of item to add.
34810      */
34811     addxtype : function(cfg)
34812     {
34813         // basically accepts a pannel...
34814         // can accept a layout region..!?!?
34815         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34816         
34817         
34818         // theory?  children can only be panels??
34819         
34820         //if (!cfg.xtype.match(/Panel$/)) {
34821         //    return false;
34822         //}
34823         var ret = false;
34824         
34825         if (typeof(cfg.region) == 'undefined') {
34826             Roo.log("Failed to add Panel, region was not set");
34827             Roo.log(cfg);
34828             return false;
34829         }
34830         var region = cfg.region;
34831         delete cfg.region;
34832         
34833           
34834         var xitems = [];
34835         if (cfg.items) {
34836             xitems = cfg.items;
34837             delete cfg.items;
34838         }
34839         var nb = false;
34840         
34841         switch(cfg.xtype) 
34842         {
34843             case 'Content':  // ContentPanel (el, cfg)
34844             case 'Scroll':  // ContentPanel (el, cfg)
34845             case 'View': 
34846                 cfg.autoCreate = true;
34847                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34848                 //} else {
34849                 //    var el = this.el.createChild();
34850                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34851                 //}
34852                 
34853                 this.add(region, ret);
34854                 break;
34855             
34856             /*
34857             case 'TreePanel': // our new panel!
34858                 cfg.el = this.el.createChild();
34859                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34860                 this.add(region, ret);
34861                 break;
34862             */
34863             
34864             case 'Nest': 
34865                 // create a new Layout (which is  a Border Layout...
34866                 
34867                 var clayout = cfg.layout;
34868                 clayout.el  = this.el.createChild();
34869                 clayout.items   = clayout.items  || [];
34870                 
34871                 delete cfg.layout;
34872                 
34873                 // replace this exitems with the clayout ones..
34874                 xitems = clayout.items;
34875                  
34876                 // force background off if it's in center...
34877                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34878                     cfg.background = false;
34879                 }
34880                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34881                 
34882                 
34883                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34884                 //console.log('adding nested layout panel '  + cfg.toSource());
34885                 this.add(region, ret);
34886                 nb = {}; /// find first...
34887                 break;
34888             
34889             case 'Grid':
34890                 
34891                 // needs grid and region
34892                 
34893                 //var el = this.getRegion(region).el.createChild();
34894                 /*
34895                  *var el = this.el.createChild();
34896                 // create the grid first...
34897                 cfg.grid.container = el;
34898                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34899                 */
34900                 
34901                 if (region == 'center' && this.active ) {
34902                     cfg.background = false;
34903                 }
34904                 
34905                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34906                 
34907                 this.add(region, ret);
34908                 /*
34909                 if (cfg.background) {
34910                     // render grid on panel activation (if panel background)
34911                     ret.on('activate', function(gp) {
34912                         if (!gp.grid.rendered) {
34913                     //        gp.grid.render(el);
34914                         }
34915                     });
34916                 } else {
34917                   //  cfg.grid.render(el);
34918                 }
34919                 */
34920                 break;
34921            
34922            
34923             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34924                 // it was the old xcomponent building that caused this before.
34925                 // espeically if border is the top element in the tree.
34926                 ret = this;
34927                 break; 
34928                 
34929                     
34930                 
34931                 
34932                 
34933             default:
34934                 /*
34935                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34936                     
34937                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34938                     this.add(region, ret);
34939                 } else {
34940                 */
34941                     Roo.log(cfg);
34942                     throw "Can not add '" + cfg.xtype + "' to Border";
34943                     return null;
34944              
34945                                 
34946              
34947         }
34948         this.beginUpdate();
34949         // add children..
34950         var region = '';
34951         var abn = {};
34952         Roo.each(xitems, function(i)  {
34953             region = nb && i.region ? i.region : false;
34954             
34955             var add = ret.addxtype(i);
34956            
34957             if (region) {
34958                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34959                 if (!i.background) {
34960                     abn[region] = nb[region] ;
34961                 }
34962             }
34963             
34964         });
34965         this.endUpdate();
34966
34967         // make the last non-background panel active..
34968         //if (nb) { Roo.log(abn); }
34969         if (nb) {
34970             
34971             for(var r in abn) {
34972                 region = this.getRegion(r);
34973                 if (region) {
34974                     // tried using nb[r], but it does not work..
34975                      
34976                     region.showPanel(abn[r]);
34977                    
34978                 }
34979             }
34980         }
34981         return ret;
34982         
34983     },
34984     
34985     
34986 // private
34987     factory : function(cfg)
34988     {
34989         
34990         var validRegions = Roo.bootstrap.layout.Border.regions;
34991
34992         var target = cfg.region;
34993         cfg.mgr = this;
34994         
34995         var r = Roo.bootstrap.layout;
34996         Roo.log(target);
34997         switch(target){
34998             case "north":
34999                 return new r.North(cfg);
35000             case "south":
35001                 return new r.South(cfg);
35002             case "east":
35003                 return new r.East(cfg);
35004             case "west":
35005                 return new r.West(cfg);
35006             case "center":
35007                 return new r.Center(cfg);
35008         }
35009         throw 'Layout region "'+target+'" not supported.';
35010     }
35011     
35012     
35013 });
35014  /*
35015  * Based on:
35016  * Ext JS Library 1.1.1
35017  * Copyright(c) 2006-2007, Ext JS, LLC.
35018  *
35019  * Originally Released Under LGPL - original licence link has changed is not relivant.
35020  *
35021  * Fork - LGPL
35022  * <script type="text/javascript">
35023  */
35024  
35025 /**
35026  * @class Roo.bootstrap.layout.Basic
35027  * @extends Roo.util.Observable
35028  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35029  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35030  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35031  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35032  * @cfg {string}   region  the region that it inhabits..
35033  * @cfg {bool}   skipConfig skip config?
35034  * 
35035
35036  */
35037 Roo.bootstrap.layout.Basic = function(config){
35038     
35039     this.mgr = config.mgr;
35040     
35041     this.position = config.region;
35042     
35043     var skipConfig = config.skipConfig;
35044     
35045     this.events = {
35046         /**
35047          * @scope Roo.BasicLayoutRegion
35048          */
35049         
35050         /**
35051          * @event beforeremove
35052          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35053          * @param {Roo.LayoutRegion} this
35054          * @param {Roo.ContentPanel} panel The panel
35055          * @param {Object} e The cancel event object
35056          */
35057         "beforeremove" : true,
35058         /**
35059          * @event invalidated
35060          * Fires when the layout for this region is changed.
35061          * @param {Roo.LayoutRegion} this
35062          */
35063         "invalidated" : true,
35064         /**
35065          * @event visibilitychange
35066          * Fires when this region is shown or hidden 
35067          * @param {Roo.LayoutRegion} this
35068          * @param {Boolean} visibility true or false
35069          */
35070         "visibilitychange" : true,
35071         /**
35072          * @event paneladded
35073          * Fires when a panel is added. 
35074          * @param {Roo.LayoutRegion} this
35075          * @param {Roo.ContentPanel} panel The panel
35076          */
35077         "paneladded" : true,
35078         /**
35079          * @event panelremoved
35080          * Fires when a panel is removed. 
35081          * @param {Roo.LayoutRegion} this
35082          * @param {Roo.ContentPanel} panel The panel
35083          */
35084         "panelremoved" : true,
35085         /**
35086          * @event beforecollapse
35087          * Fires when this region before collapse.
35088          * @param {Roo.LayoutRegion} this
35089          */
35090         "beforecollapse" : true,
35091         /**
35092          * @event collapsed
35093          * Fires when this region is collapsed.
35094          * @param {Roo.LayoutRegion} this
35095          */
35096         "collapsed" : true,
35097         /**
35098          * @event expanded
35099          * Fires when this region is expanded.
35100          * @param {Roo.LayoutRegion} this
35101          */
35102         "expanded" : true,
35103         /**
35104          * @event slideshow
35105          * Fires when this region is slid into view.
35106          * @param {Roo.LayoutRegion} this
35107          */
35108         "slideshow" : true,
35109         /**
35110          * @event slidehide
35111          * Fires when this region slides out of view. 
35112          * @param {Roo.LayoutRegion} this
35113          */
35114         "slidehide" : true,
35115         /**
35116          * @event panelactivated
35117          * Fires when a panel is activated. 
35118          * @param {Roo.LayoutRegion} this
35119          * @param {Roo.ContentPanel} panel The activated panel
35120          */
35121         "panelactivated" : true,
35122         /**
35123          * @event resized
35124          * Fires when the user resizes this region. 
35125          * @param {Roo.LayoutRegion} this
35126          * @param {Number} newSize The new size (width for east/west, height for north/south)
35127          */
35128         "resized" : true
35129     };
35130     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35131     this.panels = new Roo.util.MixedCollection();
35132     this.panels.getKey = this.getPanelId.createDelegate(this);
35133     this.box = null;
35134     this.activePanel = null;
35135     // ensure listeners are added...
35136     
35137     if (config.listeners || config.events) {
35138         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35139             listeners : config.listeners || {},
35140             events : config.events || {}
35141         });
35142     }
35143     
35144     if(skipConfig !== true){
35145         this.applyConfig(config);
35146     }
35147 };
35148
35149 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35150 {
35151     getPanelId : function(p){
35152         return p.getId();
35153     },
35154     
35155     applyConfig : function(config){
35156         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35157         this.config = config;
35158         
35159     },
35160     
35161     /**
35162      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35163      * the width, for horizontal (north, south) the height.
35164      * @param {Number} newSize The new width or height
35165      */
35166     resizeTo : function(newSize){
35167         var el = this.el ? this.el :
35168                  (this.activePanel ? this.activePanel.getEl() : null);
35169         if(el){
35170             switch(this.position){
35171                 case "east":
35172                 case "west":
35173                     el.setWidth(newSize);
35174                     this.fireEvent("resized", this, newSize);
35175                 break;
35176                 case "north":
35177                 case "south":
35178                     el.setHeight(newSize);
35179                     this.fireEvent("resized", this, newSize);
35180                 break;                
35181             }
35182         }
35183     },
35184     
35185     getBox : function(){
35186         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35187     },
35188     
35189     getMargins : function(){
35190         return this.margins;
35191     },
35192     
35193     updateBox : function(box){
35194         this.box = box;
35195         var el = this.activePanel.getEl();
35196         el.dom.style.left = box.x + "px";
35197         el.dom.style.top = box.y + "px";
35198         this.activePanel.setSize(box.width, box.height);
35199     },
35200     
35201     /**
35202      * Returns the container element for this region.
35203      * @return {Roo.Element}
35204      */
35205     getEl : function(){
35206         return this.activePanel;
35207     },
35208     
35209     /**
35210      * Returns true if this region is currently visible.
35211      * @return {Boolean}
35212      */
35213     isVisible : function(){
35214         return this.activePanel ? true : false;
35215     },
35216     
35217     setActivePanel : function(panel){
35218         panel = this.getPanel(panel);
35219         if(this.activePanel && this.activePanel != panel){
35220             this.activePanel.setActiveState(false);
35221             this.activePanel.getEl().setLeftTop(-10000,-10000);
35222         }
35223         this.activePanel = panel;
35224         panel.setActiveState(true);
35225         if(this.box){
35226             panel.setSize(this.box.width, this.box.height);
35227         }
35228         this.fireEvent("panelactivated", this, panel);
35229         this.fireEvent("invalidated");
35230     },
35231     
35232     /**
35233      * Show the specified panel.
35234      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35235      * @return {Roo.ContentPanel} The shown panel or null
35236      */
35237     showPanel : function(panel){
35238         panel = this.getPanel(panel);
35239         if(panel){
35240             this.setActivePanel(panel);
35241         }
35242         return panel;
35243     },
35244     
35245     /**
35246      * Get the active panel for this region.
35247      * @return {Roo.ContentPanel} The active panel or null
35248      */
35249     getActivePanel : function(){
35250         return this.activePanel;
35251     },
35252     
35253     /**
35254      * Add the passed ContentPanel(s)
35255      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35256      * @return {Roo.ContentPanel} The panel added (if only one was added)
35257      */
35258     add : function(panel){
35259         if(arguments.length > 1){
35260             for(var i = 0, len = arguments.length; i < len; i++) {
35261                 this.add(arguments[i]);
35262             }
35263             return null;
35264         }
35265         if(this.hasPanel(panel)){
35266             this.showPanel(panel);
35267             return panel;
35268         }
35269         var el = panel.getEl();
35270         if(el.dom.parentNode != this.mgr.el.dom){
35271             this.mgr.el.dom.appendChild(el.dom);
35272         }
35273         if(panel.setRegion){
35274             panel.setRegion(this);
35275         }
35276         this.panels.add(panel);
35277         el.setStyle("position", "absolute");
35278         if(!panel.background){
35279             this.setActivePanel(panel);
35280             if(this.config.initialSize && this.panels.getCount()==1){
35281                 this.resizeTo(this.config.initialSize);
35282             }
35283         }
35284         this.fireEvent("paneladded", this, panel);
35285         return panel;
35286     },
35287     
35288     /**
35289      * Returns true if the panel is in this region.
35290      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35291      * @return {Boolean}
35292      */
35293     hasPanel : function(panel){
35294         if(typeof panel == "object"){ // must be panel obj
35295             panel = panel.getId();
35296         }
35297         return this.getPanel(panel) ? true : false;
35298     },
35299     
35300     /**
35301      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35302      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35303      * @param {Boolean} preservePanel Overrides the config preservePanel option
35304      * @return {Roo.ContentPanel} The panel that was removed
35305      */
35306     remove : function(panel, preservePanel){
35307         panel = this.getPanel(panel);
35308         if(!panel){
35309             return null;
35310         }
35311         var e = {};
35312         this.fireEvent("beforeremove", this, panel, e);
35313         if(e.cancel === true){
35314             return null;
35315         }
35316         var panelId = panel.getId();
35317         this.panels.removeKey(panelId);
35318         return panel;
35319     },
35320     
35321     /**
35322      * Returns the panel specified or null if it's not in this region.
35323      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35324      * @return {Roo.ContentPanel}
35325      */
35326     getPanel : function(id){
35327         if(typeof id == "object"){ // must be panel obj
35328             return id;
35329         }
35330         return this.panels.get(id);
35331     },
35332     
35333     /**
35334      * Returns this regions position (north/south/east/west/center).
35335      * @return {String} 
35336      */
35337     getPosition: function(){
35338         return this.position;    
35339     }
35340 });/*
35341  * Based on:
35342  * Ext JS Library 1.1.1
35343  * Copyright(c) 2006-2007, Ext JS, LLC.
35344  *
35345  * Originally Released Under LGPL - original licence link has changed is not relivant.
35346  *
35347  * Fork - LGPL
35348  * <script type="text/javascript">
35349  */
35350  
35351 /**
35352  * @class Roo.bootstrap.layout.Region
35353  * @extends Roo.bootstrap.layout.Basic
35354  * This class represents a region in a layout manager.
35355  
35356  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35357  * @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})
35358  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35359  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35360  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35361  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35362  * @cfg {String}    title           The title for the region (overrides panel titles)
35363  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35364  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35365  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35366  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35367  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35368  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35369  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35370  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35371  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35372  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35373
35374  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35375  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35376  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35377  * @cfg {Number}    width           For East/West panels
35378  * @cfg {Number}    height          For North/South panels
35379  * @cfg {Boolean}   split           To show the splitter
35380  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35381  * 
35382  * @cfg {string}   cls             Extra CSS classes to add to region
35383  * 
35384  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35385  * @cfg {string}   region  the region that it inhabits..
35386  *
35387
35388  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35389  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35390
35391  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35392  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35393  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35394  */
35395 Roo.bootstrap.layout.Region = function(config)
35396 {
35397     this.applyConfig(config);
35398
35399     var mgr = config.mgr;
35400     var pos = config.region;
35401     config.skipConfig = true;
35402     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35403     
35404     if (mgr.el) {
35405         this.onRender(mgr.el);   
35406     }
35407      
35408     this.visible = true;
35409     this.collapsed = false;
35410     this.unrendered_panels = [];
35411 };
35412
35413 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35414
35415     position: '', // set by wrapper (eg. north/south etc..)
35416     unrendered_panels : null,  // unrendered panels.
35417     createBody : function(){
35418         /** This region's body element 
35419         * @type Roo.Element */
35420         this.bodyEl = this.el.createChild({
35421                 tag: "div",
35422                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35423         });
35424     },
35425
35426     onRender: function(ctr, pos)
35427     {
35428         var dh = Roo.DomHelper;
35429         /** This region's container element 
35430         * @type Roo.Element */
35431         this.el = dh.append(ctr.dom, {
35432                 tag: "div",
35433                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35434             }, true);
35435         /** This region's title element 
35436         * @type Roo.Element */
35437     
35438         this.titleEl = dh.append(this.el.dom,
35439             {
35440                     tag: "div",
35441                     unselectable: "on",
35442                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35443                     children:[
35444                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35445                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35446                     ]}, true);
35447         
35448         this.titleEl.enableDisplayMode();
35449         /** This region's title text element 
35450         * @type HTMLElement */
35451         this.titleTextEl = this.titleEl.dom.firstChild;
35452         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35453         /*
35454         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35455         this.closeBtn.enableDisplayMode();
35456         this.closeBtn.on("click", this.closeClicked, this);
35457         this.closeBtn.hide();
35458     */
35459         this.createBody(this.config);
35460         if(this.config.hideWhenEmpty){
35461             this.hide();
35462             this.on("paneladded", this.validateVisibility, this);
35463             this.on("panelremoved", this.validateVisibility, this);
35464         }
35465         if(this.autoScroll){
35466             this.bodyEl.setStyle("overflow", "auto");
35467         }else{
35468             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35469         }
35470         //if(c.titlebar !== false){
35471             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35472                 this.titleEl.hide();
35473             }else{
35474                 this.titleEl.show();
35475                 if(this.config.title){
35476                     this.titleTextEl.innerHTML = this.config.title;
35477                 }
35478             }
35479         //}
35480         if(this.config.collapsed){
35481             this.collapse(true);
35482         }
35483         if(this.config.hidden){
35484             this.hide();
35485         }
35486         
35487         if (this.unrendered_panels && this.unrendered_panels.length) {
35488             for (var i =0;i< this.unrendered_panels.length; i++) {
35489                 this.add(this.unrendered_panels[i]);
35490             }
35491             this.unrendered_panels = null;
35492             
35493         }
35494         
35495     },
35496     
35497     applyConfig : function(c)
35498     {
35499         /*
35500          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35501             var dh = Roo.DomHelper;
35502             if(c.titlebar !== false){
35503                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35504                 this.collapseBtn.on("click", this.collapse, this);
35505                 this.collapseBtn.enableDisplayMode();
35506                 /*
35507                 if(c.showPin === true || this.showPin){
35508                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35509                     this.stickBtn.enableDisplayMode();
35510                     this.stickBtn.on("click", this.expand, this);
35511                     this.stickBtn.hide();
35512                 }
35513                 
35514             }
35515             */
35516             /** This region's collapsed element
35517             * @type Roo.Element */
35518             /*
35519              *
35520             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35521                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35522             ]}, true);
35523             
35524             if(c.floatable !== false){
35525                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35526                this.collapsedEl.on("click", this.collapseClick, this);
35527             }
35528
35529             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35530                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35531                    id: "message", unselectable: "on", style:{"float":"left"}});
35532                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35533              }
35534             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35535             this.expandBtn.on("click", this.expand, this);
35536             
35537         }
35538         
35539         if(this.collapseBtn){
35540             this.collapseBtn.setVisible(c.collapsible == true);
35541         }
35542         
35543         this.cmargins = c.cmargins || this.cmargins ||
35544                          (this.position == "west" || this.position == "east" ?
35545                              {top: 0, left: 2, right:2, bottom: 0} :
35546                              {top: 2, left: 0, right:0, bottom: 2});
35547         */
35548         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35549         
35550         
35551         this.bottomTabs = c.tabPosition != "top";
35552         
35553         this.autoScroll = c.autoScroll || false;
35554         
35555         
35556        
35557         
35558         this.duration = c.duration || .30;
35559         this.slideDuration = c.slideDuration || .45;
35560         this.config = c;
35561        
35562     },
35563     /**
35564      * Returns true if this region is currently visible.
35565      * @return {Boolean}
35566      */
35567     isVisible : function(){
35568         return this.visible;
35569     },
35570
35571     /**
35572      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35573      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35574      */
35575     //setCollapsedTitle : function(title){
35576     //    title = title || "&#160;";
35577      //   if(this.collapsedTitleTextEl){
35578       //      this.collapsedTitleTextEl.innerHTML = title;
35579        // }
35580     //},
35581
35582     getBox : function(){
35583         var b;
35584       //  if(!this.collapsed){
35585             b = this.el.getBox(false, true);
35586        // }else{
35587           //  b = this.collapsedEl.getBox(false, true);
35588         //}
35589         return b;
35590     },
35591
35592     getMargins : function(){
35593         return this.margins;
35594         //return this.collapsed ? this.cmargins : this.margins;
35595     },
35596 /*
35597     highlight : function(){
35598         this.el.addClass("x-layout-panel-dragover");
35599     },
35600
35601     unhighlight : function(){
35602         this.el.removeClass("x-layout-panel-dragover");
35603     },
35604 */
35605     updateBox : function(box)
35606     {
35607         if (!this.bodyEl) {
35608             return; // not rendered yet..
35609         }
35610         
35611         this.box = box;
35612         if(!this.collapsed){
35613             this.el.dom.style.left = box.x + "px";
35614             this.el.dom.style.top = box.y + "px";
35615             this.updateBody(box.width, box.height);
35616         }else{
35617             this.collapsedEl.dom.style.left = box.x + "px";
35618             this.collapsedEl.dom.style.top = box.y + "px";
35619             this.collapsedEl.setSize(box.width, box.height);
35620         }
35621         if(this.tabs){
35622             this.tabs.autoSizeTabs();
35623         }
35624     },
35625
35626     updateBody : function(w, h)
35627     {
35628         if(w !== null){
35629             this.el.setWidth(w);
35630             w -= this.el.getBorderWidth("rl");
35631             if(this.config.adjustments){
35632                 w += this.config.adjustments[0];
35633             }
35634         }
35635         if(h !== null && h > 0){
35636             this.el.setHeight(h);
35637             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35638             h -= this.el.getBorderWidth("tb");
35639             if(this.config.adjustments){
35640                 h += this.config.adjustments[1];
35641             }
35642             this.bodyEl.setHeight(h);
35643             if(this.tabs){
35644                 h = this.tabs.syncHeight(h);
35645             }
35646         }
35647         if(this.panelSize){
35648             w = w !== null ? w : this.panelSize.width;
35649             h = h !== null ? h : this.panelSize.height;
35650         }
35651         if(this.activePanel){
35652             var el = this.activePanel.getEl();
35653             w = w !== null ? w : el.getWidth();
35654             h = h !== null ? h : el.getHeight();
35655             this.panelSize = {width: w, height: h};
35656             this.activePanel.setSize(w, h);
35657         }
35658         if(Roo.isIE && this.tabs){
35659             this.tabs.el.repaint();
35660         }
35661     },
35662
35663     /**
35664      * Returns the container element for this region.
35665      * @return {Roo.Element}
35666      */
35667     getEl : function(){
35668         return this.el;
35669     },
35670
35671     /**
35672      * Hides this region.
35673      */
35674     hide : function(){
35675         //if(!this.collapsed){
35676             this.el.dom.style.left = "-2000px";
35677             this.el.hide();
35678         //}else{
35679          //   this.collapsedEl.dom.style.left = "-2000px";
35680          //   this.collapsedEl.hide();
35681        // }
35682         this.visible = false;
35683         this.fireEvent("visibilitychange", this, false);
35684     },
35685
35686     /**
35687      * Shows this region if it was previously hidden.
35688      */
35689     show : function(){
35690         //if(!this.collapsed){
35691             this.el.show();
35692         //}else{
35693         //    this.collapsedEl.show();
35694        // }
35695         this.visible = true;
35696         this.fireEvent("visibilitychange", this, true);
35697     },
35698 /*
35699     closeClicked : function(){
35700         if(this.activePanel){
35701             this.remove(this.activePanel);
35702         }
35703     },
35704
35705     collapseClick : function(e){
35706         if(this.isSlid){
35707            e.stopPropagation();
35708            this.slideIn();
35709         }else{
35710            e.stopPropagation();
35711            this.slideOut();
35712         }
35713     },
35714 */
35715     /**
35716      * Collapses this region.
35717      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35718      */
35719     /*
35720     collapse : function(skipAnim, skipCheck = false){
35721         if(this.collapsed) {
35722             return;
35723         }
35724         
35725         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35726             
35727             this.collapsed = true;
35728             if(this.split){
35729                 this.split.el.hide();
35730             }
35731             if(this.config.animate && skipAnim !== true){
35732                 this.fireEvent("invalidated", this);
35733                 this.animateCollapse();
35734             }else{
35735                 this.el.setLocation(-20000,-20000);
35736                 this.el.hide();
35737                 this.collapsedEl.show();
35738                 this.fireEvent("collapsed", this);
35739                 this.fireEvent("invalidated", this);
35740             }
35741         }
35742         
35743     },
35744 */
35745     animateCollapse : function(){
35746         // overridden
35747     },
35748
35749     /**
35750      * Expands this region if it was previously collapsed.
35751      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35752      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35753      */
35754     /*
35755     expand : function(e, skipAnim){
35756         if(e) {
35757             e.stopPropagation();
35758         }
35759         if(!this.collapsed || this.el.hasActiveFx()) {
35760             return;
35761         }
35762         if(this.isSlid){
35763             this.afterSlideIn();
35764             skipAnim = true;
35765         }
35766         this.collapsed = false;
35767         if(this.config.animate && skipAnim !== true){
35768             this.animateExpand();
35769         }else{
35770             this.el.show();
35771             if(this.split){
35772                 this.split.el.show();
35773             }
35774             this.collapsedEl.setLocation(-2000,-2000);
35775             this.collapsedEl.hide();
35776             this.fireEvent("invalidated", this);
35777             this.fireEvent("expanded", this);
35778         }
35779     },
35780 */
35781     animateExpand : function(){
35782         // overridden
35783     },
35784
35785     initTabs : function()
35786     {
35787         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35788         
35789         var ts = new Roo.bootstrap.panel.Tabs({
35790                 el: this.bodyEl.dom,
35791                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35792                 disableTooltips: this.config.disableTabTips,
35793                 toolbar : this.config.toolbar
35794             });
35795         
35796         if(this.config.hideTabs){
35797             ts.stripWrap.setDisplayed(false);
35798         }
35799         this.tabs = ts;
35800         ts.resizeTabs = this.config.resizeTabs === true;
35801         ts.minTabWidth = this.config.minTabWidth || 40;
35802         ts.maxTabWidth = this.config.maxTabWidth || 250;
35803         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35804         ts.monitorResize = false;
35805         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35806         ts.bodyEl.addClass('roo-layout-tabs-body');
35807         this.panels.each(this.initPanelAsTab, this);
35808     },
35809
35810     initPanelAsTab : function(panel){
35811         var ti = this.tabs.addTab(
35812             panel.getEl().id,
35813             panel.getTitle(),
35814             null,
35815             this.config.closeOnTab && panel.isClosable(),
35816             panel.tpl
35817         );
35818         if(panel.tabTip !== undefined){
35819             ti.setTooltip(panel.tabTip);
35820         }
35821         ti.on("activate", function(){
35822               this.setActivePanel(panel);
35823         }, this);
35824         
35825         if(this.config.closeOnTab){
35826             ti.on("beforeclose", function(t, e){
35827                 e.cancel = true;
35828                 this.remove(panel);
35829             }, this);
35830         }
35831         
35832         panel.tabItem = ti;
35833         
35834         return ti;
35835     },
35836
35837     updatePanelTitle : function(panel, title)
35838     {
35839         if(this.activePanel == panel){
35840             this.updateTitle(title);
35841         }
35842         if(this.tabs){
35843             var ti = this.tabs.getTab(panel.getEl().id);
35844             ti.setText(title);
35845             if(panel.tabTip !== undefined){
35846                 ti.setTooltip(panel.tabTip);
35847             }
35848         }
35849     },
35850
35851     updateTitle : function(title){
35852         if(this.titleTextEl && !this.config.title){
35853             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35854         }
35855     },
35856
35857     setActivePanel : function(panel)
35858     {
35859         panel = this.getPanel(panel);
35860         if(this.activePanel && this.activePanel != panel){
35861             if(this.activePanel.setActiveState(false) === false){
35862                 return;
35863             }
35864         }
35865         this.activePanel = panel;
35866         panel.setActiveState(true);
35867         if(this.panelSize){
35868             panel.setSize(this.panelSize.width, this.panelSize.height);
35869         }
35870         if(this.closeBtn){
35871             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35872         }
35873         this.updateTitle(panel.getTitle());
35874         if(this.tabs){
35875             this.fireEvent("invalidated", this);
35876         }
35877         this.fireEvent("panelactivated", this, panel);
35878     },
35879
35880     /**
35881      * Shows the specified panel.
35882      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35883      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35884      */
35885     showPanel : function(panel)
35886     {
35887         panel = this.getPanel(panel);
35888         if(panel){
35889             if(this.tabs){
35890                 var tab = this.tabs.getTab(panel.getEl().id);
35891                 if(tab.isHidden()){
35892                     this.tabs.unhideTab(tab.id);
35893                 }
35894                 tab.activate();
35895             }else{
35896                 this.setActivePanel(panel);
35897             }
35898         }
35899         return panel;
35900     },
35901
35902     /**
35903      * Get the active panel for this region.
35904      * @return {Roo.ContentPanel} The active panel or null
35905      */
35906     getActivePanel : function(){
35907         return this.activePanel;
35908     },
35909
35910     validateVisibility : function(){
35911         if(this.panels.getCount() < 1){
35912             this.updateTitle("&#160;");
35913             this.closeBtn.hide();
35914             this.hide();
35915         }else{
35916             if(!this.isVisible()){
35917                 this.show();
35918             }
35919         }
35920     },
35921
35922     /**
35923      * Adds the passed ContentPanel(s) to this region.
35924      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35925      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35926      */
35927     add : function(panel)
35928     {
35929         if(arguments.length > 1){
35930             for(var i = 0, len = arguments.length; i < len; i++) {
35931                 this.add(arguments[i]);
35932             }
35933             return null;
35934         }
35935         
35936         // if we have not been rendered yet, then we can not really do much of this..
35937         if (!this.bodyEl) {
35938             this.unrendered_panels.push(panel);
35939             return panel;
35940         }
35941         
35942         
35943         
35944         
35945         if(this.hasPanel(panel)){
35946             this.showPanel(panel);
35947             return panel;
35948         }
35949         panel.setRegion(this);
35950         this.panels.add(panel);
35951        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35952             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35953             // and hide them... ???
35954             this.bodyEl.dom.appendChild(panel.getEl().dom);
35955             if(panel.background !== true){
35956                 this.setActivePanel(panel);
35957             }
35958             this.fireEvent("paneladded", this, panel);
35959             return panel;
35960         }
35961         */
35962         if(!this.tabs){
35963             this.initTabs();
35964         }else{
35965             this.initPanelAsTab(panel);
35966         }
35967         
35968         
35969         if(panel.background !== true){
35970             this.tabs.activate(panel.getEl().id);
35971         }
35972         this.fireEvent("paneladded", this, panel);
35973         return panel;
35974     },
35975
35976     /**
35977      * Hides the tab for the specified panel.
35978      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35979      */
35980     hidePanel : function(panel){
35981         if(this.tabs && (panel = this.getPanel(panel))){
35982             this.tabs.hideTab(panel.getEl().id);
35983         }
35984     },
35985
35986     /**
35987      * Unhides the tab for a previously hidden panel.
35988      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35989      */
35990     unhidePanel : function(panel){
35991         if(this.tabs && (panel = this.getPanel(panel))){
35992             this.tabs.unhideTab(panel.getEl().id);
35993         }
35994     },
35995
35996     clearPanels : function(){
35997         while(this.panels.getCount() > 0){
35998              this.remove(this.panels.first());
35999         }
36000     },
36001
36002     /**
36003      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36004      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36005      * @param {Boolean} preservePanel Overrides the config preservePanel option
36006      * @return {Roo.ContentPanel} The panel that was removed
36007      */
36008     remove : function(panel, preservePanel)
36009     {
36010         panel = this.getPanel(panel);
36011         if(!panel){
36012             return null;
36013         }
36014         var e = {};
36015         this.fireEvent("beforeremove", this, panel, e);
36016         if(e.cancel === true){
36017             return null;
36018         }
36019         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36020         var panelId = panel.getId();
36021         this.panels.removeKey(panelId);
36022         if(preservePanel){
36023             document.body.appendChild(panel.getEl().dom);
36024         }
36025         if(this.tabs){
36026             this.tabs.removeTab(panel.getEl().id);
36027         }else if (!preservePanel){
36028             this.bodyEl.dom.removeChild(panel.getEl().dom);
36029         }
36030         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36031             var p = this.panels.first();
36032             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36033             tempEl.appendChild(p.getEl().dom);
36034             this.bodyEl.update("");
36035             this.bodyEl.dom.appendChild(p.getEl().dom);
36036             tempEl = null;
36037             this.updateTitle(p.getTitle());
36038             this.tabs = null;
36039             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36040             this.setActivePanel(p);
36041         }
36042         panel.setRegion(null);
36043         if(this.activePanel == panel){
36044             this.activePanel = null;
36045         }
36046         if(this.config.autoDestroy !== false && preservePanel !== true){
36047             try{panel.destroy();}catch(e){}
36048         }
36049         this.fireEvent("panelremoved", this, panel);
36050         return panel;
36051     },
36052
36053     /**
36054      * Returns the TabPanel component used by this region
36055      * @return {Roo.TabPanel}
36056      */
36057     getTabs : function(){
36058         return this.tabs;
36059     },
36060
36061     createTool : function(parentEl, className){
36062         var btn = Roo.DomHelper.append(parentEl, {
36063             tag: "div",
36064             cls: "x-layout-tools-button",
36065             children: [ {
36066                 tag: "div",
36067                 cls: "roo-layout-tools-button-inner " + className,
36068                 html: "&#160;"
36069             }]
36070         }, true);
36071         btn.addClassOnOver("roo-layout-tools-button-over");
36072         return btn;
36073     }
36074 });/*
36075  * Based on:
36076  * Ext JS Library 1.1.1
36077  * Copyright(c) 2006-2007, Ext JS, LLC.
36078  *
36079  * Originally Released Under LGPL - original licence link has changed is not relivant.
36080  *
36081  * Fork - LGPL
36082  * <script type="text/javascript">
36083  */
36084  
36085
36086
36087 /**
36088  * @class Roo.SplitLayoutRegion
36089  * @extends Roo.LayoutRegion
36090  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36091  */
36092 Roo.bootstrap.layout.Split = function(config){
36093     this.cursor = config.cursor;
36094     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36095 };
36096
36097 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36098 {
36099     splitTip : "Drag to resize.",
36100     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36101     useSplitTips : false,
36102
36103     applyConfig : function(config){
36104         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36105     },
36106     
36107     onRender : function(ctr,pos) {
36108         
36109         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36110         if(!this.config.split){
36111             return;
36112         }
36113         if(!this.split){
36114             
36115             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36116                             tag: "div",
36117                             id: this.el.id + "-split",
36118                             cls: "roo-layout-split roo-layout-split-"+this.position,
36119                             html: "&#160;"
36120             });
36121             /** The SplitBar for this region 
36122             * @type Roo.SplitBar */
36123             // does not exist yet...
36124             Roo.log([this.position, this.orientation]);
36125             
36126             this.split = new Roo.bootstrap.SplitBar({
36127                 dragElement : splitEl,
36128                 resizingElement: this.el,
36129                 orientation : this.orientation
36130             });
36131             
36132             this.split.on("moved", this.onSplitMove, this);
36133             this.split.useShim = this.config.useShim === true;
36134             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36135             if(this.useSplitTips){
36136                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36137             }
36138             //if(config.collapsible){
36139             //    this.split.el.on("dblclick", this.collapse,  this);
36140             //}
36141         }
36142         if(typeof this.config.minSize != "undefined"){
36143             this.split.minSize = this.config.minSize;
36144         }
36145         if(typeof this.config.maxSize != "undefined"){
36146             this.split.maxSize = this.config.maxSize;
36147         }
36148         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36149             this.hideSplitter();
36150         }
36151         
36152     },
36153
36154     getHMaxSize : function(){
36155          var cmax = this.config.maxSize || 10000;
36156          var center = this.mgr.getRegion("center");
36157          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36158     },
36159
36160     getVMaxSize : function(){
36161          var cmax = this.config.maxSize || 10000;
36162          var center = this.mgr.getRegion("center");
36163          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36164     },
36165
36166     onSplitMove : function(split, newSize){
36167         this.fireEvent("resized", this, newSize);
36168     },
36169     
36170     /** 
36171      * Returns the {@link Roo.SplitBar} for this region.
36172      * @return {Roo.SplitBar}
36173      */
36174     getSplitBar : function(){
36175         return this.split;
36176     },
36177     
36178     hide : function(){
36179         this.hideSplitter();
36180         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36181     },
36182
36183     hideSplitter : function(){
36184         if(this.split){
36185             this.split.el.setLocation(-2000,-2000);
36186             this.split.el.hide();
36187         }
36188     },
36189
36190     show : function(){
36191         if(this.split){
36192             this.split.el.show();
36193         }
36194         Roo.bootstrap.layout.Split.superclass.show.call(this);
36195     },
36196     
36197     beforeSlide: function(){
36198         if(Roo.isGecko){// firefox overflow auto bug workaround
36199             this.bodyEl.clip();
36200             if(this.tabs) {
36201                 this.tabs.bodyEl.clip();
36202             }
36203             if(this.activePanel){
36204                 this.activePanel.getEl().clip();
36205                 
36206                 if(this.activePanel.beforeSlide){
36207                     this.activePanel.beforeSlide();
36208                 }
36209             }
36210         }
36211     },
36212     
36213     afterSlide : function(){
36214         if(Roo.isGecko){// firefox overflow auto bug workaround
36215             this.bodyEl.unclip();
36216             if(this.tabs) {
36217                 this.tabs.bodyEl.unclip();
36218             }
36219             if(this.activePanel){
36220                 this.activePanel.getEl().unclip();
36221                 if(this.activePanel.afterSlide){
36222                     this.activePanel.afterSlide();
36223                 }
36224             }
36225         }
36226     },
36227
36228     initAutoHide : function(){
36229         if(this.autoHide !== false){
36230             if(!this.autoHideHd){
36231                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36232                 this.autoHideHd = {
36233                     "mouseout": function(e){
36234                         if(!e.within(this.el, true)){
36235                             st.delay(500);
36236                         }
36237                     },
36238                     "mouseover" : function(e){
36239                         st.cancel();
36240                     },
36241                     scope : this
36242                 };
36243             }
36244             this.el.on(this.autoHideHd);
36245         }
36246     },
36247
36248     clearAutoHide : function(){
36249         if(this.autoHide !== false){
36250             this.el.un("mouseout", this.autoHideHd.mouseout);
36251             this.el.un("mouseover", this.autoHideHd.mouseover);
36252         }
36253     },
36254
36255     clearMonitor : function(){
36256         Roo.get(document).un("click", this.slideInIf, this);
36257     },
36258
36259     // these names are backwards but not changed for compat
36260     slideOut : function(){
36261         if(this.isSlid || this.el.hasActiveFx()){
36262             return;
36263         }
36264         this.isSlid = true;
36265         if(this.collapseBtn){
36266             this.collapseBtn.hide();
36267         }
36268         this.closeBtnState = this.closeBtn.getStyle('display');
36269         this.closeBtn.hide();
36270         if(this.stickBtn){
36271             this.stickBtn.show();
36272         }
36273         this.el.show();
36274         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36275         this.beforeSlide();
36276         this.el.setStyle("z-index", 10001);
36277         this.el.slideIn(this.getSlideAnchor(), {
36278             callback: function(){
36279                 this.afterSlide();
36280                 this.initAutoHide();
36281                 Roo.get(document).on("click", this.slideInIf, this);
36282                 this.fireEvent("slideshow", this);
36283             },
36284             scope: this,
36285             block: true
36286         });
36287     },
36288
36289     afterSlideIn : function(){
36290         this.clearAutoHide();
36291         this.isSlid = false;
36292         this.clearMonitor();
36293         this.el.setStyle("z-index", "");
36294         if(this.collapseBtn){
36295             this.collapseBtn.show();
36296         }
36297         this.closeBtn.setStyle('display', this.closeBtnState);
36298         if(this.stickBtn){
36299             this.stickBtn.hide();
36300         }
36301         this.fireEvent("slidehide", this);
36302     },
36303
36304     slideIn : function(cb){
36305         if(!this.isSlid || this.el.hasActiveFx()){
36306             Roo.callback(cb);
36307             return;
36308         }
36309         this.isSlid = false;
36310         this.beforeSlide();
36311         this.el.slideOut(this.getSlideAnchor(), {
36312             callback: function(){
36313                 this.el.setLeftTop(-10000, -10000);
36314                 this.afterSlide();
36315                 this.afterSlideIn();
36316                 Roo.callback(cb);
36317             },
36318             scope: this,
36319             block: true
36320         });
36321     },
36322     
36323     slideInIf : function(e){
36324         if(!e.within(this.el)){
36325             this.slideIn();
36326         }
36327     },
36328
36329     animateCollapse : function(){
36330         this.beforeSlide();
36331         this.el.setStyle("z-index", 20000);
36332         var anchor = this.getSlideAnchor();
36333         this.el.slideOut(anchor, {
36334             callback : function(){
36335                 this.el.setStyle("z-index", "");
36336                 this.collapsedEl.slideIn(anchor, {duration:.3});
36337                 this.afterSlide();
36338                 this.el.setLocation(-10000,-10000);
36339                 this.el.hide();
36340                 this.fireEvent("collapsed", this);
36341             },
36342             scope: this,
36343             block: true
36344         });
36345     },
36346
36347     animateExpand : function(){
36348         this.beforeSlide();
36349         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36350         this.el.setStyle("z-index", 20000);
36351         this.collapsedEl.hide({
36352             duration:.1
36353         });
36354         this.el.slideIn(this.getSlideAnchor(), {
36355             callback : function(){
36356                 this.el.setStyle("z-index", "");
36357                 this.afterSlide();
36358                 if(this.split){
36359                     this.split.el.show();
36360                 }
36361                 this.fireEvent("invalidated", this);
36362                 this.fireEvent("expanded", this);
36363             },
36364             scope: this,
36365             block: true
36366         });
36367     },
36368
36369     anchors : {
36370         "west" : "left",
36371         "east" : "right",
36372         "north" : "top",
36373         "south" : "bottom"
36374     },
36375
36376     sanchors : {
36377         "west" : "l",
36378         "east" : "r",
36379         "north" : "t",
36380         "south" : "b"
36381     },
36382
36383     canchors : {
36384         "west" : "tl-tr",
36385         "east" : "tr-tl",
36386         "north" : "tl-bl",
36387         "south" : "bl-tl"
36388     },
36389
36390     getAnchor : function(){
36391         return this.anchors[this.position];
36392     },
36393
36394     getCollapseAnchor : function(){
36395         return this.canchors[this.position];
36396     },
36397
36398     getSlideAnchor : function(){
36399         return this.sanchors[this.position];
36400     },
36401
36402     getAlignAdj : function(){
36403         var cm = this.cmargins;
36404         switch(this.position){
36405             case "west":
36406                 return [0, 0];
36407             break;
36408             case "east":
36409                 return [0, 0];
36410             break;
36411             case "north":
36412                 return [0, 0];
36413             break;
36414             case "south":
36415                 return [0, 0];
36416             break;
36417         }
36418     },
36419
36420     getExpandAdj : function(){
36421         var c = this.collapsedEl, cm = this.cmargins;
36422         switch(this.position){
36423             case "west":
36424                 return [-(cm.right+c.getWidth()+cm.left), 0];
36425             break;
36426             case "east":
36427                 return [cm.right+c.getWidth()+cm.left, 0];
36428             break;
36429             case "north":
36430                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36431             break;
36432             case "south":
36433                 return [0, cm.top+cm.bottom+c.getHeight()];
36434             break;
36435         }
36436     }
36437 });/*
36438  * Based on:
36439  * Ext JS Library 1.1.1
36440  * Copyright(c) 2006-2007, Ext JS, LLC.
36441  *
36442  * Originally Released Under LGPL - original licence link has changed is not relivant.
36443  *
36444  * Fork - LGPL
36445  * <script type="text/javascript">
36446  */
36447 /*
36448  * These classes are private internal classes
36449  */
36450 Roo.bootstrap.layout.Center = function(config){
36451     config.region = "center";
36452     Roo.bootstrap.layout.Region.call(this, config);
36453     this.visible = true;
36454     this.minWidth = config.minWidth || 20;
36455     this.minHeight = config.minHeight || 20;
36456 };
36457
36458 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36459     hide : function(){
36460         // center panel can't be hidden
36461     },
36462     
36463     show : function(){
36464         // center panel can't be hidden
36465     },
36466     
36467     getMinWidth: function(){
36468         return this.minWidth;
36469     },
36470     
36471     getMinHeight: function(){
36472         return this.minHeight;
36473     }
36474 });
36475
36476
36477
36478
36479  
36480
36481
36482
36483
36484
36485 Roo.bootstrap.layout.North = function(config)
36486 {
36487     config.region = 'north';
36488     config.cursor = 'n-resize';
36489     
36490     Roo.bootstrap.layout.Split.call(this, config);
36491     
36492     
36493     if(this.split){
36494         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36495         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36496         this.split.el.addClass("roo-layout-split-v");
36497     }
36498     var size = config.initialSize || config.height;
36499     if(typeof size != "undefined"){
36500         this.el.setHeight(size);
36501     }
36502 };
36503 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36504 {
36505     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36506     
36507     
36508     
36509     getBox : function(){
36510         if(this.collapsed){
36511             return this.collapsedEl.getBox();
36512         }
36513         var box = this.el.getBox();
36514         if(this.split){
36515             box.height += this.split.el.getHeight();
36516         }
36517         return box;
36518     },
36519     
36520     updateBox : function(box){
36521         if(this.split && !this.collapsed){
36522             box.height -= this.split.el.getHeight();
36523             this.split.el.setLeft(box.x);
36524             this.split.el.setTop(box.y+box.height);
36525             this.split.el.setWidth(box.width);
36526         }
36527         if(this.collapsed){
36528             this.updateBody(box.width, null);
36529         }
36530         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36531     }
36532 });
36533
36534
36535
36536
36537
36538 Roo.bootstrap.layout.South = function(config){
36539     config.region = 'south';
36540     config.cursor = 's-resize';
36541     Roo.bootstrap.layout.Split.call(this, config);
36542     if(this.split){
36543         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36544         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36545         this.split.el.addClass("roo-layout-split-v");
36546     }
36547     var size = config.initialSize || config.height;
36548     if(typeof size != "undefined"){
36549         this.el.setHeight(size);
36550     }
36551 };
36552
36553 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36554     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36555     getBox : function(){
36556         if(this.collapsed){
36557             return this.collapsedEl.getBox();
36558         }
36559         var box = this.el.getBox();
36560         if(this.split){
36561             var sh = this.split.el.getHeight();
36562             box.height += sh;
36563             box.y -= sh;
36564         }
36565         return box;
36566     },
36567     
36568     updateBox : function(box){
36569         if(this.split && !this.collapsed){
36570             var sh = this.split.el.getHeight();
36571             box.height -= sh;
36572             box.y += sh;
36573             this.split.el.setLeft(box.x);
36574             this.split.el.setTop(box.y-sh);
36575             this.split.el.setWidth(box.width);
36576         }
36577         if(this.collapsed){
36578             this.updateBody(box.width, null);
36579         }
36580         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36581     }
36582 });
36583
36584 Roo.bootstrap.layout.East = function(config){
36585     config.region = "east";
36586     config.cursor = "e-resize";
36587     Roo.bootstrap.layout.Split.call(this, config);
36588     if(this.split){
36589         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36590         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36591         this.split.el.addClass("roo-layout-split-h");
36592     }
36593     var size = config.initialSize || config.width;
36594     if(typeof size != "undefined"){
36595         this.el.setWidth(size);
36596     }
36597 };
36598 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36599     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36600     getBox : function(){
36601         if(this.collapsed){
36602             return this.collapsedEl.getBox();
36603         }
36604         var box = this.el.getBox();
36605         if(this.split){
36606             var sw = this.split.el.getWidth();
36607             box.width += sw;
36608             box.x -= sw;
36609         }
36610         return box;
36611     },
36612
36613     updateBox : function(box){
36614         if(this.split && !this.collapsed){
36615             var sw = this.split.el.getWidth();
36616             box.width -= sw;
36617             this.split.el.setLeft(box.x);
36618             this.split.el.setTop(box.y);
36619             this.split.el.setHeight(box.height);
36620             box.x += sw;
36621         }
36622         if(this.collapsed){
36623             this.updateBody(null, box.height);
36624         }
36625         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36626     }
36627 });
36628
36629 Roo.bootstrap.layout.West = function(config){
36630     config.region = "west";
36631     config.cursor = "w-resize";
36632     
36633     Roo.bootstrap.layout.Split.call(this, config);
36634     if(this.split){
36635         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36636         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36637         this.split.el.addClass("roo-layout-split-h");
36638     }
36639     
36640 };
36641 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36642     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36643     
36644     onRender: function(ctr, pos)
36645     {
36646         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36647         var size = this.config.initialSize || this.config.width;
36648         if(typeof size != "undefined"){
36649             this.el.setWidth(size);
36650         }
36651     },
36652     
36653     getBox : function(){
36654         if(this.collapsed){
36655             return this.collapsedEl.getBox();
36656         }
36657         var box = this.el.getBox();
36658         if(this.split){
36659             box.width += this.split.el.getWidth();
36660         }
36661         return box;
36662     },
36663     
36664     updateBox : function(box){
36665         if(this.split && !this.collapsed){
36666             var sw = this.split.el.getWidth();
36667             box.width -= sw;
36668             this.split.el.setLeft(box.x+box.width);
36669             this.split.el.setTop(box.y);
36670             this.split.el.setHeight(box.height);
36671         }
36672         if(this.collapsed){
36673             this.updateBody(null, box.height);
36674         }
36675         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36676     }
36677 });
36678 Roo.namespace("Roo.bootstrap.panel");/*
36679  * Based on:
36680  * Ext JS Library 1.1.1
36681  * Copyright(c) 2006-2007, Ext JS, LLC.
36682  *
36683  * Originally Released Under LGPL - original licence link has changed is not relivant.
36684  *
36685  * Fork - LGPL
36686  * <script type="text/javascript">
36687  */
36688 /**
36689  * @class Roo.ContentPanel
36690  * @extends Roo.util.Observable
36691  * A basic ContentPanel element.
36692  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36693  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36694  * @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
36695  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36696  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36697  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36698  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36699  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36700  * @cfg {String} title          The title for this panel
36701  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36702  * @cfg {String} url            Calls {@link #setUrl} with this value
36703  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36704  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36705  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36706  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36707  * @cfg {Boolean} badges render the badges
36708
36709  * @constructor
36710  * Create a new ContentPanel.
36711  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36712  * @param {String/Object} config A string to set only the title or a config object
36713  * @param {String} content (optional) Set the HTML content for this panel
36714  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36715  */
36716 Roo.bootstrap.panel.Content = function( config){
36717     
36718     this.tpl = config.tpl || false;
36719     
36720     var el = config.el;
36721     var content = config.content;
36722
36723     if(config.autoCreate){ // xtype is available if this is called from factory
36724         el = Roo.id();
36725     }
36726     this.el = Roo.get(el);
36727     if(!this.el && config && config.autoCreate){
36728         if(typeof config.autoCreate == "object"){
36729             if(!config.autoCreate.id){
36730                 config.autoCreate.id = config.id||el;
36731             }
36732             this.el = Roo.DomHelper.append(document.body,
36733                         config.autoCreate, true);
36734         }else{
36735             var elcfg =  {   tag: "div",
36736                             cls: "roo-layout-inactive-content",
36737                             id: config.id||el
36738                             };
36739             if (config.html) {
36740                 elcfg.html = config.html;
36741                 
36742             }
36743                         
36744             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36745         }
36746     } 
36747     this.closable = false;
36748     this.loaded = false;
36749     this.active = false;
36750    
36751       
36752     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36753         
36754         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36755         
36756         this.wrapEl = this.el; //this.el.wrap();
36757         var ti = [];
36758         if (config.toolbar.items) {
36759             ti = config.toolbar.items ;
36760             delete config.toolbar.items ;
36761         }
36762         
36763         var nitems = [];
36764         this.toolbar.render(this.wrapEl, 'before');
36765         for(var i =0;i < ti.length;i++) {
36766           //  Roo.log(['add child', items[i]]);
36767             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36768         }
36769         this.toolbar.items = nitems;
36770         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36771         delete config.toolbar;
36772         
36773     }
36774     /*
36775     // xtype created footer. - not sure if will work as we normally have to render first..
36776     if (this.footer && !this.footer.el && this.footer.xtype) {
36777         if (!this.wrapEl) {
36778             this.wrapEl = this.el.wrap();
36779         }
36780     
36781         this.footer.container = this.wrapEl.createChild();
36782          
36783         this.footer = Roo.factory(this.footer, Roo);
36784         
36785     }
36786     */
36787     
36788      if(typeof config == "string"){
36789         this.title = config;
36790     }else{
36791         Roo.apply(this, config);
36792     }
36793     
36794     if(this.resizeEl){
36795         this.resizeEl = Roo.get(this.resizeEl, true);
36796     }else{
36797         this.resizeEl = this.el;
36798     }
36799     // handle view.xtype
36800     
36801  
36802     
36803     
36804     this.addEvents({
36805         /**
36806          * @event activate
36807          * Fires when this panel is activated. 
36808          * @param {Roo.ContentPanel} this
36809          */
36810         "activate" : true,
36811         /**
36812          * @event deactivate
36813          * Fires when this panel is activated. 
36814          * @param {Roo.ContentPanel} this
36815          */
36816         "deactivate" : true,
36817
36818         /**
36819          * @event resize
36820          * Fires when this panel is resized if fitToFrame is true.
36821          * @param {Roo.ContentPanel} this
36822          * @param {Number} width The width after any component adjustments
36823          * @param {Number} height The height after any component adjustments
36824          */
36825         "resize" : true,
36826         
36827          /**
36828          * @event render
36829          * Fires when this tab is created
36830          * @param {Roo.ContentPanel} this
36831          */
36832         "render" : true
36833         
36834         
36835         
36836     });
36837     
36838
36839     
36840     
36841     if(this.autoScroll){
36842         this.resizeEl.setStyle("overflow", "auto");
36843     } else {
36844         // fix randome scrolling
36845         //this.el.on('scroll', function() {
36846         //    Roo.log('fix random scolling');
36847         //    this.scrollTo('top',0); 
36848         //});
36849     }
36850     content = content || this.content;
36851     if(content){
36852         this.setContent(content);
36853     }
36854     if(config && config.url){
36855         this.setUrl(this.url, this.params, this.loadOnce);
36856     }
36857     
36858     
36859     
36860     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36861     
36862     if (this.view && typeof(this.view.xtype) != 'undefined') {
36863         this.view.el = this.el.appendChild(document.createElement("div"));
36864         this.view = Roo.factory(this.view); 
36865         this.view.render  &&  this.view.render(false, '');  
36866     }
36867     
36868     
36869     this.fireEvent('render', this);
36870 };
36871
36872 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36873     
36874     tabTip : '',
36875     
36876     setRegion : function(region){
36877         this.region = region;
36878         this.setActiveClass(region && !this.background);
36879     },
36880     
36881     
36882     setActiveClass: function(state)
36883     {
36884         if(state){
36885            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36886            this.el.setStyle('position','relative');
36887         }else{
36888            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36889            this.el.setStyle('position', 'absolute');
36890         } 
36891     },
36892     
36893     /**
36894      * Returns the toolbar for this Panel if one was configured. 
36895      * @return {Roo.Toolbar} 
36896      */
36897     getToolbar : function(){
36898         return this.toolbar;
36899     },
36900     
36901     setActiveState : function(active)
36902     {
36903         this.active = active;
36904         this.setActiveClass(active);
36905         if(!active){
36906             if(this.fireEvent("deactivate", this) === false){
36907                 return false;
36908             }
36909             return true;
36910         }
36911         this.fireEvent("activate", this);
36912         return true;
36913     },
36914     /**
36915      * Updates this panel's element
36916      * @param {String} content The new content
36917      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36918     */
36919     setContent : function(content, loadScripts){
36920         this.el.update(content, loadScripts);
36921     },
36922
36923     ignoreResize : function(w, h){
36924         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36925             return true;
36926         }else{
36927             this.lastSize = {width: w, height: h};
36928             return false;
36929         }
36930     },
36931     /**
36932      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36933      * @return {Roo.UpdateManager} The UpdateManager
36934      */
36935     getUpdateManager : function(){
36936         return this.el.getUpdateManager();
36937     },
36938      /**
36939      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36940      * @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:
36941 <pre><code>
36942 panel.load({
36943     url: "your-url.php",
36944     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36945     callback: yourFunction,
36946     scope: yourObject, //(optional scope)
36947     discardUrl: false,
36948     nocache: false,
36949     text: "Loading...",
36950     timeout: 30,
36951     scripts: false
36952 });
36953 </code></pre>
36954      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36955      * 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.
36956      * @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}
36957      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36958      * @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.
36959      * @return {Roo.ContentPanel} this
36960      */
36961     load : function(){
36962         var um = this.el.getUpdateManager();
36963         um.update.apply(um, arguments);
36964         return this;
36965     },
36966
36967
36968     /**
36969      * 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.
36970      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36971      * @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)
36972      * @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)
36973      * @return {Roo.UpdateManager} The UpdateManager
36974      */
36975     setUrl : function(url, params, loadOnce){
36976         if(this.refreshDelegate){
36977             this.removeListener("activate", this.refreshDelegate);
36978         }
36979         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36980         this.on("activate", this.refreshDelegate);
36981         return this.el.getUpdateManager();
36982     },
36983     
36984     _handleRefresh : function(url, params, loadOnce){
36985         if(!loadOnce || !this.loaded){
36986             var updater = this.el.getUpdateManager();
36987             updater.update(url, params, this._setLoaded.createDelegate(this));
36988         }
36989     },
36990     
36991     _setLoaded : function(){
36992         this.loaded = true;
36993     }, 
36994     
36995     /**
36996      * Returns this panel's id
36997      * @return {String} 
36998      */
36999     getId : function(){
37000         return this.el.id;
37001     },
37002     
37003     /** 
37004      * Returns this panel's element - used by regiosn to add.
37005      * @return {Roo.Element} 
37006      */
37007     getEl : function(){
37008         return this.wrapEl || this.el;
37009     },
37010     
37011    
37012     
37013     adjustForComponents : function(width, height)
37014     {
37015         //Roo.log('adjustForComponents ');
37016         if(this.resizeEl != this.el){
37017             width -= this.el.getFrameWidth('lr');
37018             height -= this.el.getFrameWidth('tb');
37019         }
37020         if(this.toolbar){
37021             var te = this.toolbar.getEl();
37022             te.setWidth(width);
37023             height -= te.getHeight();
37024         }
37025         if(this.footer){
37026             var te = this.footer.getEl();
37027             te.setWidth(width);
37028             height -= te.getHeight();
37029         }
37030         
37031         
37032         if(this.adjustments){
37033             width += this.adjustments[0];
37034             height += this.adjustments[1];
37035         }
37036         return {"width": width, "height": height};
37037     },
37038     
37039     setSize : function(width, height){
37040         if(this.fitToFrame && !this.ignoreResize(width, height)){
37041             if(this.fitContainer && this.resizeEl != this.el){
37042                 this.el.setSize(width, height);
37043             }
37044             var size = this.adjustForComponents(width, height);
37045             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37046             this.fireEvent('resize', this, size.width, size.height);
37047         }
37048     },
37049     
37050     /**
37051      * Returns this panel's title
37052      * @return {String} 
37053      */
37054     getTitle : function(){
37055         
37056         if (typeof(this.title) != 'object') {
37057             return this.title;
37058         }
37059         
37060         var t = '';
37061         for (var k in this.title) {
37062             if (!this.title.hasOwnProperty(k)) {
37063                 continue;
37064             }
37065             
37066             if (k.indexOf('-') >= 0) {
37067                 var s = k.split('-');
37068                 for (var i = 0; i<s.length; i++) {
37069                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37070                 }
37071             } else {
37072                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37073             }
37074         }
37075         return t;
37076     },
37077     
37078     /**
37079      * Set this panel's title
37080      * @param {String} title
37081      */
37082     setTitle : function(title){
37083         this.title = title;
37084         if(this.region){
37085             this.region.updatePanelTitle(this, title);
37086         }
37087     },
37088     
37089     /**
37090      * Returns true is this panel was configured to be closable
37091      * @return {Boolean} 
37092      */
37093     isClosable : function(){
37094         return this.closable;
37095     },
37096     
37097     beforeSlide : function(){
37098         this.el.clip();
37099         this.resizeEl.clip();
37100     },
37101     
37102     afterSlide : function(){
37103         this.el.unclip();
37104         this.resizeEl.unclip();
37105     },
37106     
37107     /**
37108      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37109      *   Will fail silently if the {@link #setUrl} method has not been called.
37110      *   This does not activate the panel, just updates its content.
37111      */
37112     refresh : function(){
37113         if(this.refreshDelegate){
37114            this.loaded = false;
37115            this.refreshDelegate();
37116         }
37117     },
37118     
37119     /**
37120      * Destroys this panel
37121      */
37122     destroy : function(){
37123         this.el.removeAllListeners();
37124         var tempEl = document.createElement("span");
37125         tempEl.appendChild(this.el.dom);
37126         tempEl.innerHTML = "";
37127         this.el.remove();
37128         this.el = null;
37129     },
37130     
37131     /**
37132      * form - if the content panel contains a form - this is a reference to it.
37133      * @type {Roo.form.Form}
37134      */
37135     form : false,
37136     /**
37137      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37138      *    This contains a reference to it.
37139      * @type {Roo.View}
37140      */
37141     view : false,
37142     
37143       /**
37144      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37145      * <pre><code>
37146
37147 layout.addxtype({
37148        xtype : 'Form',
37149        items: [ .... ]
37150    }
37151 );
37152
37153 </code></pre>
37154      * @param {Object} cfg Xtype definition of item to add.
37155      */
37156     
37157     
37158     getChildContainer: function () {
37159         return this.getEl();
37160     }
37161     
37162     
37163     /*
37164         var  ret = new Roo.factory(cfg);
37165         return ret;
37166         
37167         
37168         // add form..
37169         if (cfg.xtype.match(/^Form$/)) {
37170             
37171             var el;
37172             //if (this.footer) {
37173             //    el = this.footer.container.insertSibling(false, 'before');
37174             //} else {
37175                 el = this.el.createChild();
37176             //}
37177
37178             this.form = new  Roo.form.Form(cfg);
37179             
37180             
37181             if ( this.form.allItems.length) {
37182                 this.form.render(el.dom);
37183             }
37184             return this.form;
37185         }
37186         // should only have one of theses..
37187         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37188             // views.. should not be just added - used named prop 'view''
37189             
37190             cfg.el = this.el.appendChild(document.createElement("div"));
37191             // factory?
37192             
37193             var ret = new Roo.factory(cfg);
37194              
37195              ret.render && ret.render(false, ''); // render blank..
37196             this.view = ret;
37197             return ret;
37198         }
37199         return false;
37200     }
37201     \*/
37202 });
37203  
37204 /**
37205  * @class Roo.bootstrap.panel.Grid
37206  * @extends Roo.bootstrap.panel.Content
37207  * @constructor
37208  * Create a new GridPanel.
37209  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37210  * @param {Object} config A the config object
37211   
37212  */
37213
37214
37215
37216 Roo.bootstrap.panel.Grid = function(config)
37217 {
37218     
37219       
37220     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37221         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37222
37223     config.el = this.wrapper;
37224     //this.el = this.wrapper;
37225     
37226       if (config.container) {
37227         // ctor'ed from a Border/panel.grid
37228         
37229         
37230         this.wrapper.setStyle("overflow", "hidden");
37231         this.wrapper.addClass('roo-grid-container');
37232
37233     }
37234     
37235     
37236     if(config.toolbar){
37237         var tool_el = this.wrapper.createChild();    
37238         this.toolbar = Roo.factory(config.toolbar);
37239         var ti = [];
37240         if (config.toolbar.items) {
37241             ti = config.toolbar.items ;
37242             delete config.toolbar.items ;
37243         }
37244         
37245         var nitems = [];
37246         this.toolbar.render(tool_el);
37247         for(var i =0;i < ti.length;i++) {
37248           //  Roo.log(['add child', items[i]]);
37249             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37250         }
37251         this.toolbar.items = nitems;
37252         
37253         delete config.toolbar;
37254     }
37255     
37256     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37257     config.grid.scrollBody = true;;
37258     config.grid.monitorWindowResize = false; // turn off autosizing
37259     config.grid.autoHeight = false;
37260     config.grid.autoWidth = false;
37261     
37262     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37263     
37264     if (config.background) {
37265         // render grid on panel activation (if panel background)
37266         this.on('activate', function(gp) {
37267             if (!gp.grid.rendered) {
37268                 gp.grid.render(this.wrapper);
37269                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37270             }
37271         });
37272             
37273     } else {
37274         this.grid.render(this.wrapper);
37275         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37276
37277     }
37278     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37279     // ??? needed ??? config.el = this.wrapper;
37280     
37281     
37282     
37283   
37284     // xtype created footer. - not sure if will work as we normally have to render first..
37285     if (this.footer && !this.footer.el && this.footer.xtype) {
37286         
37287         var ctr = this.grid.getView().getFooterPanel(true);
37288         this.footer.dataSource = this.grid.dataSource;
37289         this.footer = Roo.factory(this.footer, Roo);
37290         this.footer.render(ctr);
37291         
37292     }
37293     
37294     
37295     
37296     
37297      
37298 };
37299
37300 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37301     getId : function(){
37302         return this.grid.id;
37303     },
37304     
37305     /**
37306      * Returns the grid for this panel
37307      * @return {Roo.bootstrap.Table} 
37308      */
37309     getGrid : function(){
37310         return this.grid;    
37311     },
37312     
37313     setSize : function(width, height){
37314         if(!this.ignoreResize(width, height)){
37315             var grid = this.grid;
37316             var size = this.adjustForComponents(width, height);
37317             var gridel = grid.getGridEl();
37318             gridel.setSize(size.width, size.height);
37319             /*
37320             var thd = grid.getGridEl().select('thead',true).first();
37321             var tbd = grid.getGridEl().select('tbody', true).first();
37322             if (tbd) {
37323                 tbd.setSize(width, height - thd.getHeight());
37324             }
37325             */
37326             grid.autoSize();
37327         }
37328     },
37329      
37330     
37331     
37332     beforeSlide : function(){
37333         this.grid.getView().scroller.clip();
37334     },
37335     
37336     afterSlide : function(){
37337         this.grid.getView().scroller.unclip();
37338     },
37339     
37340     destroy : function(){
37341         this.grid.destroy();
37342         delete this.grid;
37343         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37344     }
37345 });
37346
37347 /**
37348  * @class Roo.bootstrap.panel.Nest
37349  * @extends Roo.bootstrap.panel.Content
37350  * @constructor
37351  * Create a new Panel, that can contain a layout.Border.
37352  * 
37353  * 
37354  * @param {Roo.BorderLayout} layout The layout for this panel
37355  * @param {String/Object} config A string to set only the title or a config object
37356  */
37357 Roo.bootstrap.panel.Nest = function(config)
37358 {
37359     // construct with only one argument..
37360     /* FIXME - implement nicer consturctors
37361     if (layout.layout) {
37362         config = layout;
37363         layout = config.layout;
37364         delete config.layout;
37365     }
37366     if (layout.xtype && !layout.getEl) {
37367         // then layout needs constructing..
37368         layout = Roo.factory(layout, Roo);
37369     }
37370     */
37371     
37372     config.el =  config.layout.getEl();
37373     
37374     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37375     
37376     config.layout.monitorWindowResize = false; // turn off autosizing
37377     this.layout = config.layout;
37378     this.layout.getEl().addClass("roo-layout-nested-layout");
37379     
37380     
37381     
37382     
37383 };
37384
37385 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37386
37387     setSize : function(width, height){
37388         if(!this.ignoreResize(width, height)){
37389             var size = this.adjustForComponents(width, height);
37390             var el = this.layout.getEl();
37391             if (size.height < 1) {
37392                 el.setWidth(size.width);   
37393             } else {
37394                 el.setSize(size.width, size.height);
37395             }
37396             var touch = el.dom.offsetWidth;
37397             this.layout.layout();
37398             // ie requires a double layout on the first pass
37399             if(Roo.isIE && !this.initialized){
37400                 this.initialized = true;
37401                 this.layout.layout();
37402             }
37403         }
37404     },
37405     
37406     // activate all subpanels if not currently active..
37407     
37408     setActiveState : function(active){
37409         this.active = active;
37410         this.setActiveClass(active);
37411         
37412         if(!active){
37413             this.fireEvent("deactivate", this);
37414             return;
37415         }
37416         
37417         this.fireEvent("activate", this);
37418         // not sure if this should happen before or after..
37419         if (!this.layout) {
37420             return; // should not happen..
37421         }
37422         var reg = false;
37423         for (var r in this.layout.regions) {
37424             reg = this.layout.getRegion(r);
37425             if (reg.getActivePanel()) {
37426                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37427                 reg.setActivePanel(reg.getActivePanel());
37428                 continue;
37429             }
37430             if (!reg.panels.length) {
37431                 continue;
37432             }
37433             reg.showPanel(reg.getPanel(0));
37434         }
37435         
37436         
37437         
37438         
37439     },
37440     
37441     /**
37442      * Returns the nested BorderLayout for this panel
37443      * @return {Roo.BorderLayout} 
37444      */
37445     getLayout : function(){
37446         return this.layout;
37447     },
37448     
37449      /**
37450      * Adds a xtype elements to the layout of the nested panel
37451      * <pre><code>
37452
37453 panel.addxtype({
37454        xtype : 'ContentPanel',
37455        region: 'west',
37456        items: [ .... ]
37457    }
37458 );
37459
37460 panel.addxtype({
37461         xtype : 'NestedLayoutPanel',
37462         region: 'west',
37463         layout: {
37464            center: { },
37465            west: { }   
37466         },
37467         items : [ ... list of content panels or nested layout panels.. ]
37468    }
37469 );
37470 </code></pre>
37471      * @param {Object} cfg Xtype definition of item to add.
37472      */
37473     addxtype : function(cfg) {
37474         return this.layout.addxtype(cfg);
37475     
37476     }
37477 });        /*
37478  * Based on:
37479  * Ext JS Library 1.1.1
37480  * Copyright(c) 2006-2007, Ext JS, LLC.
37481  *
37482  * Originally Released Under LGPL - original licence link has changed is not relivant.
37483  *
37484  * Fork - LGPL
37485  * <script type="text/javascript">
37486  */
37487 /**
37488  * @class Roo.TabPanel
37489  * @extends Roo.util.Observable
37490  * A lightweight tab container.
37491  * <br><br>
37492  * Usage:
37493  * <pre><code>
37494 // basic tabs 1, built from existing content
37495 var tabs = new Roo.TabPanel("tabs1");
37496 tabs.addTab("script", "View Script");
37497 tabs.addTab("markup", "View Markup");
37498 tabs.activate("script");
37499
37500 // more advanced tabs, built from javascript
37501 var jtabs = new Roo.TabPanel("jtabs");
37502 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37503
37504 // set up the UpdateManager
37505 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37506 var updater = tab2.getUpdateManager();
37507 updater.setDefaultUrl("ajax1.htm");
37508 tab2.on('activate', updater.refresh, updater, true);
37509
37510 // Use setUrl for Ajax loading
37511 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37512 tab3.setUrl("ajax2.htm", null, true);
37513
37514 // Disabled tab
37515 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37516 tab4.disable();
37517
37518 jtabs.activate("jtabs-1");
37519  * </code></pre>
37520  * @constructor
37521  * Create a new TabPanel.
37522  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37523  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37524  */
37525 Roo.bootstrap.panel.Tabs = function(config){
37526     /**
37527     * The container element for this TabPanel.
37528     * @type Roo.Element
37529     */
37530     this.el = Roo.get(config.el);
37531     delete config.el;
37532     if(config){
37533         if(typeof config == "boolean"){
37534             this.tabPosition = config ? "bottom" : "top";
37535         }else{
37536             Roo.apply(this, config);
37537         }
37538     }
37539     
37540     if(this.tabPosition == "bottom"){
37541         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37542         this.el.addClass("roo-tabs-bottom");
37543     }
37544     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37545     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37546     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37547     if(Roo.isIE){
37548         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37549     }
37550     if(this.tabPosition != "bottom"){
37551         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37552          * @type Roo.Element
37553          */
37554         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37555         this.el.addClass("roo-tabs-top");
37556     }
37557     this.items = [];
37558
37559     this.bodyEl.setStyle("position", "relative");
37560
37561     this.active = null;
37562     this.activateDelegate = this.activate.createDelegate(this);
37563
37564     this.addEvents({
37565         /**
37566          * @event tabchange
37567          * Fires when the active tab changes
37568          * @param {Roo.TabPanel} this
37569          * @param {Roo.TabPanelItem} activePanel The new active tab
37570          */
37571         "tabchange": true,
37572         /**
37573          * @event beforetabchange
37574          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37575          * @param {Roo.TabPanel} this
37576          * @param {Object} e Set cancel to true on this object to cancel the tab change
37577          * @param {Roo.TabPanelItem} tab The tab being changed to
37578          */
37579         "beforetabchange" : true
37580     });
37581
37582     Roo.EventManager.onWindowResize(this.onResize, this);
37583     this.cpad = this.el.getPadding("lr");
37584     this.hiddenCount = 0;
37585
37586
37587     // toolbar on the tabbar support...
37588     if (this.toolbar) {
37589         alert("no toolbar support yet");
37590         this.toolbar  = false;
37591         /*
37592         var tcfg = this.toolbar;
37593         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37594         this.toolbar = new Roo.Toolbar(tcfg);
37595         if (Roo.isSafari) {
37596             var tbl = tcfg.container.child('table', true);
37597             tbl.setAttribute('width', '100%');
37598         }
37599         */
37600         
37601     }
37602    
37603
37604
37605     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37606 };
37607
37608 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37609     /*
37610      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37611      */
37612     tabPosition : "top",
37613     /*
37614      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37615      */
37616     currentTabWidth : 0,
37617     /*
37618      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37619      */
37620     minTabWidth : 40,
37621     /*
37622      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37623      */
37624     maxTabWidth : 250,
37625     /*
37626      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37627      */
37628     preferredTabWidth : 175,
37629     /*
37630      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37631      */
37632     resizeTabs : false,
37633     /*
37634      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37635      */
37636     monitorResize : true,
37637     /*
37638      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37639      */
37640     toolbar : false,
37641
37642     /**
37643      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37644      * @param {String} id The id of the div to use <b>or create</b>
37645      * @param {String} text The text for the tab
37646      * @param {String} content (optional) Content to put in the TabPanelItem body
37647      * @param {Boolean} closable (optional) True to create a close icon on the tab
37648      * @return {Roo.TabPanelItem} The created TabPanelItem
37649      */
37650     addTab : function(id, text, content, closable, tpl)
37651     {
37652         var item = new Roo.bootstrap.panel.TabItem({
37653             panel: this,
37654             id : id,
37655             text : text,
37656             closable : closable,
37657             tpl : tpl
37658         });
37659         this.addTabItem(item);
37660         if(content){
37661             item.setContent(content);
37662         }
37663         return item;
37664     },
37665
37666     /**
37667      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37668      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37669      * @return {Roo.TabPanelItem}
37670      */
37671     getTab : function(id){
37672         return this.items[id];
37673     },
37674
37675     /**
37676      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37677      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37678      */
37679     hideTab : function(id){
37680         var t = this.items[id];
37681         if(!t.isHidden()){
37682            t.setHidden(true);
37683            this.hiddenCount++;
37684            this.autoSizeTabs();
37685         }
37686     },
37687
37688     /**
37689      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37690      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37691      */
37692     unhideTab : function(id){
37693         var t = this.items[id];
37694         if(t.isHidden()){
37695            t.setHidden(false);
37696            this.hiddenCount--;
37697            this.autoSizeTabs();
37698         }
37699     },
37700
37701     /**
37702      * Adds an existing {@link Roo.TabPanelItem}.
37703      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37704      */
37705     addTabItem : function(item){
37706         this.items[item.id] = item;
37707         this.items.push(item);
37708       //  if(this.resizeTabs){
37709     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37710   //         this.autoSizeTabs();
37711 //        }else{
37712 //            item.autoSize();
37713        // }
37714     },
37715
37716     /**
37717      * Removes a {@link Roo.TabPanelItem}.
37718      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37719      */
37720     removeTab : function(id){
37721         var items = this.items;
37722         var tab = items[id];
37723         if(!tab) { return; }
37724         var index = items.indexOf(tab);
37725         if(this.active == tab && items.length > 1){
37726             var newTab = this.getNextAvailable(index);
37727             if(newTab) {
37728                 newTab.activate();
37729             }
37730         }
37731         this.stripEl.dom.removeChild(tab.pnode.dom);
37732         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37733             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37734         }
37735         items.splice(index, 1);
37736         delete this.items[tab.id];
37737         tab.fireEvent("close", tab);
37738         tab.purgeListeners();
37739         this.autoSizeTabs();
37740     },
37741
37742     getNextAvailable : function(start){
37743         var items = this.items;
37744         var index = start;
37745         // look for a next tab that will slide over to
37746         // replace the one being removed
37747         while(index < items.length){
37748             var item = items[++index];
37749             if(item && !item.isHidden()){
37750                 return item;
37751             }
37752         }
37753         // if one isn't found select the previous tab (on the left)
37754         index = start;
37755         while(index >= 0){
37756             var item = items[--index];
37757             if(item && !item.isHidden()){
37758                 return item;
37759             }
37760         }
37761         return null;
37762     },
37763
37764     /**
37765      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37766      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37767      */
37768     disableTab : function(id){
37769         var tab = this.items[id];
37770         if(tab && this.active != tab){
37771             tab.disable();
37772         }
37773     },
37774
37775     /**
37776      * Enables a {@link Roo.TabPanelItem} that is disabled.
37777      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37778      */
37779     enableTab : function(id){
37780         var tab = this.items[id];
37781         tab.enable();
37782     },
37783
37784     /**
37785      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37786      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37787      * @return {Roo.TabPanelItem} The TabPanelItem.
37788      */
37789     activate : function(id){
37790         var tab = this.items[id];
37791         if(!tab){
37792             return null;
37793         }
37794         if(tab == this.active || tab.disabled){
37795             return tab;
37796         }
37797         var e = {};
37798         this.fireEvent("beforetabchange", this, e, tab);
37799         if(e.cancel !== true && !tab.disabled){
37800             if(this.active){
37801                 this.active.hide();
37802             }
37803             this.active = this.items[id];
37804             this.active.show();
37805             this.fireEvent("tabchange", this, this.active);
37806         }
37807         return tab;
37808     },
37809
37810     /**
37811      * Gets the active {@link Roo.TabPanelItem}.
37812      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37813      */
37814     getActiveTab : function(){
37815         return this.active;
37816     },
37817
37818     /**
37819      * Updates the tab body element to fit the height of the container element
37820      * for overflow scrolling
37821      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37822      */
37823     syncHeight : function(targetHeight){
37824         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37825         var bm = this.bodyEl.getMargins();
37826         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37827         this.bodyEl.setHeight(newHeight);
37828         return newHeight;
37829     },
37830
37831     onResize : function(){
37832         if(this.monitorResize){
37833             this.autoSizeTabs();
37834         }
37835     },
37836
37837     /**
37838      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37839      */
37840     beginUpdate : function(){
37841         this.updating = true;
37842     },
37843
37844     /**
37845      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37846      */
37847     endUpdate : function(){
37848         this.updating = false;
37849         this.autoSizeTabs();
37850     },
37851
37852     /**
37853      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37854      */
37855     autoSizeTabs : function(){
37856         var count = this.items.length;
37857         var vcount = count - this.hiddenCount;
37858         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37859             return;
37860         }
37861         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37862         var availWidth = Math.floor(w / vcount);
37863         var b = this.stripBody;
37864         if(b.getWidth() > w){
37865             var tabs = this.items;
37866             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37867             if(availWidth < this.minTabWidth){
37868                 /*if(!this.sleft){    // incomplete scrolling code
37869                     this.createScrollButtons();
37870                 }
37871                 this.showScroll();
37872                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37873             }
37874         }else{
37875             if(this.currentTabWidth < this.preferredTabWidth){
37876                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37877             }
37878         }
37879     },
37880
37881     /**
37882      * Returns the number of tabs in this TabPanel.
37883      * @return {Number}
37884      */
37885      getCount : function(){
37886          return this.items.length;
37887      },
37888
37889     /**
37890      * Resizes all the tabs to the passed width
37891      * @param {Number} The new width
37892      */
37893     setTabWidth : function(width){
37894         this.currentTabWidth = width;
37895         for(var i = 0, len = this.items.length; i < len; i++) {
37896                 if(!this.items[i].isHidden()) {
37897                 this.items[i].setWidth(width);
37898             }
37899         }
37900     },
37901
37902     /**
37903      * Destroys this TabPanel
37904      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37905      */
37906     destroy : function(removeEl){
37907         Roo.EventManager.removeResizeListener(this.onResize, this);
37908         for(var i = 0, len = this.items.length; i < len; i++){
37909             this.items[i].purgeListeners();
37910         }
37911         if(removeEl === true){
37912             this.el.update("");
37913             this.el.remove();
37914         }
37915     },
37916     
37917     createStrip : function(container)
37918     {
37919         var strip = document.createElement("nav");
37920         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37921         container.appendChild(strip);
37922         return strip;
37923     },
37924     
37925     createStripList : function(strip)
37926     {
37927         // div wrapper for retard IE
37928         // returns the "tr" element.
37929         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37930         //'<div class="x-tabs-strip-wrap">'+
37931           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37932           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37933         return strip.firstChild; //.firstChild.firstChild.firstChild;
37934     },
37935     createBody : function(container)
37936     {
37937         var body = document.createElement("div");
37938         Roo.id(body, "tab-body");
37939         //Roo.fly(body).addClass("x-tabs-body");
37940         Roo.fly(body).addClass("tab-content");
37941         container.appendChild(body);
37942         return body;
37943     },
37944     createItemBody :function(bodyEl, id){
37945         var body = Roo.getDom(id);
37946         if(!body){
37947             body = document.createElement("div");
37948             body.id = id;
37949         }
37950         //Roo.fly(body).addClass("x-tabs-item-body");
37951         Roo.fly(body).addClass("tab-pane");
37952          bodyEl.insertBefore(body, bodyEl.firstChild);
37953         return body;
37954     },
37955     /** @private */
37956     createStripElements :  function(stripEl, text, closable, tpl)
37957     {
37958         var td = document.createElement("li"); // was td..
37959         
37960         
37961         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37962         
37963         
37964         stripEl.appendChild(td);
37965         /*if(closable){
37966             td.className = "x-tabs-closable";
37967             if(!this.closeTpl){
37968                 this.closeTpl = new Roo.Template(
37969                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37970                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37971                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37972                 );
37973             }
37974             var el = this.closeTpl.overwrite(td, {"text": text});
37975             var close = el.getElementsByTagName("div")[0];
37976             var inner = el.getElementsByTagName("em")[0];
37977             return {"el": el, "close": close, "inner": inner};
37978         } else {
37979         */
37980         // not sure what this is..
37981 //            if(!this.tabTpl){
37982                 //this.tabTpl = new Roo.Template(
37983                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37984                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37985                 //);
37986 //                this.tabTpl = new Roo.Template(
37987 //                   '<a href="#">' +
37988 //                   '<span unselectable="on"' +
37989 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37990 //                            ' >{text}</span></a>'
37991 //                );
37992 //                
37993 //            }
37994
37995
37996             var template = tpl || this.tabTpl || false;
37997             
37998             if(!template){
37999                 
38000                 template = new Roo.Template(
38001                    '<a href="#">' +
38002                    '<span unselectable="on"' +
38003                             (this.disableTooltips ? '' : ' title="{text}"') +
38004                             ' >{text}</span></a>'
38005                 );
38006             }
38007             
38008             switch (typeof(template)) {
38009                 case 'object' :
38010                     break;
38011                 case 'string' :
38012                     template = new Roo.Template(template);
38013                     break;
38014                 default :
38015                     break;
38016             }
38017             
38018             var el = template.overwrite(td, {"text": text});
38019             
38020             var inner = el.getElementsByTagName("span")[0];
38021             
38022             return {"el": el, "inner": inner};
38023             
38024     }
38025         
38026     
38027 });
38028
38029 /**
38030  * @class Roo.TabPanelItem
38031  * @extends Roo.util.Observable
38032  * Represents an individual item (tab plus body) in a TabPanel.
38033  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38034  * @param {String} id The id of this TabPanelItem
38035  * @param {String} text The text for the tab of this TabPanelItem
38036  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38037  */
38038 Roo.bootstrap.panel.TabItem = function(config){
38039     /**
38040      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38041      * @type Roo.TabPanel
38042      */
38043     this.tabPanel = config.panel;
38044     /**
38045      * The id for this TabPanelItem
38046      * @type String
38047      */
38048     this.id = config.id;
38049     /** @private */
38050     this.disabled = false;
38051     /** @private */
38052     this.text = config.text;
38053     /** @private */
38054     this.loaded = false;
38055     this.closable = config.closable;
38056
38057     /**
38058      * The body element for this TabPanelItem.
38059      * @type Roo.Element
38060      */
38061     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38062     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38063     this.bodyEl.setStyle("display", "block");
38064     this.bodyEl.setStyle("zoom", "1");
38065     //this.hideAction();
38066
38067     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38068     /** @private */
38069     this.el = Roo.get(els.el);
38070     this.inner = Roo.get(els.inner, true);
38071     this.textEl = Roo.get(this.el.dom.firstChild, true);
38072     this.pnode = Roo.get(els.el.parentNode, true);
38073 //    this.el.on("mousedown", this.onTabMouseDown, this);
38074     this.el.on("click", this.onTabClick, this);
38075     /** @private */
38076     if(config.closable){
38077         var c = Roo.get(els.close, true);
38078         c.dom.title = this.closeText;
38079         c.addClassOnOver("close-over");
38080         c.on("click", this.closeClick, this);
38081      }
38082
38083     this.addEvents({
38084          /**
38085          * @event activate
38086          * Fires when this tab becomes the active tab.
38087          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38088          * @param {Roo.TabPanelItem} this
38089          */
38090         "activate": true,
38091         /**
38092          * @event beforeclose
38093          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38094          * @param {Roo.TabPanelItem} this
38095          * @param {Object} e Set cancel to true on this object to cancel the close.
38096          */
38097         "beforeclose": true,
38098         /**
38099          * @event close
38100          * Fires when this tab is closed.
38101          * @param {Roo.TabPanelItem} this
38102          */
38103          "close": true,
38104         /**
38105          * @event deactivate
38106          * Fires when this tab is no longer the active tab.
38107          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38108          * @param {Roo.TabPanelItem} this
38109          */
38110          "deactivate" : true
38111     });
38112     this.hidden = false;
38113
38114     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38115 };
38116
38117 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38118            {
38119     purgeListeners : function(){
38120        Roo.util.Observable.prototype.purgeListeners.call(this);
38121        this.el.removeAllListeners();
38122     },
38123     /**
38124      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38125      */
38126     show : function(){
38127         this.pnode.addClass("active");
38128         this.showAction();
38129         if(Roo.isOpera){
38130             this.tabPanel.stripWrap.repaint();
38131         }
38132         this.fireEvent("activate", this.tabPanel, this);
38133     },
38134
38135     /**
38136      * Returns true if this tab is the active tab.
38137      * @return {Boolean}
38138      */
38139     isActive : function(){
38140         return this.tabPanel.getActiveTab() == this;
38141     },
38142
38143     /**
38144      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38145      */
38146     hide : function(){
38147         this.pnode.removeClass("active");
38148         this.hideAction();
38149         this.fireEvent("deactivate", this.tabPanel, this);
38150     },
38151
38152     hideAction : function(){
38153         this.bodyEl.hide();
38154         this.bodyEl.setStyle("position", "absolute");
38155         this.bodyEl.setLeft("-20000px");
38156         this.bodyEl.setTop("-20000px");
38157     },
38158
38159     showAction : function(){
38160         this.bodyEl.setStyle("position", "relative");
38161         this.bodyEl.setTop("");
38162         this.bodyEl.setLeft("");
38163         this.bodyEl.show();
38164     },
38165
38166     /**
38167      * Set the tooltip for the tab.
38168      * @param {String} tooltip The tab's tooltip
38169      */
38170     setTooltip : function(text){
38171         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38172             this.textEl.dom.qtip = text;
38173             this.textEl.dom.removeAttribute('title');
38174         }else{
38175             this.textEl.dom.title = text;
38176         }
38177     },
38178
38179     onTabClick : function(e){
38180         e.preventDefault();
38181         this.tabPanel.activate(this.id);
38182     },
38183
38184     onTabMouseDown : function(e){
38185         e.preventDefault();
38186         this.tabPanel.activate(this.id);
38187     },
38188 /*
38189     getWidth : function(){
38190         return this.inner.getWidth();
38191     },
38192
38193     setWidth : function(width){
38194         var iwidth = width - this.pnode.getPadding("lr");
38195         this.inner.setWidth(iwidth);
38196         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38197         this.pnode.setWidth(width);
38198     },
38199 */
38200     /**
38201      * Show or hide the tab
38202      * @param {Boolean} hidden True to hide or false to show.
38203      */
38204     setHidden : function(hidden){
38205         this.hidden = hidden;
38206         this.pnode.setStyle("display", hidden ? "none" : "");
38207     },
38208
38209     /**
38210      * Returns true if this tab is "hidden"
38211      * @return {Boolean}
38212      */
38213     isHidden : function(){
38214         return this.hidden;
38215     },
38216
38217     /**
38218      * Returns the text for this tab
38219      * @return {String}
38220      */
38221     getText : function(){
38222         return this.text;
38223     },
38224     /*
38225     autoSize : function(){
38226         //this.el.beginMeasure();
38227         this.textEl.setWidth(1);
38228         /*
38229          *  #2804 [new] Tabs in Roojs
38230          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38231          */
38232         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38233         //this.el.endMeasure();
38234     //},
38235
38236     /**
38237      * Sets the text for the tab (Note: this also sets the tooltip text)
38238      * @param {String} text The tab's text and tooltip
38239      */
38240     setText : function(text){
38241         this.text = text;
38242         this.textEl.update(text);
38243         this.setTooltip(text);
38244         //if(!this.tabPanel.resizeTabs){
38245         //    this.autoSize();
38246         //}
38247     },
38248     /**
38249      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38250      */
38251     activate : function(){
38252         this.tabPanel.activate(this.id);
38253     },
38254
38255     /**
38256      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38257      */
38258     disable : function(){
38259         if(this.tabPanel.active != this){
38260             this.disabled = true;
38261             this.pnode.addClass("disabled");
38262         }
38263     },
38264
38265     /**
38266      * Enables this TabPanelItem if it was previously disabled.
38267      */
38268     enable : function(){
38269         this.disabled = false;
38270         this.pnode.removeClass("disabled");
38271     },
38272
38273     /**
38274      * Sets the content for this TabPanelItem.
38275      * @param {String} content The content
38276      * @param {Boolean} loadScripts true to look for and load scripts
38277      */
38278     setContent : function(content, loadScripts){
38279         this.bodyEl.update(content, loadScripts);
38280     },
38281
38282     /**
38283      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38284      * @return {Roo.UpdateManager} The UpdateManager
38285      */
38286     getUpdateManager : function(){
38287         return this.bodyEl.getUpdateManager();
38288     },
38289
38290     /**
38291      * Set a URL to be used to load the content for this TabPanelItem.
38292      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38293      * @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)
38294      * @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)
38295      * @return {Roo.UpdateManager} The UpdateManager
38296      */
38297     setUrl : function(url, params, loadOnce){
38298         if(this.refreshDelegate){
38299             this.un('activate', this.refreshDelegate);
38300         }
38301         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38302         this.on("activate", this.refreshDelegate);
38303         return this.bodyEl.getUpdateManager();
38304     },
38305
38306     /** @private */
38307     _handleRefresh : function(url, params, loadOnce){
38308         if(!loadOnce || !this.loaded){
38309             var updater = this.bodyEl.getUpdateManager();
38310             updater.update(url, params, this._setLoaded.createDelegate(this));
38311         }
38312     },
38313
38314     /**
38315      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38316      *   Will fail silently if the setUrl method has not been called.
38317      *   This does not activate the panel, just updates its content.
38318      */
38319     refresh : function(){
38320         if(this.refreshDelegate){
38321            this.loaded = false;
38322            this.refreshDelegate();
38323         }
38324     },
38325
38326     /** @private */
38327     _setLoaded : function(){
38328         this.loaded = true;
38329     },
38330
38331     /** @private */
38332     closeClick : function(e){
38333         var o = {};
38334         e.stopEvent();
38335         this.fireEvent("beforeclose", this, o);
38336         if(o.cancel !== true){
38337             this.tabPanel.removeTab(this.id);
38338         }
38339     },
38340     /**
38341      * The text displayed in the tooltip for the close icon.
38342      * @type String
38343      */
38344     closeText : "Close this tab"
38345 });
38346 /**
38347 *    This script refer to:
38348 *    Title: International Telephone Input
38349 *    Author: Jack O'Connor
38350 *    Code version:  v12.1.12
38351 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38352 **/
38353
38354 Roo.bootstrap.PhoneInputData = function() {
38355     var d = [
38356       [
38357         "Afghanistan (‫افغانستان‬‎)",
38358         "af",
38359         "93"
38360       ],
38361       [
38362         "Albania (Shqipëri)",
38363         "al",
38364         "355"
38365       ],
38366       [
38367         "Algeria (‫الجزائر‬‎)",
38368         "dz",
38369         "213"
38370       ],
38371       [
38372         "American Samoa",
38373         "as",
38374         "1684"
38375       ],
38376       [
38377         "Andorra",
38378         "ad",
38379         "376"
38380       ],
38381       [
38382         "Angola",
38383         "ao",
38384         "244"
38385       ],
38386       [
38387         "Anguilla",
38388         "ai",
38389         "1264"
38390       ],
38391       [
38392         "Antigua and Barbuda",
38393         "ag",
38394         "1268"
38395       ],
38396       [
38397         "Argentina",
38398         "ar",
38399         "54"
38400       ],
38401       [
38402         "Armenia (Հայաստան)",
38403         "am",
38404         "374"
38405       ],
38406       [
38407         "Aruba",
38408         "aw",
38409         "297"
38410       ],
38411       [
38412         "Australia",
38413         "au",
38414         "61",
38415         0
38416       ],
38417       [
38418         "Austria (Österreich)",
38419         "at",
38420         "43"
38421       ],
38422       [
38423         "Azerbaijan (Azərbaycan)",
38424         "az",
38425         "994"
38426       ],
38427       [
38428         "Bahamas",
38429         "bs",
38430         "1242"
38431       ],
38432       [
38433         "Bahrain (‫البحرين‬‎)",
38434         "bh",
38435         "973"
38436       ],
38437       [
38438         "Bangladesh (বাংলাদেশ)",
38439         "bd",
38440         "880"
38441       ],
38442       [
38443         "Barbados",
38444         "bb",
38445         "1246"
38446       ],
38447       [
38448         "Belarus (Беларусь)",
38449         "by",
38450         "375"
38451       ],
38452       [
38453         "Belgium (België)",
38454         "be",
38455         "32"
38456       ],
38457       [
38458         "Belize",
38459         "bz",
38460         "501"
38461       ],
38462       [
38463         "Benin (Bénin)",
38464         "bj",
38465         "229"
38466       ],
38467       [
38468         "Bermuda",
38469         "bm",
38470         "1441"
38471       ],
38472       [
38473         "Bhutan (འབྲུག)",
38474         "bt",
38475         "975"
38476       ],
38477       [
38478         "Bolivia",
38479         "bo",
38480         "591"
38481       ],
38482       [
38483         "Bosnia and Herzegovina (Босна и Херцеговина)",
38484         "ba",
38485         "387"
38486       ],
38487       [
38488         "Botswana",
38489         "bw",
38490         "267"
38491       ],
38492       [
38493         "Brazil (Brasil)",
38494         "br",
38495         "55"
38496       ],
38497       [
38498         "British Indian Ocean Territory",
38499         "io",
38500         "246"
38501       ],
38502       [
38503         "British Virgin Islands",
38504         "vg",
38505         "1284"
38506       ],
38507       [
38508         "Brunei",
38509         "bn",
38510         "673"
38511       ],
38512       [
38513         "Bulgaria (България)",
38514         "bg",
38515         "359"
38516       ],
38517       [
38518         "Burkina Faso",
38519         "bf",
38520         "226"
38521       ],
38522       [
38523         "Burundi (Uburundi)",
38524         "bi",
38525         "257"
38526       ],
38527       [
38528         "Cambodia (កម្ពុជា)",
38529         "kh",
38530         "855"
38531       ],
38532       [
38533         "Cameroon (Cameroun)",
38534         "cm",
38535         "237"
38536       ],
38537       [
38538         "Canada",
38539         "ca",
38540         "1",
38541         1,
38542         ["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"]
38543       ],
38544       [
38545         "Cape Verde (Kabu Verdi)",
38546         "cv",
38547         "238"
38548       ],
38549       [
38550         "Caribbean Netherlands",
38551         "bq",
38552         "599",
38553         1
38554       ],
38555       [
38556         "Cayman Islands",
38557         "ky",
38558         "1345"
38559       ],
38560       [
38561         "Central African Republic (République centrafricaine)",
38562         "cf",
38563         "236"
38564       ],
38565       [
38566         "Chad (Tchad)",
38567         "td",
38568         "235"
38569       ],
38570       [
38571         "Chile",
38572         "cl",
38573         "56"
38574       ],
38575       [
38576         "China (中国)",
38577         "cn",
38578         "86"
38579       ],
38580       [
38581         "Christmas Island",
38582         "cx",
38583         "61",
38584         2
38585       ],
38586       [
38587         "Cocos (Keeling) Islands",
38588         "cc",
38589         "61",
38590         1
38591       ],
38592       [
38593         "Colombia",
38594         "co",
38595         "57"
38596       ],
38597       [
38598         "Comoros (‫جزر القمر‬‎)",
38599         "km",
38600         "269"
38601       ],
38602       [
38603         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38604         "cd",
38605         "243"
38606       ],
38607       [
38608         "Congo (Republic) (Congo-Brazzaville)",
38609         "cg",
38610         "242"
38611       ],
38612       [
38613         "Cook Islands",
38614         "ck",
38615         "682"
38616       ],
38617       [
38618         "Costa Rica",
38619         "cr",
38620         "506"
38621       ],
38622       [
38623         "Côte d’Ivoire",
38624         "ci",
38625         "225"
38626       ],
38627       [
38628         "Croatia (Hrvatska)",
38629         "hr",
38630         "385"
38631       ],
38632       [
38633         "Cuba",
38634         "cu",
38635         "53"
38636       ],
38637       [
38638         "Curaçao",
38639         "cw",
38640         "599",
38641         0
38642       ],
38643       [
38644         "Cyprus (Κύπρος)",
38645         "cy",
38646         "357"
38647       ],
38648       [
38649         "Czech Republic (Česká republika)",
38650         "cz",
38651         "420"
38652       ],
38653       [
38654         "Denmark (Danmark)",
38655         "dk",
38656         "45"
38657       ],
38658       [
38659         "Djibouti",
38660         "dj",
38661         "253"
38662       ],
38663       [
38664         "Dominica",
38665         "dm",
38666         "1767"
38667       ],
38668       [
38669         "Dominican Republic (República Dominicana)",
38670         "do",
38671         "1",
38672         2,
38673         ["809", "829", "849"]
38674       ],
38675       [
38676         "Ecuador",
38677         "ec",
38678         "593"
38679       ],
38680       [
38681         "Egypt (‫مصر‬‎)",
38682         "eg",
38683         "20"
38684       ],
38685       [
38686         "El Salvador",
38687         "sv",
38688         "503"
38689       ],
38690       [
38691         "Equatorial Guinea (Guinea Ecuatorial)",
38692         "gq",
38693         "240"
38694       ],
38695       [
38696         "Eritrea",
38697         "er",
38698         "291"
38699       ],
38700       [
38701         "Estonia (Eesti)",
38702         "ee",
38703         "372"
38704       ],
38705       [
38706         "Ethiopia",
38707         "et",
38708         "251"
38709       ],
38710       [
38711         "Falkland Islands (Islas Malvinas)",
38712         "fk",
38713         "500"
38714       ],
38715       [
38716         "Faroe Islands (Føroyar)",
38717         "fo",
38718         "298"
38719       ],
38720       [
38721         "Fiji",
38722         "fj",
38723         "679"
38724       ],
38725       [
38726         "Finland (Suomi)",
38727         "fi",
38728         "358",
38729         0
38730       ],
38731       [
38732         "France",
38733         "fr",
38734         "33"
38735       ],
38736       [
38737         "French Guiana (Guyane française)",
38738         "gf",
38739         "594"
38740       ],
38741       [
38742         "French Polynesia (Polynésie française)",
38743         "pf",
38744         "689"
38745       ],
38746       [
38747         "Gabon",
38748         "ga",
38749         "241"
38750       ],
38751       [
38752         "Gambia",
38753         "gm",
38754         "220"
38755       ],
38756       [
38757         "Georgia (საქართველო)",
38758         "ge",
38759         "995"
38760       ],
38761       [
38762         "Germany (Deutschland)",
38763         "de",
38764         "49"
38765       ],
38766       [
38767         "Ghana (Gaana)",
38768         "gh",
38769         "233"
38770       ],
38771       [
38772         "Gibraltar",
38773         "gi",
38774         "350"
38775       ],
38776       [
38777         "Greece (Ελλάδα)",
38778         "gr",
38779         "30"
38780       ],
38781       [
38782         "Greenland (Kalaallit Nunaat)",
38783         "gl",
38784         "299"
38785       ],
38786       [
38787         "Grenada",
38788         "gd",
38789         "1473"
38790       ],
38791       [
38792         "Guadeloupe",
38793         "gp",
38794         "590",
38795         0
38796       ],
38797       [
38798         "Guam",
38799         "gu",
38800         "1671"
38801       ],
38802       [
38803         "Guatemala",
38804         "gt",
38805         "502"
38806       ],
38807       [
38808         "Guernsey",
38809         "gg",
38810         "44",
38811         1
38812       ],
38813       [
38814         "Guinea (Guinée)",
38815         "gn",
38816         "224"
38817       ],
38818       [
38819         "Guinea-Bissau (Guiné Bissau)",
38820         "gw",
38821         "245"
38822       ],
38823       [
38824         "Guyana",
38825         "gy",
38826         "592"
38827       ],
38828       [
38829         "Haiti",
38830         "ht",
38831         "509"
38832       ],
38833       [
38834         "Honduras",
38835         "hn",
38836         "504"
38837       ],
38838       [
38839         "Hong Kong (香港)",
38840         "hk",
38841         "852"
38842       ],
38843       [
38844         "Hungary (Magyarország)",
38845         "hu",
38846         "36"
38847       ],
38848       [
38849         "Iceland (Ísland)",
38850         "is",
38851         "354"
38852       ],
38853       [
38854         "India (भारत)",
38855         "in",
38856         "91"
38857       ],
38858       [
38859         "Indonesia",
38860         "id",
38861         "62"
38862       ],
38863       [
38864         "Iran (‫ایران‬‎)",
38865         "ir",
38866         "98"
38867       ],
38868       [
38869         "Iraq (‫العراق‬‎)",
38870         "iq",
38871         "964"
38872       ],
38873       [
38874         "Ireland",
38875         "ie",
38876         "353"
38877       ],
38878       [
38879         "Isle of Man",
38880         "im",
38881         "44",
38882         2
38883       ],
38884       [
38885         "Israel (‫ישראל‬‎)",
38886         "il",
38887         "972"
38888       ],
38889       [
38890         "Italy (Italia)",
38891         "it",
38892         "39",
38893         0
38894       ],
38895       [
38896         "Jamaica",
38897         "jm",
38898         "1876"
38899       ],
38900       [
38901         "Japan (日本)",
38902         "jp",
38903         "81"
38904       ],
38905       [
38906         "Jersey",
38907         "je",
38908         "44",
38909         3
38910       ],
38911       [
38912         "Jordan (‫الأردن‬‎)",
38913         "jo",
38914         "962"
38915       ],
38916       [
38917         "Kazakhstan (Казахстан)",
38918         "kz",
38919         "7",
38920         1
38921       ],
38922       [
38923         "Kenya",
38924         "ke",
38925         "254"
38926       ],
38927       [
38928         "Kiribati",
38929         "ki",
38930         "686"
38931       ],
38932       [
38933         "Kosovo",
38934         "xk",
38935         "383"
38936       ],
38937       [
38938         "Kuwait (‫الكويت‬‎)",
38939         "kw",
38940         "965"
38941       ],
38942       [
38943         "Kyrgyzstan (Кыргызстан)",
38944         "kg",
38945         "996"
38946       ],
38947       [
38948         "Laos (ລາວ)",
38949         "la",
38950         "856"
38951       ],
38952       [
38953         "Latvia (Latvija)",
38954         "lv",
38955         "371"
38956       ],
38957       [
38958         "Lebanon (‫لبنان‬‎)",
38959         "lb",
38960         "961"
38961       ],
38962       [
38963         "Lesotho",
38964         "ls",
38965         "266"
38966       ],
38967       [
38968         "Liberia",
38969         "lr",
38970         "231"
38971       ],
38972       [
38973         "Libya (‫ليبيا‬‎)",
38974         "ly",
38975         "218"
38976       ],
38977       [
38978         "Liechtenstein",
38979         "li",
38980         "423"
38981       ],
38982       [
38983         "Lithuania (Lietuva)",
38984         "lt",
38985         "370"
38986       ],
38987       [
38988         "Luxembourg",
38989         "lu",
38990         "352"
38991       ],
38992       [
38993         "Macau (澳門)",
38994         "mo",
38995         "853"
38996       ],
38997       [
38998         "Macedonia (FYROM) (Македонија)",
38999         "mk",
39000         "389"
39001       ],
39002       [
39003         "Madagascar (Madagasikara)",
39004         "mg",
39005         "261"
39006       ],
39007       [
39008         "Malawi",
39009         "mw",
39010         "265"
39011       ],
39012       [
39013         "Malaysia",
39014         "my",
39015         "60"
39016       ],
39017       [
39018         "Maldives",
39019         "mv",
39020         "960"
39021       ],
39022       [
39023         "Mali",
39024         "ml",
39025         "223"
39026       ],
39027       [
39028         "Malta",
39029         "mt",
39030         "356"
39031       ],
39032       [
39033         "Marshall Islands",
39034         "mh",
39035         "692"
39036       ],
39037       [
39038         "Martinique",
39039         "mq",
39040         "596"
39041       ],
39042       [
39043         "Mauritania (‫موريتانيا‬‎)",
39044         "mr",
39045         "222"
39046       ],
39047       [
39048         "Mauritius (Moris)",
39049         "mu",
39050         "230"
39051       ],
39052       [
39053         "Mayotte",
39054         "yt",
39055         "262",
39056         1
39057       ],
39058       [
39059         "Mexico (México)",
39060         "mx",
39061         "52"
39062       ],
39063       [
39064         "Micronesia",
39065         "fm",
39066         "691"
39067       ],
39068       [
39069         "Moldova (Republica Moldova)",
39070         "md",
39071         "373"
39072       ],
39073       [
39074         "Monaco",
39075         "mc",
39076         "377"
39077       ],
39078       [
39079         "Mongolia (Монгол)",
39080         "mn",
39081         "976"
39082       ],
39083       [
39084         "Montenegro (Crna Gora)",
39085         "me",
39086         "382"
39087       ],
39088       [
39089         "Montserrat",
39090         "ms",
39091         "1664"
39092       ],
39093       [
39094         "Morocco (‫المغرب‬‎)",
39095         "ma",
39096         "212",
39097         0
39098       ],
39099       [
39100         "Mozambique (Moçambique)",
39101         "mz",
39102         "258"
39103       ],
39104       [
39105         "Myanmar (Burma) (မြန်မာ)",
39106         "mm",
39107         "95"
39108       ],
39109       [
39110         "Namibia (Namibië)",
39111         "na",
39112         "264"
39113       ],
39114       [
39115         "Nauru",
39116         "nr",
39117         "674"
39118       ],
39119       [
39120         "Nepal (नेपाल)",
39121         "np",
39122         "977"
39123       ],
39124       [
39125         "Netherlands (Nederland)",
39126         "nl",
39127         "31"
39128       ],
39129       [
39130         "New Caledonia (Nouvelle-Calédonie)",
39131         "nc",
39132         "687"
39133       ],
39134       [
39135         "New Zealand",
39136         "nz",
39137         "64"
39138       ],
39139       [
39140         "Nicaragua",
39141         "ni",
39142         "505"
39143       ],
39144       [
39145         "Niger (Nijar)",
39146         "ne",
39147         "227"
39148       ],
39149       [
39150         "Nigeria",
39151         "ng",
39152         "234"
39153       ],
39154       [
39155         "Niue",
39156         "nu",
39157         "683"
39158       ],
39159       [
39160         "Norfolk Island",
39161         "nf",
39162         "672"
39163       ],
39164       [
39165         "North Korea (조선 민주주의 인민 공화국)",
39166         "kp",
39167         "850"
39168       ],
39169       [
39170         "Northern Mariana Islands",
39171         "mp",
39172         "1670"
39173       ],
39174       [
39175         "Norway (Norge)",
39176         "no",
39177         "47",
39178         0
39179       ],
39180       [
39181         "Oman (‫عُمان‬‎)",
39182         "om",
39183         "968"
39184       ],
39185       [
39186         "Pakistan (‫پاکستان‬‎)",
39187         "pk",
39188         "92"
39189       ],
39190       [
39191         "Palau",
39192         "pw",
39193         "680"
39194       ],
39195       [
39196         "Palestine (‫فلسطين‬‎)",
39197         "ps",
39198         "970"
39199       ],
39200       [
39201         "Panama (Panamá)",
39202         "pa",
39203         "507"
39204       ],
39205       [
39206         "Papua New Guinea",
39207         "pg",
39208         "675"
39209       ],
39210       [
39211         "Paraguay",
39212         "py",
39213         "595"
39214       ],
39215       [
39216         "Peru (Perú)",
39217         "pe",
39218         "51"
39219       ],
39220       [
39221         "Philippines",
39222         "ph",
39223         "63"
39224       ],
39225       [
39226         "Poland (Polska)",
39227         "pl",
39228         "48"
39229       ],
39230       [
39231         "Portugal",
39232         "pt",
39233         "351"
39234       ],
39235       [
39236         "Puerto Rico",
39237         "pr",
39238         "1",
39239         3,
39240         ["787", "939"]
39241       ],
39242       [
39243         "Qatar (‫قطر‬‎)",
39244         "qa",
39245         "974"
39246       ],
39247       [
39248         "Réunion (La Réunion)",
39249         "re",
39250         "262",
39251         0
39252       ],
39253       [
39254         "Romania (România)",
39255         "ro",
39256         "40"
39257       ],
39258       [
39259         "Russia (Россия)",
39260         "ru",
39261         "7",
39262         0
39263       ],
39264       [
39265         "Rwanda",
39266         "rw",
39267         "250"
39268       ],
39269       [
39270         "Saint Barthélemy",
39271         "bl",
39272         "590",
39273         1
39274       ],
39275       [
39276         "Saint Helena",
39277         "sh",
39278         "290"
39279       ],
39280       [
39281         "Saint Kitts and Nevis",
39282         "kn",
39283         "1869"
39284       ],
39285       [
39286         "Saint Lucia",
39287         "lc",
39288         "1758"
39289       ],
39290       [
39291         "Saint Martin (Saint-Martin (partie française))",
39292         "mf",
39293         "590",
39294         2
39295       ],
39296       [
39297         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39298         "pm",
39299         "508"
39300       ],
39301       [
39302         "Saint Vincent and the Grenadines",
39303         "vc",
39304         "1784"
39305       ],
39306       [
39307         "Samoa",
39308         "ws",
39309         "685"
39310       ],
39311       [
39312         "San Marino",
39313         "sm",
39314         "378"
39315       ],
39316       [
39317         "São Tomé and Príncipe (São Tomé e Príncipe)",
39318         "st",
39319         "239"
39320       ],
39321       [
39322         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39323         "sa",
39324         "966"
39325       ],
39326       [
39327         "Senegal (Sénégal)",
39328         "sn",
39329         "221"
39330       ],
39331       [
39332         "Serbia (Србија)",
39333         "rs",
39334         "381"
39335       ],
39336       [
39337         "Seychelles",
39338         "sc",
39339         "248"
39340       ],
39341       [
39342         "Sierra Leone",
39343         "sl",
39344         "232"
39345       ],
39346       [
39347         "Singapore",
39348         "sg",
39349         "65"
39350       ],
39351       [
39352         "Sint Maarten",
39353         "sx",
39354         "1721"
39355       ],
39356       [
39357         "Slovakia (Slovensko)",
39358         "sk",
39359         "421"
39360       ],
39361       [
39362         "Slovenia (Slovenija)",
39363         "si",
39364         "386"
39365       ],
39366       [
39367         "Solomon Islands",
39368         "sb",
39369         "677"
39370       ],
39371       [
39372         "Somalia (Soomaaliya)",
39373         "so",
39374         "252"
39375       ],
39376       [
39377         "South Africa",
39378         "za",
39379         "27"
39380       ],
39381       [
39382         "South Korea (대한민국)",
39383         "kr",
39384         "82"
39385       ],
39386       [
39387         "South Sudan (‫جنوب السودان‬‎)",
39388         "ss",
39389         "211"
39390       ],
39391       [
39392         "Spain (España)",
39393         "es",
39394         "34"
39395       ],
39396       [
39397         "Sri Lanka (ශ්‍රී ලංකාව)",
39398         "lk",
39399         "94"
39400       ],
39401       [
39402         "Sudan (‫السودان‬‎)",
39403         "sd",
39404         "249"
39405       ],
39406       [
39407         "Suriname",
39408         "sr",
39409         "597"
39410       ],
39411       [
39412         "Svalbard and Jan Mayen",
39413         "sj",
39414         "47",
39415         1
39416       ],
39417       [
39418         "Swaziland",
39419         "sz",
39420         "268"
39421       ],
39422       [
39423         "Sweden (Sverige)",
39424         "se",
39425         "46"
39426       ],
39427       [
39428         "Switzerland (Schweiz)",
39429         "ch",
39430         "41"
39431       ],
39432       [
39433         "Syria (‫سوريا‬‎)",
39434         "sy",
39435         "963"
39436       ],
39437       [
39438         "Taiwan (台灣)",
39439         "tw",
39440         "886"
39441       ],
39442       [
39443         "Tajikistan",
39444         "tj",
39445         "992"
39446       ],
39447       [
39448         "Tanzania",
39449         "tz",
39450         "255"
39451       ],
39452       [
39453         "Thailand (ไทย)",
39454         "th",
39455         "66"
39456       ],
39457       [
39458         "Timor-Leste",
39459         "tl",
39460         "670"
39461       ],
39462       [
39463         "Togo",
39464         "tg",
39465         "228"
39466       ],
39467       [
39468         "Tokelau",
39469         "tk",
39470         "690"
39471       ],
39472       [
39473         "Tonga",
39474         "to",
39475         "676"
39476       ],
39477       [
39478         "Trinidad and Tobago",
39479         "tt",
39480         "1868"
39481       ],
39482       [
39483         "Tunisia (‫تونس‬‎)",
39484         "tn",
39485         "216"
39486       ],
39487       [
39488         "Turkey (Türkiye)",
39489         "tr",
39490         "90"
39491       ],
39492       [
39493         "Turkmenistan",
39494         "tm",
39495         "993"
39496       ],
39497       [
39498         "Turks and Caicos Islands",
39499         "tc",
39500         "1649"
39501       ],
39502       [
39503         "Tuvalu",
39504         "tv",
39505         "688"
39506       ],
39507       [
39508         "U.S. Virgin Islands",
39509         "vi",
39510         "1340"
39511       ],
39512       [
39513         "Uganda",
39514         "ug",
39515         "256"
39516       ],
39517       [
39518         "Ukraine (Україна)",
39519         "ua",
39520         "380"
39521       ],
39522       [
39523         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39524         "ae",
39525         "971"
39526       ],
39527       [
39528         "United Kingdom",
39529         "gb",
39530         "44",
39531         0
39532       ],
39533       [
39534         "United States",
39535         "us",
39536         "1",
39537         0
39538       ],
39539       [
39540         "Uruguay",
39541         "uy",
39542         "598"
39543       ],
39544       [
39545         "Uzbekistan (Oʻzbekiston)",
39546         "uz",
39547         "998"
39548       ],
39549       [
39550         "Vanuatu",
39551         "vu",
39552         "678"
39553       ],
39554       [
39555         "Vatican City (Città del Vaticano)",
39556         "va",
39557         "39",
39558         1
39559       ],
39560       [
39561         "Venezuela",
39562         "ve",
39563         "58"
39564       ],
39565       [
39566         "Vietnam (Việt Nam)",
39567         "vn",
39568         "84"
39569       ],
39570       [
39571         "Wallis and Futuna (Wallis-et-Futuna)",
39572         "wf",
39573         "681"
39574       ],
39575       [
39576         "Western Sahara (‫الصحراء الغربية‬‎)",
39577         "eh",
39578         "212",
39579         1
39580       ],
39581       [
39582         "Yemen (‫اليمن‬‎)",
39583         "ye",
39584         "967"
39585       ],
39586       [
39587         "Zambia",
39588         "zm",
39589         "260"
39590       ],
39591       [
39592         "Zimbabwe",
39593         "zw",
39594         "263"
39595       ],
39596       [
39597         "Åland Islands",
39598         "ax",
39599         "358",
39600         1
39601       ]
39602   ];
39603   
39604   return d;
39605 }/**
39606 *    This script refer to:
39607 *    Title: International Telephone Input
39608 *    Author: Jack O'Connor
39609 *    Code version:  v12.1.12
39610 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39611 **/
39612
39613 /**
39614  * @class Roo.bootstrap.PhoneInput
39615  * @extends Roo.bootstrap.TriggerField
39616  * An input with International dial-code selection
39617  
39618  * @cfg {String} defaultDialCode default '+852'
39619  * @cfg {Array} preferedCountries default []
39620   
39621  * @constructor
39622  * Create a new PhoneInput.
39623  * @param {Object} config Configuration options
39624  */
39625
39626 Roo.bootstrap.PhoneInput = function(config) {
39627     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39628 };
39629
39630 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39631         
39632         listWidth: undefined,
39633         
39634         selectedClass: 'active',
39635         
39636         invalidClass : "has-warning",
39637         
39638         validClass: 'has-success',
39639         
39640         allowed: '0123456789',
39641         
39642         /**
39643          * @cfg {String} defaultDialCode The default dial code when initializing the input
39644          */
39645         defaultDialCode: '+852',
39646         
39647         /**
39648          * @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
39649          */
39650         preferedCountries: false,
39651         
39652         getAutoCreate : function()
39653         {
39654             var data = Roo.bootstrap.PhoneInputData();
39655             var align = this.labelAlign || this.parentLabelAlign();
39656             var id = Roo.id();
39657             
39658             this.allCountries = [];
39659             this.dialCodeMapping = [];
39660             
39661             for (var i = 0; i < data.length; i++) {
39662               var c = data[i];
39663               this.allCountries[i] = {
39664                 name: c[0],
39665                 iso2: c[1],
39666                 dialCode: c[2],
39667                 priority: c[3] || 0,
39668                 areaCodes: c[4] || null
39669               };
39670               this.dialCodeMapping[c[2]] = {
39671                   name: c[0],
39672                   iso2: c[1],
39673                   priority: c[3] || 0,
39674                   areaCodes: c[4] || null
39675               };
39676             }
39677             
39678             var cfg = {
39679                 cls: 'form-group',
39680                 cn: []
39681             };
39682             
39683             var input =  {
39684                 tag: 'input',
39685                 id : id,
39686                 cls : 'form-control tel-input',
39687                 autocomplete: 'new-password'
39688             };
39689             
39690             var hiddenInput = {
39691                 tag: 'input',
39692                 type: 'hidden',
39693                 cls: 'hidden-tel-input'
39694             };
39695             
39696             if (this.name) {
39697                 hiddenInput.name = this.name;
39698             }
39699             
39700             if (this.disabled) {
39701                 input.disabled = true;
39702             }
39703             
39704             var flag_container = {
39705                 tag: 'div',
39706                 cls: 'flag-box',
39707                 cn: [
39708                     {
39709                         tag: 'div',
39710                         cls: 'flag'
39711                     },
39712                     {
39713                         tag: 'div',
39714                         cls: 'caret'
39715                     }
39716                 ]
39717             };
39718             
39719             var box = {
39720                 tag: 'div',
39721                 cls: this.hasFeedback ? 'has-feedback' : '',
39722                 cn: [
39723                     hiddenInput,
39724                     input,
39725                     {
39726                         tag: 'input',
39727                         cls: 'dial-code-holder',
39728                         disabled: true
39729                     }
39730                 ]
39731             };
39732             
39733             var container = {
39734                 cls: 'roo-select2-container input-group',
39735                 cn: [
39736                     flag_container,
39737                     box
39738                 ]
39739             };
39740             
39741             if (this.fieldLabel.length) {
39742                 var indicator = {
39743                     tag: 'i',
39744                     tooltip: 'This field is required'
39745                 };
39746                 
39747                 var label = {
39748                     tag: 'label',
39749                     'for':  id,
39750                     cls: 'control-label',
39751                     cn: []
39752                 };
39753                 
39754                 var label_text = {
39755                     tag: 'span',
39756                     html: this.fieldLabel
39757                 };
39758                 
39759                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39760                 label.cn = [
39761                     indicator,
39762                     label_text
39763                 ];
39764                 
39765                 if(this.indicatorpos == 'right') {
39766                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39767                     label.cn = [
39768                         label_text,
39769                         indicator
39770                     ];
39771                 }
39772                 
39773                 if(align == 'left') {
39774                     container = {
39775                         tag: 'div',
39776                         cn: [
39777                             container
39778                         ]
39779                     };
39780                     
39781                     if(this.labelWidth > 12){
39782                         label.style = "width: " + this.labelWidth + 'px';
39783                     }
39784                     if(this.labelWidth < 13 && this.labelmd == 0){
39785                         this.labelmd = this.labelWidth;
39786                     }
39787                     if(this.labellg > 0){
39788                         label.cls += ' col-lg-' + this.labellg;
39789                         input.cls += ' col-lg-' + (12 - this.labellg);
39790                     }
39791                     if(this.labelmd > 0){
39792                         label.cls += ' col-md-' + this.labelmd;
39793                         container.cls += ' col-md-' + (12 - this.labelmd);
39794                     }
39795                     if(this.labelsm > 0){
39796                         label.cls += ' col-sm-' + this.labelsm;
39797                         container.cls += ' col-sm-' + (12 - this.labelsm);
39798                     }
39799                     if(this.labelxs > 0){
39800                         label.cls += ' col-xs-' + this.labelxs;
39801                         container.cls += ' col-xs-' + (12 - this.labelxs);
39802                     }
39803                 }
39804             }
39805             
39806             cfg.cn = [
39807                 label,
39808                 container
39809             ];
39810             
39811             var settings = this;
39812             
39813             ['xs','sm','md','lg'].map(function(size){
39814                 if (settings[size]) {
39815                     cfg.cls += ' col-' + size + '-' + settings[size];
39816                 }
39817             });
39818             
39819             this.store = new Roo.data.Store({
39820                 proxy : new Roo.data.MemoryProxy({}),
39821                 reader : new Roo.data.JsonReader({
39822                     fields : [
39823                         {
39824                             'name' : 'name',
39825                             'type' : 'string'
39826                         },
39827                         {
39828                             'name' : 'iso2',
39829                             'type' : 'string'
39830                         },
39831                         {
39832                             'name' : 'dialCode',
39833                             'type' : 'string'
39834                         },
39835                         {
39836                             'name' : 'priority',
39837                             'type' : 'string'
39838                         },
39839                         {
39840                             'name' : 'areaCodes',
39841                             'type' : 'string'
39842                         }
39843                     ]
39844                 })
39845             });
39846             
39847             if(!this.preferedCountries) {
39848                 this.preferedCountries = [
39849                     'hk',
39850                     'gb',
39851                     'us'
39852                 ];
39853             }
39854             
39855             var p = this.preferedCountries.reverse();
39856             
39857             if(p) {
39858                 for (var i = 0; i < p.length; i++) {
39859                     for (var j = 0; j < this.allCountries.length; j++) {
39860                         if(this.allCountries[j].iso2 == p[i]) {
39861                             var t = this.allCountries[j];
39862                             this.allCountries.splice(j,1);
39863                             this.allCountries.unshift(t);
39864                         }
39865                     } 
39866                 }
39867             }
39868             
39869             this.store.proxy.data = {
39870                 success: true,
39871                 data: this.allCountries
39872             };
39873             
39874             return cfg;
39875         },
39876         
39877         initEvents : function()
39878         {
39879             this.createList();
39880             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39881             
39882             this.indicator = this.indicatorEl();
39883             this.flag = this.flagEl();
39884             this.dialCodeHolder = this.dialCodeHolderEl();
39885             
39886             this.trigger = this.el.select('div.flag-box',true).first();
39887             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39888             
39889             var _this = this;
39890             
39891             (function(){
39892                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39893                 _this.list.setWidth(lw);
39894             }).defer(100);
39895             
39896             this.list.on('mouseover', this.onViewOver, this);
39897             this.list.on('mousemove', this.onViewMove, this);
39898             this.inputEl().on("keyup", this.onKeyUp, this);
39899             
39900             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39901
39902             this.view = new Roo.View(this.list, this.tpl, {
39903                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39904             });
39905             
39906             this.view.on('click', this.onViewClick, this);
39907             this.setValue(this.defaultDialCode);
39908         },
39909         
39910         onTriggerClick : function(e)
39911         {
39912             Roo.log('trigger click');
39913             if(this.disabled){
39914                 return;
39915             }
39916             
39917             if(this.isExpanded()){
39918                 this.collapse();
39919                 this.hasFocus = false;
39920             }else {
39921                 this.store.load({});
39922                 this.hasFocus = true;
39923                 this.expand();
39924             }
39925         },
39926         
39927         isExpanded : function()
39928         {
39929             return this.list.isVisible();
39930         },
39931         
39932         collapse : function()
39933         {
39934             if(!this.isExpanded()){
39935                 return;
39936             }
39937             this.list.hide();
39938             Roo.get(document).un('mousedown', this.collapseIf, this);
39939             Roo.get(document).un('mousewheel', this.collapseIf, this);
39940             this.fireEvent('collapse', this);
39941             this.validate();
39942         },
39943         
39944         expand : function()
39945         {
39946             Roo.log('expand');
39947
39948             if(this.isExpanded() || !this.hasFocus){
39949                 return;
39950             }
39951             
39952             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39953             this.list.setWidth(lw);
39954             
39955             this.list.show();
39956             this.restrictHeight();
39957             
39958             Roo.get(document).on('mousedown', this.collapseIf, this);
39959             Roo.get(document).on('mousewheel', this.collapseIf, this);
39960             
39961             this.fireEvent('expand', this);
39962         },
39963         
39964         restrictHeight : function()
39965         {
39966             this.list.alignTo(this.inputEl(), this.listAlign);
39967             this.list.alignTo(this.inputEl(), this.listAlign);
39968         },
39969         
39970         onViewOver : function(e, t)
39971         {
39972             if(this.inKeyMode){
39973                 return;
39974             }
39975             var item = this.view.findItemFromChild(t);
39976             
39977             if(item){
39978                 var index = this.view.indexOf(item);
39979                 this.select(index, false);
39980             }
39981         },
39982
39983         // private
39984         onViewClick : function(view, doFocus, el, e)
39985         {
39986             var index = this.view.getSelectedIndexes()[0];
39987             
39988             var r = this.store.getAt(index);
39989             
39990             if(r){
39991                 this.onSelect(r, index);
39992             }
39993             if(doFocus !== false && !this.blockFocus){
39994                 this.inputEl().focus();
39995             }
39996         },
39997         
39998         onViewMove : function(e, t)
39999         {
40000             this.inKeyMode = false;
40001         },
40002         
40003         select : function(index, scrollIntoView)
40004         {
40005             this.selectedIndex = index;
40006             this.view.select(index);
40007             if(scrollIntoView !== false){
40008                 var el = this.view.getNode(index);
40009                 if(el){
40010                     this.list.scrollChildIntoView(el, false);
40011                 }
40012             }
40013         },
40014         
40015         createList : function()
40016         {
40017             this.list = Roo.get(document.body).createChild({
40018                 tag: 'ul',
40019                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40020                 style: 'display:none'
40021             });
40022             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
40023         },
40024         
40025         collapseIf : function(e)
40026         {
40027             var in_combo  = e.within(this.el);
40028             var in_list =  e.within(this.list);
40029             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40030             
40031             if (in_combo || in_list || is_list) {
40032                 return;
40033             }
40034             this.collapse();
40035         },
40036         
40037         onSelect : function(record, index)
40038         {
40039             if(this.fireEvent('beforeselect', this, record, index) !== false){
40040                 
40041                 this.setFlagClass(record.data.iso2);
40042                 this.setDialCode(record.data.dialCode);
40043                 this.hasFocus = false;
40044                 this.collapse();
40045                 this.fireEvent('select', this, record, index);
40046             }
40047         },
40048         
40049         flagEl : function()
40050         {
40051             var flag = this.el.select('div.flag',true).first();
40052             if(!flag){
40053                 return false;
40054             }
40055             return flag;
40056         },
40057         
40058         dialCodeHolderEl : function()
40059         {
40060             var d = this.el.select('input.dial-code-holder',true).first();
40061             if(!d){
40062                 return false;
40063             }
40064             return d;
40065         },
40066         
40067         setDialCode : function(v)
40068         {
40069             this.dialCodeHolder.dom.value = '+'+v;
40070         },
40071         
40072         setFlagClass : function(n)
40073         {
40074             this.flag.dom.className = 'flag '+n;
40075         },
40076         
40077         getValue : function()
40078         {
40079             var v = this.inputEl().getValue();
40080             if(this.dialCodeHolder) {
40081                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40082             }
40083             return v;
40084         },
40085         
40086         setValue : function(v)
40087         {
40088             var d = this.getDialCode(v);
40089             
40090             //invalid dial code
40091             if(v.length == 0 || !d || d.length == 0) {
40092                 if(this.rendered){
40093                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40094                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40095                 }
40096                 return;
40097             }
40098             
40099             //valid dial code
40100             this.setFlagClass(this.dialCodeMapping[d].iso2);
40101             this.setDialCode(d);
40102             this.inputEl().dom.value = v.replace('+'+d,'');
40103             this.hiddenEl().dom.value = this.getValue();
40104             
40105             this.validate();
40106         },
40107         
40108         getDialCode : function(v = '')
40109         {
40110             if (v.length == 0) {
40111                 return this.dialCodeHolder.dom.value;
40112             }
40113             
40114             var dialCode = "";
40115             if (v.charAt(0) != "+") {
40116                 return false;
40117             }
40118             var numericChars = "";
40119             for (var i = 1; i < v.length; i++) {
40120               var c = v.charAt(i);
40121               if (!isNaN(c)) {
40122                 numericChars += c;
40123                 if (this.dialCodeMapping[numericChars]) {
40124                   dialCode = v.substr(1, i);
40125                 }
40126                 if (numericChars.length == 4) {
40127                   break;
40128                 }
40129               }
40130             }
40131             return dialCode;
40132         },
40133         
40134         reset : function()
40135         {
40136             this.setValue(this.defaultDialCode);
40137             this.validate();
40138         },
40139         
40140         hiddenEl : function()
40141         {
40142             return this.el.select('input.hidden-tel-input',true).first();
40143         },
40144         
40145         onKeyUp : function(e){
40146             
40147             var k = e.getKey();
40148             var c = e.getCharCode();
40149             
40150             if(
40151                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40152                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40153             ){
40154                 e.stopEvent();
40155             }
40156             
40157             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40158             //     return;
40159             // }
40160             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40161                 e.stopEvent();
40162             }
40163             
40164             this.setValue(this.getValue());
40165         }
40166         
40167 });
40168 /**
40169  * @class Roo.bootstrap.MoneyField
40170  * @extends Roo.bootstrap.ComboBox
40171  * Bootstrap MoneyField class
40172  * 
40173  * @constructor
40174  * Create a new MoneyField.
40175  * @param {Object} config Configuration options
40176  */
40177
40178 Roo.bootstrap.MoneyField = function(config) {
40179     
40180     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40181     
40182 };
40183
40184 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40185     
40186     /**
40187      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40188      */
40189     allowDecimals : true,
40190     /**
40191      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40192      */
40193     decimalSeparator : ".",
40194     /**
40195      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40196      */
40197     decimalPrecision : 0,
40198     /**
40199      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40200      */
40201     allowNegative : true,
40202     /**
40203      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40204      */
40205     minValue : Number.NEGATIVE_INFINITY,
40206     /**
40207      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40208      */
40209     maxValue : Number.MAX_VALUE,
40210     /**
40211      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40212      */
40213     minText : "The minimum value for this field is {0}",
40214     /**
40215      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40216      */
40217     maxText : "The maximum value for this field is {0}",
40218     /**
40219      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40220      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40221      */
40222     nanText : "{0} is not a valid number",
40223     /**
40224      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40225      */
40226     castInt : true,
40227     /**
40228      * @cfg {String} defaults currency of the MoneyField
40229      * value should be in lkey
40230      */
40231     defaultCurrency : false,
40232     /**
40233      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40234      */
40235     thousandsDelimiter : false,
40236     
40237     
40238     inputlg : 9,
40239     inputmd : 9,
40240     inputsm : 9,
40241     inputxs : 6,
40242     
40243     store : false,
40244     
40245     getAutoCreate : function()
40246     {
40247         var align = this.labelAlign || this.parentLabelAlign();
40248         
40249         var id = Roo.id();
40250
40251         var cfg = {
40252             cls: 'form-group',
40253             cn: []
40254         };
40255
40256         var input =  {
40257             tag: 'input',
40258             id : id,
40259             cls : 'form-control roo-money-amount-input',
40260             autocomplete: 'new-password'
40261         };
40262         
40263         var hiddenInput = {
40264             tag: 'input',
40265             type: 'hidden',
40266             id: Roo.id(),
40267             cls: 'hidden-number-input'
40268         };
40269         
40270         if (this.name) {
40271             hiddenInput.name = this.name;
40272         }
40273
40274         if (this.disabled) {
40275             input.disabled = true;
40276         }
40277
40278         var clg = 12 - this.inputlg;
40279         var cmd = 12 - this.inputmd;
40280         var csm = 12 - this.inputsm;
40281         var cxs = 12 - this.inputxs;
40282         
40283         var container = {
40284             tag : 'div',
40285             cls : 'row roo-money-field',
40286             cn : [
40287                 {
40288                     tag : 'div',
40289                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40290                     cn : [
40291                         {
40292                             tag : 'div',
40293                             cls: 'roo-select2-container input-group',
40294                             cn: [
40295                                 {
40296                                     tag : 'input',
40297                                     cls : 'form-control roo-money-currency-input',
40298                                     autocomplete: 'new-password',
40299                                     readOnly : 1,
40300                                     name : this.currencyName
40301                                 },
40302                                 {
40303                                     tag :'span',
40304                                     cls : 'input-group-addon',
40305                                     cn : [
40306                                         {
40307                                             tag: 'span',
40308                                             cls: 'caret'
40309                                         }
40310                                     ]
40311                                 }
40312                             ]
40313                         }
40314                     ]
40315                 },
40316                 {
40317                     tag : 'div',
40318                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40319                     cn : [
40320                         {
40321                             tag: 'div',
40322                             cls: this.hasFeedback ? 'has-feedback' : '',
40323                             cn: [
40324                                 input
40325                             ]
40326                         }
40327                     ]
40328                 }
40329             ]
40330             
40331         };
40332         
40333         if (this.fieldLabel.length) {
40334             var indicator = {
40335                 tag: 'i',
40336                 tooltip: 'This field is required'
40337             };
40338
40339             var label = {
40340                 tag: 'label',
40341                 'for':  id,
40342                 cls: 'control-label',
40343                 cn: []
40344             };
40345
40346             var label_text = {
40347                 tag: 'span',
40348                 html: this.fieldLabel
40349             };
40350
40351             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40352             label.cn = [
40353                 indicator,
40354                 label_text
40355             ];
40356
40357             if(this.indicatorpos == 'right') {
40358                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40359                 label.cn = [
40360                     label_text,
40361                     indicator
40362                 ];
40363             }
40364
40365             if(align == 'left') {
40366                 container = {
40367                     tag: 'div',
40368                     cn: [
40369                         container
40370                     ]
40371                 };
40372
40373                 if(this.labelWidth > 12){
40374                     label.style = "width: " + this.labelWidth + 'px';
40375                 }
40376                 if(this.labelWidth < 13 && this.labelmd == 0){
40377                     this.labelmd = this.labelWidth;
40378                 }
40379                 if(this.labellg > 0){
40380                     label.cls += ' col-lg-' + this.labellg;
40381                     input.cls += ' col-lg-' + (12 - this.labellg);
40382                 }
40383                 if(this.labelmd > 0){
40384                     label.cls += ' col-md-' + this.labelmd;
40385                     container.cls += ' col-md-' + (12 - this.labelmd);
40386                 }
40387                 if(this.labelsm > 0){
40388                     label.cls += ' col-sm-' + this.labelsm;
40389                     container.cls += ' col-sm-' + (12 - this.labelsm);
40390                 }
40391                 if(this.labelxs > 0){
40392                     label.cls += ' col-xs-' + this.labelxs;
40393                     container.cls += ' col-xs-' + (12 - this.labelxs);
40394                 }
40395             }
40396         }
40397
40398         cfg.cn = [
40399             label,
40400             container,
40401             hiddenInput
40402         ];
40403         
40404         var settings = this;
40405
40406         ['xs','sm','md','lg'].map(function(size){
40407             if (settings[size]) {
40408                 cfg.cls += ' col-' + size + '-' + settings[size];
40409             }
40410         });
40411         
40412         return cfg;
40413     },
40414     
40415     initEvents : function()
40416     {
40417         this.indicator = this.indicatorEl();
40418         
40419         this.initCurrencyEvent();
40420         
40421         this.initNumberEvent();
40422     },
40423     
40424     initCurrencyEvent : function()
40425     {
40426         if (!this.store) {
40427             throw "can not find store for combo";
40428         }
40429         
40430         this.store = Roo.factory(this.store, Roo.data);
40431         this.store.parent = this;
40432         
40433         this.createList();
40434         
40435         this.triggerEl = this.el.select('.input-group-addon', true).first();
40436         
40437         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40438         
40439         var _this = this;
40440         
40441         (function(){
40442             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40443             _this.list.setWidth(lw);
40444         }).defer(100);
40445         
40446         this.list.on('mouseover', this.onViewOver, this);
40447         this.list.on('mousemove', this.onViewMove, this);
40448         this.list.on('scroll', this.onViewScroll, this);
40449         
40450         if(!this.tpl){
40451             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40452         }
40453         
40454         this.view = new Roo.View(this.list, this.tpl, {
40455             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40456         });
40457         
40458         this.view.on('click', this.onViewClick, this);
40459         
40460         this.store.on('beforeload', this.onBeforeLoad, this);
40461         this.store.on('load', this.onLoad, this);
40462         this.store.on('loadexception', this.onLoadException, this);
40463         
40464         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40465             "up" : function(e){
40466                 this.inKeyMode = true;
40467                 this.selectPrev();
40468             },
40469
40470             "down" : function(e){
40471                 if(!this.isExpanded()){
40472                     this.onTriggerClick();
40473                 }else{
40474                     this.inKeyMode = true;
40475                     this.selectNext();
40476                 }
40477             },
40478
40479             "enter" : function(e){
40480                 this.collapse();
40481                 
40482                 if(this.fireEvent("specialkey", this, e)){
40483                     this.onViewClick(false);
40484                 }
40485                 
40486                 return true;
40487             },
40488
40489             "esc" : function(e){
40490                 this.collapse();
40491             },
40492
40493             "tab" : function(e){
40494                 this.collapse();
40495                 
40496                 if(this.fireEvent("specialkey", this, e)){
40497                     this.onViewClick(false);
40498                 }
40499                 
40500                 return true;
40501             },
40502
40503             scope : this,
40504
40505             doRelay : function(foo, bar, hname){
40506                 if(hname == 'down' || this.scope.isExpanded()){
40507                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40508                 }
40509                 return true;
40510             },
40511
40512             forceKeyDown: true
40513         });
40514         
40515         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40516         
40517     },
40518     
40519     initNumberEvent : function(e)
40520     {
40521         this.inputEl().on("keydown" , this.fireKey,  this);
40522         this.inputEl().on("focus", this.onFocus,  this);
40523         this.inputEl().on("blur", this.onBlur,  this);
40524         
40525         this.inputEl().relayEvent('keyup', this);
40526         
40527         if(this.indicator){
40528             this.indicator.addClass('invisible');
40529         }
40530  
40531         this.originalValue = this.getValue();
40532         
40533         if(this.validationEvent == 'keyup'){
40534             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40535             this.inputEl().on('keyup', this.filterValidation, this);
40536         }
40537         else if(this.validationEvent !== false){
40538             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40539         }
40540         
40541         if(this.selectOnFocus){
40542             this.on("focus", this.preFocus, this);
40543             
40544         }
40545         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40546             this.inputEl().on("keypress", this.filterKeys, this);
40547         } else {
40548             this.inputEl().relayEvent('keypress', this);
40549         }
40550         
40551         var allowed = "0123456789";
40552         
40553         if(this.allowDecimals){
40554             allowed += this.decimalSeparator;
40555         }
40556         
40557         if(this.allowNegative){
40558             allowed += "-";
40559         }
40560         
40561         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40562         
40563         var keyPress = function(e){
40564             
40565             var k = e.getKey();
40566             
40567             var c = e.getCharCode();
40568             
40569             if(
40570                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40571                     allowed.indexOf(String.fromCharCode(c)) === -1
40572             ){
40573                 e.stopEvent();
40574                 return;
40575             }
40576             
40577             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40578                 return;
40579             }
40580             
40581             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40582                 e.stopEvent();
40583             }
40584         };
40585         
40586         this.inputEl().on("keypress", keyPress, this);
40587         
40588     },
40589     
40590     onTriggerClick : function(e)
40591     {   
40592         if(this.disabled){
40593             return;
40594         }
40595         
40596         this.page = 0;
40597         this.loadNext = false;
40598         
40599         if(this.isExpanded()){
40600             this.collapse();
40601             return;
40602         }
40603         
40604         this.hasFocus = true;
40605         
40606         if(this.triggerAction == 'all') {
40607             this.doQuery(this.allQuery, true);
40608             return;
40609         }
40610         
40611         this.doQuery(this.getRawValue());
40612     },
40613     
40614     getCurrency : function()
40615     {   
40616         var v = this.currencyEl().getValue();
40617         
40618         return v;
40619     },
40620     
40621     restrictHeight : function()
40622     {
40623         this.list.alignTo(this.currencyEl(), this.listAlign);
40624         this.list.alignTo(this.currencyEl(), this.listAlign);
40625     },
40626     
40627     onViewClick : function(view, doFocus, el, e)
40628     {
40629         var index = this.view.getSelectedIndexes()[0];
40630         
40631         var r = this.store.getAt(index);
40632         
40633         if(r){
40634             this.onSelect(r, index);
40635         }
40636     },
40637     
40638     onSelect : function(record, index){
40639         
40640         if(this.fireEvent('beforeselect', this, record, index) !== false){
40641         
40642             this.setFromCurrencyData(index > -1 ? record.data : false);
40643             
40644             this.collapse();
40645             
40646             this.fireEvent('select', this, record, index);
40647         }
40648     },
40649     
40650     setFromCurrencyData : function(o)
40651     {
40652         var currency = '';
40653         
40654         this.lastCurrency = o;
40655         
40656         if (this.currencyField) {
40657             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40658         } else {
40659             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40660         }
40661         
40662         this.lastSelectionText = currency;
40663         
40664         //setting default currency
40665         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40666             this.setCurrency(this.defaultCurrency);
40667             return;
40668         }
40669         
40670         this.setCurrency(currency);
40671     },
40672     
40673     setFromData : function(o)
40674     {
40675         var c = {};
40676         
40677         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40678         
40679         this.setFromCurrencyData(c);
40680         
40681         var value = '';
40682         
40683         if (this.name) {
40684             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40685         } else {
40686             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40687         }
40688         
40689         this.setValue(value);
40690         
40691     },
40692     
40693     setCurrency : function(v)
40694     {   
40695         this.currencyValue = v;
40696         
40697         if(this.rendered){
40698             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40699             this.validate();
40700         }
40701     },
40702     
40703     setValue : function(v)
40704     {
40705         v = this.fixPrecision(v);
40706         
40707         v = String(v).replace(".", this.decimalSeparator);
40708         
40709         this.value = v;
40710         
40711         if(this.rendered){
40712             
40713             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40714             
40715             this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
40716                 this.thousandsDelimiter || ','
40717             );
40718             
40719             if(this.allowBlank && !v) {
40720                 this.inputEl().dom.value = '';
40721             }
40722             
40723             this.validate();
40724         }
40725     },
40726     
40727     getRawValue : function()
40728     {
40729         var v = this.inputEl().getValue();
40730         
40731         return v;
40732     },
40733     
40734     getValue : function()
40735     {
40736         return this.fixPrecision(this.parseValue(this.getRawValue()));
40737     },
40738     
40739     parseValue : function(value)
40740     {
40741         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40742         return isNaN(value) ? '' : value;
40743     },
40744     
40745     fixPrecision : function(value)
40746     {
40747         var nan = isNaN(value);
40748         
40749         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40750             return nan ? '' : value;
40751         }
40752         
40753         return parseFloat(value).toFixed(this.decimalPrecision);
40754     },
40755     
40756     decimalPrecisionFcn : function(v)
40757     {
40758         return Math.floor(v);
40759     },
40760     
40761     validateValue : function(value)
40762     {
40763         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40764             return false;
40765         }
40766         
40767         var num = this.parseValue(value);
40768         
40769         if(isNaN(num)){
40770             this.markInvalid(String.format(this.nanText, value));
40771             return false;
40772         }
40773         
40774         if(num < this.minValue){
40775             this.markInvalid(String.format(this.minText, this.minValue));
40776             return false;
40777         }
40778         
40779         if(num > this.maxValue){
40780             this.markInvalid(String.format(this.maxText, this.maxValue));
40781             return false;
40782         }
40783         
40784         return true;
40785     },
40786     
40787     validate : function()
40788     {
40789         if(this.disabled || this.allowBlank){
40790             this.markValid();
40791             return true;
40792         }
40793         
40794         var currency = this.getCurrency();
40795         
40796         if(this.validateValue(this.getRawValue()) && currency.length){
40797             this.markValid();
40798             return true;
40799         }
40800         
40801         this.markInvalid();
40802         return false;
40803     },
40804     
40805     getName: function()
40806     {
40807         return this.name;
40808     },
40809     
40810     beforeBlur : function()
40811     {
40812         if(!this.castInt){
40813             return;
40814         }
40815         
40816         var v = this.parseValue(this.getRawValue());
40817         
40818         if(v || v == 0){
40819             this.setValue(v);
40820         }
40821     },
40822     
40823     onBlur : function()
40824     {
40825         this.beforeBlur();
40826         
40827         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40828             //this.el.removeClass(this.focusClass);
40829         }
40830         
40831         this.hasFocus = false;
40832         
40833         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40834             this.validate();
40835         }
40836         
40837         var v = this.getValue();
40838         
40839         if(String(v) !== String(this.startValue)){
40840             this.fireEvent('change', this, v, this.startValue);
40841         }
40842         
40843         this.fireEvent("blur", this);
40844     },
40845     
40846     inputEl : function()
40847     {
40848         return this.el.select('.roo-money-amount-input', true).first();
40849     },
40850     
40851     currencyEl : function()
40852     {
40853         return this.el.select('.roo-money-currency-input', true).first();
40854     },
40855     
40856     hiddenEl : function()
40857     {
40858         return this.el.select('input.hidden-number-input',true).first();
40859     }
40860     
40861 });