sync
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         
405     },
406     /**
407      * Hide a component - adds 'hidden' class
408      */
409     hide: function()
410     {
411         if(!this.getVisibilityEl()){
412             return;
413         }
414         
415         this.getVisibilityEl().addClass('hidden');
416         
417     }
418 });
419
420  /*
421  * - LGPL
422  *
423  * Body
424  *
425  */
426
427 /**
428  * @class Roo.bootstrap.Body
429  * @extends Roo.bootstrap.Component
430  * Bootstrap Body class
431  *
432  * @constructor
433  * Create a new body
434  * @param {Object} config The config object
435  */
436
437 Roo.bootstrap.Body = function(config){
438
439     config = config || {};
440
441     Roo.bootstrap.Body.superclass.constructor.call(this, config);
442     this.el = Roo.get(config.el ? config.el : document.body );
443     if (this.cls && this.cls.length) {
444         Roo.get(document.body).addClass(this.cls);
445     }
446 };
447
448 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
449
450     is_body : true,// just to make sure it's constructed?
451
452         autoCreate : {
453         cls: 'container'
454     },
455     onRender : function(ct, position)
456     {
457        /* Roo.log("Roo.bootstrap.Body - onRender");
458         if (this.cls && this.cls.length) {
459             Roo.get(document.body).addClass(this.cls);
460         }
461         // style??? xttr???
462         */
463     }
464
465
466
467
468 });
469 /*
470  * - LGPL
471  *
472  * button group
473  * 
474  */
475
476
477 /**
478  * @class Roo.bootstrap.ButtonGroup
479  * @extends Roo.bootstrap.Component
480  * Bootstrap ButtonGroup class
481  * @cfg {String} size lg | sm | xs (default empty normal)
482  * @cfg {String} align vertical | justified  (default none)
483  * @cfg {String} direction up | down (default down)
484  * @cfg {Boolean} toolbar false | true
485  * @cfg {Boolean} btn true | false
486  * 
487  * 
488  * @constructor
489  * Create a new Input
490  * @param {Object} config The config object
491  */
492
493 Roo.bootstrap.ButtonGroup = function(config){
494     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
495 };
496
497 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
498     
499     size: '',
500     align: '',
501     direction: '',
502     toolbar: false,
503     btn: true,
504
505     getAutoCreate : function(){
506         var cfg = {
507             cls: 'btn-group',
508             html : null
509         };
510         
511         cfg.html = this.html || cfg.html;
512         
513         if (this.toolbar) {
514             cfg = {
515                 cls: 'btn-toolbar',
516                 html: null
517             };
518             
519             return cfg;
520         }
521         
522         if (['vertical','justified'].indexOf(this.align)!==-1) {
523             cfg.cls = 'btn-group-' + this.align;
524             
525             if (this.align == 'justified') {
526                 console.log(this.items);
527             }
528         }
529         
530         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
531             cfg.cls += ' btn-group-' + this.size;
532         }
533         
534         if (this.direction == 'up') {
535             cfg.cls += ' dropup' ;
536         }
537         
538         return cfg;
539     }
540    
541 });
542
543  /*
544  * - LGPL
545  *
546  * button
547  * 
548  */
549
550 /**
551  * @class Roo.bootstrap.Button
552  * @extends Roo.bootstrap.Component
553  * Bootstrap Button class
554  * @cfg {String} html The button content
555  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
556  * @cfg {String} size ( lg | sm | xs)
557  * @cfg {String} tag ( a | input | submit)
558  * @cfg {String} href empty or href
559  * @cfg {Boolean} disabled default false;
560  * @cfg {Boolean} isClose default false;
561  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
562  * @cfg {String} badge text for badge
563  * @cfg {String} theme (default|glow)  
564  * @cfg {Boolean} inverse dark themed version
565  * @cfg {Boolean} toggle is it a slidy toggle button
566  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
567  * @cfg {String} ontext text for on slidy toggle state
568  * @cfg {String} offtext text for off slidy toggle state
569  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
570  * @cfg {Boolean} removeClass remove the standard class..
571  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
572  * 
573  * @constructor
574  * Create a new button
575  * @param {Object} config The config object
576  */
577
578
579 Roo.bootstrap.Button = function(config){
580     Roo.bootstrap.Button.superclass.constructor.call(this, config);
581     this.weightClass = ["btn-default", 
582                        "btn-primary", 
583                        "btn-success", 
584                        "btn-info", 
585                        "btn-warning",
586                        "btn-danger",
587                        "btn-link"
588                       ],  
589     this.addEvents({
590         // raw events
591         /**
592          * @event click
593          * When a butotn is pressed
594          * @param {Roo.bootstrap.Button} btn
595          * @param {Roo.EventObject} e
596          */
597         "click" : true,
598          /**
599          * @event toggle
600          * After the button has been toggles
601          * @param {Roo.bootstrap.Button} btn
602          * @param {Roo.EventObject} e
603          * @param {boolean} pressed (also available as button.pressed)
604          */
605         "toggle" : true
606     });
607 };
608
609 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
610     html: false,
611     active: false,
612     weight: '',
613     size: '',
614     tag: 'button',
615     href: '',
616     disabled: false,
617     isClose: false,
618     glyphicon: '',
619     badge: '',
620     theme: 'default',
621     inverse: false,
622     
623     toggle: false,
624     ontext: 'ON',
625     offtext: 'OFF',
626     defaulton: true,
627     preventDefault: true,
628     removeClass: false,
629     name: false,
630     target: false,
631      
632     pressed : false,
633      
634     
635     getAutoCreate : function(){
636         
637         var cfg = {
638             tag : 'button',
639             cls : 'roo-button',
640             html: ''
641         };
642         
643         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
644             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
645             this.tag = 'button';
646         } else {
647             cfg.tag = this.tag;
648         }
649         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
650         
651         if (this.toggle == true) {
652             cfg={
653                 tag: 'div',
654                 cls: 'slider-frame roo-button',
655                 cn: [
656                     {
657                         tag: 'span',
658                         'data-on-text':'ON',
659                         'data-off-text':'OFF',
660                         cls: 'slider-button',
661                         html: this.offtext
662                     }
663                 ]
664             };
665             
666             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
667                 cfg.cls += ' '+this.weight;
668             }
669             
670             return cfg;
671         }
672         
673         if (this.isClose) {
674             cfg.cls += ' close';
675             
676             cfg["aria-hidden"] = true;
677             
678             cfg.html = "&times;";
679             
680             return cfg;
681         }
682         
683          
684         if (this.theme==='default') {
685             cfg.cls = 'btn roo-button';
686             
687             //if (this.parentType != 'Navbar') {
688             this.weight = this.weight.length ?  this.weight : 'default';
689             //}
690             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
691                 
692                 cfg.cls += ' btn-' + this.weight;
693             }
694         } else if (this.theme==='glow') {
695             
696             cfg.tag = 'a';
697             cfg.cls = 'btn-glow roo-button';
698             
699             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
700                 
701                 cfg.cls += ' ' + this.weight;
702             }
703         }
704    
705         
706         if (this.inverse) {
707             this.cls += ' inverse';
708         }
709         
710         
711         if (this.active || this.pressed === true) {
712             cfg.cls += ' active';
713         }
714         
715         if (this.disabled) {
716             cfg.disabled = 'disabled';
717         }
718         
719         if (this.items) {
720             Roo.log('changing to ul' );
721             cfg.tag = 'ul';
722             this.glyphicon = 'caret';
723         }
724         
725         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
726          
727         //gsRoo.log(this.parentType);
728         if (this.parentType === 'Navbar' && !this.parent().bar) {
729             Roo.log('changing to li?');
730             
731             cfg.tag = 'li';
732             
733             cfg.cls = '';
734             cfg.cn =  [{
735                 tag : 'a',
736                 cls : 'roo-button',
737                 html : this.html,
738                 href : this.href || '#'
739             }];
740             if (this.menu) {
741                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
742                 cfg.cls += ' dropdown';
743             }   
744             
745             delete cfg.html;
746             
747         }
748         
749        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
750         
751         if (this.glyphicon) {
752             cfg.html = ' ' + cfg.html;
753             
754             cfg.cn = [
755                 {
756                     tag: 'span',
757                     cls: 'glyphicon glyphicon-' + this.glyphicon
758                 }
759             ];
760         }
761         
762         if (this.badge) {
763             cfg.html += ' ';
764             
765             cfg.tag = 'a';
766             
767 //            cfg.cls='btn roo-button';
768             
769             cfg.href=this.href;
770             
771             var value = cfg.html;
772             
773             if(this.glyphicon){
774                 value = {
775                             tag: 'span',
776                             cls: 'glyphicon glyphicon-' + this.glyphicon,
777                             html: this.html
778                         };
779                 
780             }
781             
782             cfg.cn = [
783                 value,
784                 {
785                     tag: 'span',
786                     cls: 'badge',
787                     html: this.badge
788                 }
789             ];
790             
791             cfg.html='';
792         }
793         
794         if (this.menu) {
795             cfg.cls += ' dropdown';
796             cfg.html = typeof(cfg.html) != 'undefined' ?
797                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
798         }
799         
800         if (cfg.tag !== 'a' && this.href !== '') {
801             throw "Tag must be a to set href.";
802         } else if (this.href.length > 0) {
803             cfg.href = this.href;
804         }
805         
806         if(this.removeClass){
807             cfg.cls = '';
808         }
809         
810         if(this.target){
811             cfg.target = this.target;
812         }
813         
814         return cfg;
815     },
816     initEvents: function() {
817        // Roo.log('init events?');
818 //        Roo.log(this.el.dom);
819         // add the menu...
820         
821         if (typeof (this.menu) != 'undefined') {
822             this.menu.parentType = this.xtype;
823             this.menu.triggerEl = this.el;
824             this.addxtype(Roo.apply({}, this.menu));
825         }
826
827
828        if (this.el.hasClass('roo-button')) {
829             this.el.on('click', this.onClick, this);
830        } else {
831             this.el.select('.roo-button').on('click', this.onClick, this);
832        }
833        
834        if(this.removeClass){
835            this.el.on('click', this.onClick, this);
836        }
837        
838        this.el.enableDisplayMode();
839         
840     },
841     onClick : function(e)
842     {
843         if (this.disabled) {
844             return;
845         }
846         
847         
848         Roo.log('button on click ');
849         if(this.preventDefault){
850             e.preventDefault();
851         }
852         
853         if (this.pressed === true || this.pressed === false) {
854             this.pressed = !this.pressed;
855             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
856             this.fireEvent('toggle', this, e, this.pressed);
857         }
858         
859         
860         this.fireEvent('click', this, e);
861     },
862     
863     /**
864      * Enables this button
865      */
866     enable : function()
867     {
868         this.disabled = false;
869         this.el.removeClass('disabled');
870     },
871     
872     /**
873      * Disable this button
874      */
875     disable : function()
876     {
877         this.disabled = true;
878         this.el.addClass('disabled');
879     },
880      /**
881      * sets the active state on/off, 
882      * @param {Boolean} state (optional) Force a particular state
883      */
884     setActive : function(v) {
885         
886         this.el[v ? 'addClass' : 'removeClass']('active');
887         this.pressed = v;
888     },
889      /**
890      * toggles the current active state 
891      */
892     toggleActive : function()
893     {
894        var active = this.el.hasClass('active');
895        this.setActive(!active);
896        
897         
898     },
899      /**
900      * get the current active state
901      * @return {boolean} true if it's active
902      */
903     isActive : function()
904     {
905         return this.el.hasClass('active');
906     },
907     /**
908      * set the text of the first selected button
909      */
910     setText : function(str)
911     {
912         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
913     },
914     /**
915      * get the text of the first selected button
916      */
917     getText : function()
918     {
919         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
920     },
921     hide: function() {
922        
923      
924         this.el.hide();   
925     },
926     show: function() {
927        
928         this.el.show();   
929     },
930     setWeight : function(str)
931     {
932         this.el.removeClass(this.weightClass);
933         this.el.addClass('btn-' + str);        
934     }
935     
936     
937 });
938
939  /*
940  * - LGPL
941  *
942  * column
943  * 
944  */
945
946 /**
947  * @class Roo.bootstrap.Column
948  * @extends Roo.bootstrap.Component
949  * Bootstrap Column class
950  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
951  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
952  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
953  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
954  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
955  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
956  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
957  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
958  *
959  * 
960  * @cfg {Boolean} hidden (true|false) hide the element
961  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
962  * @cfg {String} fa (ban|check|...) font awesome icon
963  * @cfg {Number} fasize (1|2|....) font awsome size
964
965  * @cfg {String} icon (info-sign|check|...) glyphicon name
966
967  * @cfg {String} html content of column.
968  * 
969  * @constructor
970  * Create a new Column
971  * @param {Object} config The config object
972  */
973
974 Roo.bootstrap.Column = function(config){
975     Roo.bootstrap.Column.superclass.constructor.call(this, config);
976 };
977
978 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
979     
980     xs: false,
981     sm: false,
982     md: false,
983     lg: false,
984     xsoff: false,
985     smoff: false,
986     mdoff: false,
987     lgoff: false,
988     html: '',
989     offset: 0,
990     alert: false,
991     fa: false,
992     icon : false,
993     hidden : false,
994     fasize : 1,
995     
996     getAutoCreate : function(){
997         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
998         
999         cfg = {
1000             tag: 'div',
1001             cls: 'column'
1002         };
1003         
1004         var settings=this;
1005         ['xs','sm','md','lg'].map(function(size){
1006             //Roo.log( size + ':' + settings[size]);
1007             
1008             if (settings[size+'off'] !== false) {
1009                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1010             }
1011             
1012             if (settings[size] === false) {
1013                 return;
1014             }
1015             
1016             if (!settings[size]) { // 0 = hidden
1017                 cfg.cls += ' hidden-' + size;
1018                 return;
1019             }
1020             cfg.cls += ' col-' + size + '-' + settings[size];
1021             
1022         });
1023         
1024         if (this.hidden) {
1025             cfg.cls += ' hidden';
1026         }
1027         
1028         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1029             cfg.cls +=' alert alert-' + this.alert;
1030         }
1031         
1032         
1033         if (this.html.length) {
1034             cfg.html = this.html;
1035         }
1036         if (this.fa) {
1037             var fasize = '';
1038             if (this.fasize > 1) {
1039                 fasize = ' fa-' + this.fasize + 'x';
1040             }
1041             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1042             
1043             
1044         }
1045         if (this.icon) {
1046             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1047         }
1048         
1049         return cfg;
1050     }
1051    
1052 });
1053
1054  
1055
1056  /*
1057  * - LGPL
1058  *
1059  * page container.
1060  * 
1061  */
1062
1063
1064 /**
1065  * @class Roo.bootstrap.Container
1066  * @extends Roo.bootstrap.Component
1067  * Bootstrap Container class
1068  * @cfg {Boolean} jumbotron is it a jumbotron element
1069  * @cfg {String} html content of element
1070  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1071  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1072  * @cfg {String} header content of header (for panel)
1073  * @cfg {String} footer content of footer (for panel)
1074  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1075  * @cfg {String} tag (header|aside|section) type of HTML tag.
1076  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1077  * @cfg {String} fa font awesome icon
1078  * @cfg {String} icon (info-sign|check|...) glyphicon name
1079  * @cfg {Boolean} hidden (true|false) hide the element
1080  * @cfg {Boolean} expandable (true|false) default false
1081  * @cfg {Boolean} expanded (true|false) default true
1082  * @cfg {String} rheader contet on the right of header
1083  * @cfg {Boolean} clickable (true|false) default false
1084
1085  *     
1086  * @constructor
1087  * Create a new Container
1088  * @param {Object} config The config object
1089  */
1090
1091 Roo.bootstrap.Container = function(config){
1092     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1093     
1094     this.addEvents({
1095         // raw events
1096          /**
1097          * @event expand
1098          * After the panel has been expand
1099          * 
1100          * @param {Roo.bootstrap.Container} this
1101          */
1102         "expand" : true,
1103         /**
1104          * @event collapse
1105          * After the panel has been collapsed
1106          * 
1107          * @param {Roo.bootstrap.Container} this
1108          */
1109         "collapse" : true,
1110         /**
1111          * @event click
1112          * When a element is chick
1113          * @param {Roo.bootstrap.Container} this
1114          * @param {Roo.EventObject} e
1115          */
1116         "click" : true
1117     });
1118 };
1119
1120 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1121     
1122     jumbotron : false,
1123     well: '',
1124     panel : '',
1125     header: '',
1126     footer : '',
1127     sticky: '',
1128     tag : false,
1129     alert : false,
1130     fa: false,
1131     icon : false,
1132     expandable : false,
1133     rheader : '',
1134     expanded : true,
1135     clickable: false,
1136   
1137      
1138     getChildContainer : function() {
1139         
1140         if(!this.el){
1141             return false;
1142         }
1143         
1144         if (this.panel.length) {
1145             return this.el.select('.panel-body',true).first();
1146         }
1147         
1148         return this.el;
1149     },
1150     
1151     
1152     getAutoCreate : function(){
1153         
1154         var cfg = {
1155             tag : this.tag || 'div',
1156             html : '',
1157             cls : ''
1158         };
1159         if (this.jumbotron) {
1160             cfg.cls = 'jumbotron';
1161         }
1162         
1163         
1164         
1165         // - this is applied by the parent..
1166         //if (this.cls) {
1167         //    cfg.cls = this.cls + '';
1168         //}
1169         
1170         if (this.sticky.length) {
1171             
1172             var bd = Roo.get(document.body);
1173             if (!bd.hasClass('bootstrap-sticky')) {
1174                 bd.addClass('bootstrap-sticky');
1175                 Roo.select('html',true).setStyle('height', '100%');
1176             }
1177              
1178             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1179         }
1180         
1181         
1182         if (this.well.length) {
1183             switch (this.well) {
1184                 case 'lg':
1185                 case 'sm':
1186                     cfg.cls +=' well well-' +this.well;
1187                     break;
1188                 default:
1189                     cfg.cls +=' well';
1190                     break;
1191             }
1192         }
1193         
1194         if (this.hidden) {
1195             cfg.cls += ' hidden';
1196         }
1197         
1198         
1199         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1200             cfg.cls +=' alert alert-' + this.alert;
1201         }
1202         
1203         var body = cfg;
1204         
1205         if (this.panel.length) {
1206             cfg.cls += ' panel panel-' + this.panel;
1207             cfg.cn = [];
1208             if (this.header.length) {
1209                 
1210                 var h = [];
1211                 
1212                 if(this.expandable){
1213                     
1214                     cfg.cls = cfg.cls + ' expandable';
1215                     
1216                     h.push({
1217                         tag: 'i',
1218                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1219                     });
1220                     
1221                 }
1222                 
1223                 h.push(
1224                     {
1225                         tag: 'span',
1226                         cls : 'panel-title',
1227                         html : (this.expandable ? '&nbsp;' : '') + this.header
1228                     },
1229                     {
1230                         tag: 'span',
1231                         cls: 'panel-header-right',
1232                         html: this.rheader
1233                     }
1234                 );
1235                 
1236                 cfg.cn.push({
1237                     cls : 'panel-heading',
1238                     style : this.expandable ? 'cursor: pointer' : '',
1239                     cn : h
1240                 });
1241                 
1242             }
1243             
1244             body = false;
1245             cfg.cn.push({
1246                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1247                 html : this.html
1248             });
1249             
1250             
1251             if (this.footer.length) {
1252                 cfg.cn.push({
1253                     cls : 'panel-footer',
1254                     html : this.footer
1255                     
1256                 });
1257             }
1258             
1259         }
1260         
1261         if (body) {
1262             body.html = this.html || cfg.html;
1263             // prefix with the icons..
1264             if (this.fa) {
1265                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1266             }
1267             if (this.icon) {
1268                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1269             }
1270             
1271             
1272         }
1273         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1274             cfg.cls =  'container';
1275         }
1276         
1277         return cfg;
1278     },
1279     
1280     initEvents: function() 
1281     {
1282         if(this.expandable){
1283             var headerEl = this.headerEl();
1284         
1285             if(headerEl){
1286                 headerEl.on('click', this.onToggleClick, this);
1287             }
1288         }
1289         
1290         if(this.clickable){
1291             this.el.on('click', this.onClick, this);
1292         }
1293         
1294     },
1295     
1296     onToggleClick : function()
1297     {
1298         var headerEl = this.headerEl();
1299         
1300         if(!headerEl){
1301             return;
1302         }
1303         
1304         if(this.expanded){
1305             this.collapse();
1306             return;
1307         }
1308         
1309         this.expand();
1310     },
1311     
1312     expand : function()
1313     {
1314         if(this.fireEvent('expand', this)) {
1315             
1316             this.expanded = true;
1317             
1318             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1319             
1320             this.el.select('.panel-body',true).first().removeClass('hide');
1321             
1322             var toggleEl = this.toggleEl();
1323
1324             if(!toggleEl){
1325                 return;
1326             }
1327
1328             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1329         }
1330         
1331     },
1332     
1333     collapse : function()
1334     {
1335         if(this.fireEvent('collapse', this)) {
1336             
1337             this.expanded = false;
1338             
1339             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1340             this.el.select('.panel-body',true).first().addClass('hide');
1341         
1342             var toggleEl = this.toggleEl();
1343
1344             if(!toggleEl){
1345                 return;
1346             }
1347
1348             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1349         }
1350     },
1351     
1352     toggleEl : function()
1353     {
1354         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1355             return;
1356         }
1357         
1358         return this.el.select('.panel-heading .fa',true).first();
1359     },
1360     
1361     headerEl : function()
1362     {
1363         if(!this.el || !this.panel.length || !this.header.length){
1364             return;
1365         }
1366         
1367         return this.el.select('.panel-heading',true).first()
1368     },
1369     
1370     bodyEl : function()
1371     {
1372         if(!this.el || !this.panel.length){
1373             return;
1374         }
1375         
1376         return this.el.select('.panel-body',true).first()
1377     },
1378     
1379     titleEl : function()
1380     {
1381         if(!this.el || !this.panel.length || !this.header.length){
1382             return;
1383         }
1384         
1385         return this.el.select('.panel-title',true).first();
1386     },
1387     
1388     setTitle : function(v)
1389     {
1390         var titleEl = this.titleEl();
1391         
1392         if(!titleEl){
1393             return;
1394         }
1395         
1396         titleEl.dom.innerHTML = v;
1397     },
1398     
1399     getTitle : function()
1400     {
1401         
1402         var titleEl = this.titleEl();
1403         
1404         if(!titleEl){
1405             return '';
1406         }
1407         
1408         return titleEl.dom.innerHTML;
1409     },
1410     
1411     setRightTitle : function(v)
1412     {
1413         var t = this.el.select('.panel-header-right',true).first();
1414         
1415         if(!t){
1416             return;
1417         }
1418         
1419         t.dom.innerHTML = v;
1420     },
1421     
1422     onClick : function(e)
1423     {
1424         e.preventDefault();
1425         
1426         this.fireEvent('click', this, e);
1427     }
1428 });
1429
1430  /*
1431  * - LGPL
1432  *
1433  * image
1434  * 
1435  */
1436
1437
1438 /**
1439  * @class Roo.bootstrap.Img
1440  * @extends Roo.bootstrap.Component
1441  * Bootstrap Img class
1442  * @cfg {Boolean} imgResponsive false | true
1443  * @cfg {String} border rounded | circle | thumbnail
1444  * @cfg {String} src image source
1445  * @cfg {String} alt image alternative text
1446  * @cfg {String} href a tag href
1447  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1448  * @cfg {String} xsUrl xs image source
1449  * @cfg {String} smUrl sm image source
1450  * @cfg {String} mdUrl md image source
1451  * @cfg {String} lgUrl lg image source
1452  * 
1453  * @constructor
1454  * Create a new Input
1455  * @param {Object} config The config object
1456  */
1457
1458 Roo.bootstrap.Img = function(config){
1459     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1460     
1461     this.addEvents({
1462         // img events
1463         /**
1464          * @event click
1465          * The img click event for the img.
1466          * @param {Roo.EventObject} e
1467          */
1468         "click" : true
1469     });
1470 };
1471
1472 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1473     
1474     imgResponsive: true,
1475     border: '',
1476     src: 'about:blank',
1477     href: false,
1478     target: false,
1479     xsUrl: '',
1480     smUrl: '',
1481     mdUrl: '',
1482     lgUrl: '',
1483
1484     getAutoCreate : function()
1485     {   
1486         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1487             return this.createSingleImg();
1488         }
1489         
1490         var cfg = {
1491             tag: 'div',
1492             cls: 'roo-image-responsive-group',
1493             cn: []
1494         };
1495         var _this = this;
1496         
1497         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1498             
1499             if(!_this[size + 'Url']){
1500                 return;
1501             }
1502             
1503             var img = {
1504                 tag: 'img',
1505                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1506                 html: _this.html || cfg.html,
1507                 src: _this[size + 'Url']
1508             };
1509             
1510             img.cls += ' roo-image-responsive-' + size;
1511             
1512             var s = ['xs', 'sm', 'md', 'lg'];
1513             
1514             s.splice(s.indexOf(size), 1);
1515             
1516             Roo.each(s, function(ss){
1517                 img.cls += ' hidden-' + ss;
1518             });
1519             
1520             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1521                 cfg.cls += ' img-' + _this.border;
1522             }
1523             
1524             if(_this.alt){
1525                 cfg.alt = _this.alt;
1526             }
1527             
1528             if(_this.href){
1529                 var a = {
1530                     tag: 'a',
1531                     href: _this.href,
1532                     cn: [
1533                         img
1534                     ]
1535                 };
1536
1537                 if(this.target){
1538                     a.target = _this.target;
1539                 }
1540             }
1541             
1542             cfg.cn.push((_this.href) ? a : img);
1543             
1544         });
1545         
1546         return cfg;
1547     },
1548     
1549     createSingleImg : function()
1550     {
1551         var cfg = {
1552             tag: 'img',
1553             cls: (this.imgResponsive) ? 'img-responsive' : '',
1554             html : null,
1555             src : 'about:blank'  // just incase src get's set to undefined?!?
1556         };
1557         
1558         cfg.html = this.html || cfg.html;
1559         
1560         cfg.src = this.src || cfg.src;
1561         
1562         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1563             cfg.cls += ' img-' + this.border;
1564         }
1565         
1566         if(this.alt){
1567             cfg.alt = this.alt;
1568         }
1569         
1570         if(this.href){
1571             var a = {
1572                 tag: 'a',
1573                 href: this.href,
1574                 cn: [
1575                     cfg
1576                 ]
1577             };
1578             
1579             if(this.target){
1580                 a.target = this.target;
1581             }
1582             
1583         }
1584         
1585         return (this.href) ? a : cfg;
1586     },
1587     
1588     initEvents: function() 
1589     {
1590         if(!this.href){
1591             this.el.on('click', this.onClick, this);
1592         }
1593         
1594     },
1595     
1596     onClick : function(e)
1597     {
1598         Roo.log('img onclick');
1599         this.fireEvent('click', this, e);
1600     },
1601     /**
1602      * Sets the url of the image - used to update it
1603      * @param {String} url the url of the image
1604      */
1605     
1606     setSrc : function(url)
1607     {
1608         this.src =  url;
1609         
1610         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1611             this.el.dom.src =  url;
1612             return;
1613         }
1614         
1615         this.el.select('img', true).first().dom.src =  url;
1616     }
1617     
1618     
1619    
1620 });
1621
1622  /*
1623  * - LGPL
1624  *
1625  * image
1626  * 
1627  */
1628
1629
1630 /**
1631  * @class Roo.bootstrap.Link
1632  * @extends Roo.bootstrap.Component
1633  * Bootstrap Link Class
1634  * @cfg {String} alt image alternative text
1635  * @cfg {String} href a tag href
1636  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1637  * @cfg {String} html the content of the link.
1638  * @cfg {String} anchor name for the anchor link
1639  * @cfg {String} fa - favicon
1640
1641  * @cfg {Boolean} preventDefault (true | false) default false
1642
1643  * 
1644  * @constructor
1645  * Create a new Input
1646  * @param {Object} config The config object
1647  */
1648
1649 Roo.bootstrap.Link = function(config){
1650     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1651     
1652     this.addEvents({
1653         // img events
1654         /**
1655          * @event click
1656          * The img click event for the img.
1657          * @param {Roo.EventObject} e
1658          */
1659         "click" : true
1660     });
1661 };
1662
1663 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1664     
1665     href: false,
1666     target: false,
1667     preventDefault: false,
1668     anchor : false,
1669     alt : false,
1670     fa: false,
1671
1672
1673     getAutoCreate : function()
1674     {
1675         var html = this.html || '';
1676         
1677         if (this.fa !== false) {
1678             html = '<i class="fa fa-' + this.fa + '"></i>';
1679         }
1680         var cfg = {
1681             tag: 'a'
1682         };
1683         // anchor's do not require html/href...
1684         if (this.anchor === false) {
1685             cfg.html = html;
1686             cfg.href = this.href || '#';
1687         } else {
1688             cfg.name = this.anchor;
1689             if (this.html !== false || this.fa !== false) {
1690                 cfg.html = html;
1691             }
1692             if (this.href !== false) {
1693                 cfg.href = this.href;
1694             }
1695         }
1696         
1697         if(this.alt !== false){
1698             cfg.alt = this.alt;
1699         }
1700         
1701         
1702         if(this.target !== false) {
1703             cfg.target = this.target;
1704         }
1705         
1706         return cfg;
1707     },
1708     
1709     initEvents: function() {
1710         
1711         if(!this.href || this.preventDefault){
1712             this.el.on('click', this.onClick, this);
1713         }
1714     },
1715     
1716     onClick : function(e)
1717     {
1718         if(this.preventDefault){
1719             e.preventDefault();
1720         }
1721         //Roo.log('img onclick');
1722         this.fireEvent('click', this, e);
1723     }
1724    
1725 });
1726
1727  /*
1728  * - LGPL
1729  *
1730  * header
1731  * 
1732  */
1733
1734 /**
1735  * @class Roo.bootstrap.Header
1736  * @extends Roo.bootstrap.Component
1737  * Bootstrap Header class
1738  * @cfg {String} html content of header
1739  * @cfg {Number} level (1|2|3|4|5|6) default 1
1740  * 
1741  * @constructor
1742  * Create a new Header
1743  * @param {Object} config The config object
1744  */
1745
1746
1747 Roo.bootstrap.Header  = function(config){
1748     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1749 };
1750
1751 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1752     
1753     //href : false,
1754     html : false,
1755     level : 1,
1756     
1757     
1758     
1759     getAutoCreate : function(){
1760         
1761         
1762         
1763         var cfg = {
1764             tag: 'h' + (1 *this.level),
1765             html: this.html || ''
1766         } ;
1767         
1768         return cfg;
1769     }
1770    
1771 });
1772
1773  
1774
1775  /*
1776  * Based on:
1777  * Ext JS Library 1.1.1
1778  * Copyright(c) 2006-2007, Ext JS, LLC.
1779  *
1780  * Originally Released Under LGPL - original licence link has changed is not relivant.
1781  *
1782  * Fork - LGPL
1783  * <script type="text/javascript">
1784  */
1785  
1786 /**
1787  * @class Roo.bootstrap.MenuMgr
1788  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1789  * @singleton
1790  */
1791 Roo.bootstrap.MenuMgr = function(){
1792    var menus, active, groups = {}, attached = false, lastShow = new Date();
1793
1794    // private - called when first menu is created
1795    function init(){
1796        menus = {};
1797        active = new Roo.util.MixedCollection();
1798        Roo.get(document).addKeyListener(27, function(){
1799            if(active.length > 0){
1800                hideAll();
1801            }
1802        });
1803    }
1804
1805    // private
1806    function hideAll(){
1807        if(active && active.length > 0){
1808            var c = active.clone();
1809            c.each(function(m){
1810                m.hide();
1811            });
1812        }
1813    }
1814
1815    // private
1816    function onHide(m){
1817        active.remove(m);
1818        if(active.length < 1){
1819            Roo.get(document).un("mouseup", onMouseDown);
1820             
1821            attached = false;
1822        }
1823    }
1824
1825    // private
1826    function onShow(m){
1827        var last = active.last();
1828        lastShow = new Date();
1829        active.add(m);
1830        if(!attached){
1831           Roo.get(document).on("mouseup", onMouseDown);
1832            
1833            attached = true;
1834        }
1835        if(m.parentMenu){
1836           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1837           m.parentMenu.activeChild = m;
1838        }else if(last && last.isVisible()){
1839           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1840        }
1841    }
1842
1843    // private
1844    function onBeforeHide(m){
1845        if(m.activeChild){
1846            m.activeChild.hide();
1847        }
1848        if(m.autoHideTimer){
1849            clearTimeout(m.autoHideTimer);
1850            delete m.autoHideTimer;
1851        }
1852    }
1853
1854    // private
1855    function onBeforeShow(m){
1856        var pm = m.parentMenu;
1857        if(!pm && !m.allowOtherMenus){
1858            hideAll();
1859        }else if(pm && pm.activeChild && active != m){
1860            pm.activeChild.hide();
1861        }
1862    }
1863
1864    // private this should really trigger on mouseup..
1865    function onMouseDown(e){
1866         Roo.log("on Mouse Up");
1867         
1868         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1869             Roo.log("MenuManager hideAll");
1870             hideAll();
1871             e.stopEvent();
1872         }
1873         
1874         
1875    }
1876
1877    // private
1878    function onBeforeCheck(mi, state){
1879        if(state){
1880            var g = groups[mi.group];
1881            for(var i = 0, l = g.length; i < l; i++){
1882                if(g[i] != mi){
1883                    g[i].setChecked(false);
1884                }
1885            }
1886        }
1887    }
1888
1889    return {
1890
1891        /**
1892         * Hides all menus that are currently visible
1893         */
1894        hideAll : function(){
1895             hideAll();  
1896        },
1897
1898        // private
1899        register : function(menu){
1900            if(!menus){
1901                init();
1902            }
1903            menus[menu.id] = menu;
1904            menu.on("beforehide", onBeforeHide);
1905            menu.on("hide", onHide);
1906            menu.on("beforeshow", onBeforeShow);
1907            menu.on("show", onShow);
1908            var g = menu.group;
1909            if(g && menu.events["checkchange"]){
1910                if(!groups[g]){
1911                    groups[g] = [];
1912                }
1913                groups[g].push(menu);
1914                menu.on("checkchange", onCheck);
1915            }
1916        },
1917
1918         /**
1919          * Returns a {@link Roo.menu.Menu} object
1920          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1921          * be used to generate and return a new Menu instance.
1922          */
1923        get : function(menu){
1924            if(typeof menu == "string"){ // menu id
1925                return menus[menu];
1926            }else if(menu.events){  // menu instance
1927                return menu;
1928            }
1929            /*else if(typeof menu.length == 'number'){ // array of menu items?
1930                return new Roo.bootstrap.Menu({items:menu});
1931            }else{ // otherwise, must be a config
1932                return new Roo.bootstrap.Menu(menu);
1933            }
1934            */
1935            return false;
1936        },
1937
1938        // private
1939        unregister : function(menu){
1940            delete menus[menu.id];
1941            menu.un("beforehide", onBeforeHide);
1942            menu.un("hide", onHide);
1943            menu.un("beforeshow", onBeforeShow);
1944            menu.un("show", onShow);
1945            var g = menu.group;
1946            if(g && menu.events["checkchange"]){
1947                groups[g].remove(menu);
1948                menu.un("checkchange", onCheck);
1949            }
1950        },
1951
1952        // private
1953        registerCheckable : function(menuItem){
1954            var g = menuItem.group;
1955            if(g){
1956                if(!groups[g]){
1957                    groups[g] = [];
1958                }
1959                groups[g].push(menuItem);
1960                menuItem.on("beforecheckchange", onBeforeCheck);
1961            }
1962        },
1963
1964        // private
1965        unregisterCheckable : function(menuItem){
1966            var g = menuItem.group;
1967            if(g){
1968                groups[g].remove(menuItem);
1969                menuItem.un("beforecheckchange", onBeforeCheck);
1970            }
1971        }
1972    };
1973 }();/*
1974  * - LGPL
1975  *
1976  * menu
1977  * 
1978  */
1979
1980 /**
1981  * @class Roo.bootstrap.Menu
1982  * @extends Roo.bootstrap.Component
1983  * Bootstrap Menu class - container for MenuItems
1984  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1985  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1986  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1987  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1988  * 
1989  * @constructor
1990  * Create a new Menu
1991  * @param {Object} config The config object
1992  */
1993
1994
1995 Roo.bootstrap.Menu = function(config){
1996     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1997     if (this.registerMenu && this.type != 'treeview')  {
1998         Roo.bootstrap.MenuMgr.register(this);
1999     }
2000     this.addEvents({
2001         /**
2002          * @event beforeshow
2003          * Fires before this menu is displayed
2004          * @param {Roo.menu.Menu} this
2005          */
2006         beforeshow : true,
2007         /**
2008          * @event beforehide
2009          * Fires before this menu is hidden
2010          * @param {Roo.menu.Menu} this
2011          */
2012         beforehide : true,
2013         /**
2014          * @event show
2015          * Fires after this menu is displayed
2016          * @param {Roo.menu.Menu} this
2017          */
2018         show : true,
2019         /**
2020          * @event hide
2021          * Fires after this menu is hidden
2022          * @param {Roo.menu.Menu} this
2023          */
2024         hide : true,
2025         /**
2026          * @event click
2027          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2028          * @param {Roo.menu.Menu} this
2029          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2030          * @param {Roo.EventObject} e
2031          */
2032         click : true,
2033         /**
2034          * @event mouseover
2035          * Fires when the mouse is hovering over this menu
2036          * @param {Roo.menu.Menu} this
2037          * @param {Roo.EventObject} e
2038          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2039          */
2040         mouseover : true,
2041         /**
2042          * @event mouseout
2043          * Fires when the mouse exits this menu
2044          * @param {Roo.menu.Menu} this
2045          * @param {Roo.EventObject} e
2046          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2047          */
2048         mouseout : true,
2049         /**
2050          * @event itemclick
2051          * Fires when a menu item contained in this menu is clicked
2052          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2053          * @param {Roo.EventObject} e
2054          */
2055         itemclick: true
2056     });
2057     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2058 };
2059
2060 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2061     
2062    /// html : false,
2063     //align : '',
2064     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2065     type: false,
2066     /**
2067      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2068      */
2069     registerMenu : true,
2070     
2071     menuItems :false, // stores the menu items..
2072     
2073     hidden:true,
2074         
2075     parentMenu : false,
2076     
2077     stopEvent : true,
2078     
2079     isLink : false,
2080     
2081     getChildContainer : function() {
2082         return this.el;  
2083     },
2084     
2085     getAutoCreate : function(){
2086          
2087         //if (['right'].indexOf(this.align)!==-1) {
2088         //    cfg.cn[1].cls += ' pull-right'
2089         //}
2090         
2091         
2092         var cfg = {
2093             tag : 'ul',
2094             cls : 'dropdown-menu' ,
2095             style : 'z-index:1000'
2096             
2097         };
2098         
2099         if (this.type === 'submenu') {
2100             cfg.cls = 'submenu active';
2101         }
2102         if (this.type === 'treeview') {
2103             cfg.cls = 'treeview-menu';
2104         }
2105         
2106         return cfg;
2107     },
2108     initEvents : function() {
2109         
2110        // Roo.log("ADD event");
2111        // Roo.log(this.triggerEl.dom);
2112         
2113         this.triggerEl.on('click', this.onTriggerClick, this);
2114         
2115         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2116         
2117         this.triggerEl.addClass('dropdown-toggle');
2118         
2119         if (Roo.isTouch) {
2120             this.el.on('touchstart'  , this.onTouch, this);
2121         }
2122         this.el.on('click' , this.onClick, this);
2123
2124         this.el.on("mouseover", this.onMouseOver, this);
2125         this.el.on("mouseout", this.onMouseOut, this);
2126         
2127     },
2128     
2129     findTargetItem : function(e)
2130     {
2131         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2132         if(!t){
2133             return false;
2134         }
2135         //Roo.log(t);         Roo.log(t.id);
2136         if(t && t.id){
2137             //Roo.log(this.menuitems);
2138             return this.menuitems.get(t.id);
2139             
2140             //return this.items.get(t.menuItemId);
2141         }
2142         
2143         return false;
2144     },
2145     
2146     onTouch : function(e) 
2147     {
2148         Roo.log("menu.onTouch");
2149         //e.stopEvent(); this make the user popdown broken
2150         this.onClick(e);
2151     },
2152     
2153     onClick : function(e)
2154     {
2155         Roo.log("menu.onClick");
2156         
2157         var t = this.findTargetItem(e);
2158         if(!t || t.isContainer){
2159             return;
2160         }
2161         Roo.log(e);
2162         /*
2163         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2164             if(t == this.activeItem && t.shouldDeactivate(e)){
2165                 this.activeItem.deactivate();
2166                 delete this.activeItem;
2167                 return;
2168             }
2169             if(t.canActivate){
2170                 this.setActiveItem(t, true);
2171             }
2172             return;
2173             
2174             
2175         }
2176         */
2177        
2178         Roo.log('pass click event');
2179         
2180         t.onClick(e);
2181         
2182         this.fireEvent("click", this, t, e);
2183         
2184         var _this = this;
2185         
2186         if(!t.href.length || t.href == '#'){
2187             (function() { _this.hide(); }).defer(100);
2188         }
2189         
2190     },
2191     
2192     onMouseOver : function(e){
2193         var t  = this.findTargetItem(e);
2194         //Roo.log(t);
2195         //if(t){
2196         //    if(t.canActivate && !t.disabled){
2197         //        this.setActiveItem(t, true);
2198         //    }
2199         //}
2200         
2201         this.fireEvent("mouseover", this, e, t);
2202     },
2203     isVisible : function(){
2204         return !this.hidden;
2205     },
2206      onMouseOut : function(e){
2207         var t  = this.findTargetItem(e);
2208         
2209         //if(t ){
2210         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2211         //        this.activeItem.deactivate();
2212         //        delete this.activeItem;
2213         //    }
2214         //}
2215         this.fireEvent("mouseout", this, e, t);
2216     },
2217     
2218     
2219     /**
2220      * Displays this menu relative to another element
2221      * @param {String/HTMLElement/Roo.Element} element The element to align to
2222      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2223      * the element (defaults to this.defaultAlign)
2224      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2225      */
2226     show : function(el, pos, parentMenu){
2227         this.parentMenu = parentMenu;
2228         if(!this.el){
2229             this.render();
2230         }
2231         this.fireEvent("beforeshow", this);
2232         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2233     },
2234      /**
2235      * Displays this menu at a specific xy position
2236      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2237      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2238      */
2239     showAt : function(xy, parentMenu, /* private: */_e){
2240         this.parentMenu = parentMenu;
2241         if(!this.el){
2242             this.render();
2243         }
2244         if(_e !== false){
2245             this.fireEvent("beforeshow", this);
2246             //xy = this.el.adjustForConstraints(xy);
2247         }
2248         
2249         //this.el.show();
2250         this.hideMenuItems();
2251         this.hidden = false;
2252         this.triggerEl.addClass('open');
2253         
2254         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2255             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2256         }
2257         
2258         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2259             this.el.setXY(xy);
2260         }
2261         
2262         this.focus();
2263         this.fireEvent("show", this);
2264     },
2265     
2266     focus : function(){
2267         return;
2268         if(!this.hidden){
2269             this.doFocus.defer(50, this);
2270         }
2271     },
2272
2273     doFocus : function(){
2274         if(!this.hidden){
2275             this.focusEl.focus();
2276         }
2277     },
2278
2279     /**
2280      * Hides this menu and optionally all parent menus
2281      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2282      */
2283     hide : function(deep)
2284     {
2285         
2286         this.hideMenuItems();
2287         if(this.el && this.isVisible()){
2288             this.fireEvent("beforehide", this);
2289             if(this.activeItem){
2290                 this.activeItem.deactivate();
2291                 this.activeItem = null;
2292             }
2293             this.triggerEl.removeClass('open');;
2294             this.hidden = true;
2295             this.fireEvent("hide", this);
2296         }
2297         if(deep === true && this.parentMenu){
2298             this.parentMenu.hide(true);
2299         }
2300     },
2301     
2302     onTriggerClick : function(e)
2303     {
2304         Roo.log('trigger click');
2305         
2306         var target = e.getTarget();
2307         
2308         Roo.log(target.nodeName.toLowerCase());
2309         
2310         if(target.nodeName.toLowerCase() === 'i'){
2311             e.preventDefault();
2312         }
2313         
2314     },
2315     
2316     onTriggerPress  : function(e)
2317     {
2318         Roo.log('trigger press');
2319         //Roo.log(e.getTarget());
2320        // Roo.log(this.triggerEl.dom);
2321        
2322         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2323         var pel = Roo.get(e.getTarget());
2324         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2325             Roo.log('is treeview or dropdown?');
2326             return;
2327         }
2328         
2329         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2330             return;
2331         }
2332         
2333         if (this.isVisible()) {
2334             Roo.log('hide');
2335             this.hide();
2336         } else {
2337             Roo.log('show');
2338             this.show(this.triggerEl, false, false);
2339         }
2340         
2341         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2342             e.stopEvent();
2343         }
2344         
2345     },
2346        
2347     
2348     hideMenuItems : function()
2349     {
2350         Roo.log("hide Menu Items");
2351         if (!this.el) { 
2352             return;
2353         }
2354         //$(backdrop).remove()
2355         this.el.select('.open',true).each(function(aa) {
2356             
2357             aa.removeClass('open');
2358           //var parent = getParent($(this))
2359           //var relatedTarget = { relatedTarget: this }
2360           
2361            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2362           //if (e.isDefaultPrevented()) return
2363            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2364         });
2365     },
2366     addxtypeChild : function (tree, cntr) {
2367         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2368           
2369         this.menuitems.add(comp);
2370         return comp;
2371
2372     },
2373     getEl : function()
2374     {
2375         Roo.log(this.el);
2376         return this.el;
2377     },
2378     
2379     clear : function()
2380     {
2381         this.getEl().dom.innerHTML = '';
2382         this.menuitems.clear();
2383     }
2384 });
2385
2386  
2387  /*
2388  * - LGPL
2389  *
2390  * menu item
2391  * 
2392  */
2393
2394
2395 /**
2396  * @class Roo.bootstrap.MenuItem
2397  * @extends Roo.bootstrap.Component
2398  * Bootstrap MenuItem class
2399  * @cfg {String} html the menu label
2400  * @cfg {String} href the link
2401  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2402  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2403  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2404  * @cfg {String} fa favicon to show on left of menu item.
2405  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2406  * 
2407  * 
2408  * @constructor
2409  * Create a new MenuItem
2410  * @param {Object} config The config object
2411  */
2412
2413
2414 Roo.bootstrap.MenuItem = function(config){
2415     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2416     this.addEvents({
2417         // raw events
2418         /**
2419          * @event click
2420          * The raw click event for the entire grid.
2421          * @param {Roo.bootstrap.MenuItem} this
2422          * @param {Roo.EventObject} e
2423          */
2424         "click" : true
2425     });
2426 };
2427
2428 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2429     
2430     href : false,
2431     html : false,
2432     preventDefault: false,
2433     isContainer : false,
2434     active : false,
2435     fa: false,
2436     
2437     getAutoCreate : function(){
2438         
2439         if(this.isContainer){
2440             return {
2441                 tag: 'li',
2442                 cls: 'dropdown-menu-item'
2443             };
2444         }
2445         var ctag = {
2446             tag: 'span',
2447             html: 'Link'
2448         };
2449         
2450         var anc = {
2451             tag : 'a',
2452             href : '#',
2453             cn : [  ]
2454         };
2455         
2456         if (this.fa !== false) {
2457             anc.cn.push({
2458                 tag : 'i',
2459                 cls : 'fa fa-' + this.fa
2460             });
2461         }
2462         
2463         anc.cn.push(ctag);
2464         
2465         
2466         var cfg= {
2467             tag: 'li',
2468             cls: 'dropdown-menu-item',
2469             cn: [ anc ]
2470         };
2471         if (this.parent().type == 'treeview') {
2472             cfg.cls = 'treeview-menu';
2473         }
2474         if (this.active) {
2475             cfg.cls += ' active';
2476         }
2477         
2478         
2479         
2480         anc.href = this.href || cfg.cn[0].href ;
2481         ctag.html = this.html || cfg.cn[0].html ;
2482         return cfg;
2483     },
2484     
2485     initEvents: function()
2486     {
2487         if (this.parent().type == 'treeview') {
2488             this.el.select('a').on('click', this.onClick, this);
2489         }
2490         
2491         if (this.menu) {
2492             this.menu.parentType = this.xtype;
2493             this.menu.triggerEl = this.el;
2494             this.menu = this.addxtype(Roo.apply({}, this.menu));
2495         }
2496         
2497     },
2498     onClick : function(e)
2499     {
2500         Roo.log('item on click ');
2501         
2502         if(this.preventDefault){
2503             e.preventDefault();
2504         }
2505         //this.parent().hideMenuItems();
2506         
2507         this.fireEvent('click', this, e);
2508     },
2509     getEl : function()
2510     {
2511         return this.el;
2512     } 
2513 });
2514
2515  
2516
2517  /*
2518  * - LGPL
2519  *
2520  * menu separator
2521  * 
2522  */
2523
2524
2525 /**
2526  * @class Roo.bootstrap.MenuSeparator
2527  * @extends Roo.bootstrap.Component
2528  * Bootstrap MenuSeparator class
2529  * 
2530  * @constructor
2531  * Create a new MenuItem
2532  * @param {Object} config The config object
2533  */
2534
2535
2536 Roo.bootstrap.MenuSeparator = function(config){
2537     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2538 };
2539
2540 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2541     
2542     getAutoCreate : function(){
2543         var cfg = {
2544             cls: 'divider',
2545             tag : 'li'
2546         };
2547         
2548         return cfg;
2549     }
2550    
2551 });
2552
2553  
2554
2555  
2556 /*
2557 * Licence: LGPL
2558 */
2559
2560 /**
2561  * @class Roo.bootstrap.Modal
2562  * @extends Roo.bootstrap.Component
2563  * Bootstrap Modal class
2564  * @cfg {String} title Title of dialog
2565  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2566  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2567  * @cfg {Boolean} specificTitle default false
2568  * @cfg {Array} buttons Array of buttons or standard button set..
2569  * @cfg {String} buttonPosition (left|right|center) default right
2570  * @cfg {Boolean} animate default true
2571  * @cfg {Boolean} allow_close default true
2572  * @cfg {Boolean} fitwindow default false
2573  * @cfg {String} size (sm|lg) default empty
2574  *
2575  *
2576  * @constructor
2577  * Create a new Modal Dialog
2578  * @param {Object} config The config object
2579  */
2580
2581 Roo.bootstrap.Modal = function(config){
2582     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2583     this.addEvents({
2584         // raw events
2585         /**
2586          * @event btnclick
2587          * The raw btnclick event for the button
2588          * @param {Roo.EventObject} e
2589          */
2590         "btnclick" : true,
2591         /**
2592          * @event resize
2593          * Fire when dialog resize
2594          * @param {Roo.bootstrap.Modal} this
2595          * @param {Roo.EventObject} e
2596          */
2597         "resize" : true
2598     });
2599     this.buttons = this.buttons || [];
2600
2601     if (this.tmpl) {
2602         this.tmpl = Roo.factory(this.tmpl);
2603     }
2604
2605 };
2606
2607 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2608
2609     title : 'test dialog',
2610
2611     buttons : false,
2612
2613     // set on load...
2614
2615     html: false,
2616
2617     tmp: false,
2618
2619     specificTitle: false,
2620
2621     buttonPosition: 'right',
2622
2623     allow_close : true,
2624
2625     animate : true,
2626
2627     fitwindow: false,
2628
2629
2630      // private
2631     dialogEl: false,
2632     bodyEl:  false,
2633     footerEl:  false,
2634     titleEl:  false,
2635     closeEl:  false,
2636
2637     size: '',
2638
2639
2640     onRender : function(ct, position)
2641     {
2642         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2643
2644         if(!this.el){
2645             var cfg = Roo.apply({},  this.getAutoCreate());
2646             cfg.id = Roo.id();
2647             //if(!cfg.name){
2648             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2649             //}
2650             //if (!cfg.name.length) {
2651             //    delete cfg.name;
2652            // }
2653             if (this.cls) {
2654                 cfg.cls += ' ' + this.cls;
2655             }
2656             if (this.style) {
2657                 cfg.style = this.style;
2658             }
2659             this.el = Roo.get(document.body).createChild(cfg, position);
2660         }
2661         //var type = this.el.dom.type;
2662
2663
2664         if(this.tabIndex !== undefined){
2665             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2666         }
2667
2668         this.dialogEl = this.el.select('.modal-dialog',true).first();
2669         this.bodyEl = this.el.select('.modal-body',true).first();
2670         this.closeEl = this.el.select('.modal-header .close', true).first();
2671         this.headerEl = this.el.select('.modal-header',true).first();
2672         this.titleEl = this.el.select('.modal-title',true).first();
2673         this.footerEl = this.el.select('.modal-footer',true).first();
2674
2675         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2676         
2677         //this.el.addClass("x-dlg-modal");
2678
2679         if (this.buttons.length) {
2680             Roo.each(this.buttons, function(bb) {
2681                 var b = Roo.apply({}, bb);
2682                 b.xns = b.xns || Roo.bootstrap;
2683                 b.xtype = b.xtype || 'Button';
2684                 if (typeof(b.listeners) == 'undefined') {
2685                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2686                 }
2687
2688                 var btn = Roo.factory(b);
2689
2690                 btn.render(this.el.select('.modal-footer div').first());
2691
2692             },this);
2693         }
2694         // render the children.
2695         var nitems = [];
2696
2697         if(typeof(this.items) != 'undefined'){
2698             var items = this.items;
2699             delete this.items;
2700
2701             for(var i =0;i < items.length;i++) {
2702                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2703             }
2704         }
2705
2706         this.items = nitems;
2707
2708         // where are these used - they used to be body/close/footer
2709
2710
2711         this.initEvents();
2712         //this.el.addClass([this.fieldClass, this.cls]);
2713
2714     },
2715
2716     getAutoCreate : function(){
2717
2718
2719         var bdy = {
2720                 cls : 'modal-body',
2721                 html : this.html || ''
2722         };
2723
2724         var title = {
2725             tag: 'h4',
2726             cls : 'modal-title',
2727             html : this.title
2728         };
2729
2730         if(this.specificTitle){
2731             title = this.title;
2732
2733         };
2734
2735         var header = [];
2736         if (this.allow_close) {
2737             header.push({
2738                 tag: 'button',
2739                 cls : 'close',
2740                 html : '&times'
2741             });
2742         }
2743
2744         header.push(title);
2745
2746         var size = '';
2747
2748         if(this.size.length){
2749             size = 'modal-' + this.size;
2750         }
2751
2752         var modal = {
2753             cls: "modal",
2754              cn : [
2755                 {
2756                     cls: "modal-dialog " + size,
2757                     cn : [
2758                         {
2759                             cls : "modal-content",
2760                             cn : [
2761                                 {
2762                                     cls : 'modal-header',
2763                                     cn : header
2764                                 },
2765                                 bdy,
2766                                 {
2767                                     cls : 'modal-footer',
2768                                     cn : [
2769                                         {
2770                                             tag: 'div',
2771                                             cls: 'btn-' + this.buttonPosition
2772                                         }
2773                                     ]
2774
2775                                 }
2776
2777
2778                             ]
2779
2780                         }
2781                     ]
2782
2783                 }
2784             ]
2785         };
2786
2787         if(this.animate){
2788             modal.cls += ' fade';
2789         }
2790
2791         return modal;
2792
2793     },
2794     getChildContainer : function() {
2795
2796          return this.bodyEl;
2797
2798     },
2799     getButtonContainer : function() {
2800          return this.el.select('.modal-footer div',true).first();
2801
2802     },
2803     initEvents : function()
2804     {
2805         if (this.allow_close) {
2806             this.closeEl.on('click', this.hide, this);
2807         }
2808         Roo.EventManager.onWindowResize(this.resize, this, true);
2809
2810
2811     },
2812
2813     resize : function()
2814     {
2815         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2816         if (this.fitwindow) {
2817             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2818             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2819             this.setSize(w,h);
2820         }
2821     },
2822
2823     setSize : function(w,h)
2824     {
2825         if (!w && !h) {
2826             return;
2827         }
2828         this.resizeTo(w,h);
2829     },
2830
2831     show : function() {
2832
2833         if (!this.rendered) {
2834             this.render();
2835         }
2836
2837         //this.el.setStyle('display', 'block');
2838         this.el.removeClass('hideing');        
2839         this.el.addClass('show');
2840  
2841         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2842             var _this = this;
2843             (function(){
2844                 this.el.addClass('in');
2845             }).defer(50, this);
2846         }else{
2847             this.el.addClass('in');
2848
2849         }
2850
2851         // not sure how we can show data in here..
2852         //if (this.tmpl) {
2853         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2854         //}
2855
2856         Roo.get(document.body).addClass("x-body-masked");
2857         
2858         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2859         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2860         this.maskEl.addClass('show');
2861         
2862         this.resize();
2863         
2864         this.fireEvent('show', this);
2865
2866         // set zindex here - otherwise it appears to be ignored...
2867         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2868
2869         (function () {
2870             this.items.forEach( function(e) {
2871                 e.layout ? e.layout() : false;
2872
2873             });
2874         }).defer(100,this);
2875
2876     },
2877     hide : function()
2878     {
2879         if(this.fireEvent("beforehide", this) !== false){
2880             this.maskEl.removeClass('show');
2881             Roo.get(document.body).removeClass("x-body-masked");
2882             this.el.removeClass('in');
2883             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2884
2885             if(this.animate){ // why
2886                 this.el.addClass('hideing');
2887                 (function(){
2888                     if (!this.el.hasClass('hideing')) {
2889                         return; // it's been shown again...
2890                     }
2891                     this.el.removeClass('show');
2892                     this.el.removeClass('hideing');
2893                 }).defer(150,this);
2894                 
2895             }else{
2896                  this.el.removeClass('show');
2897             }
2898             this.fireEvent('hide', this);
2899         }
2900     },
2901     isVisible : function()
2902     {
2903         
2904         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2905         
2906     },
2907
2908     addButton : function(str, cb)
2909     {
2910
2911
2912         var b = Roo.apply({}, { html : str } );
2913         b.xns = b.xns || Roo.bootstrap;
2914         b.xtype = b.xtype || 'Button';
2915         if (typeof(b.listeners) == 'undefined') {
2916             b.listeners = { click : cb.createDelegate(this)  };
2917         }
2918
2919         var btn = Roo.factory(b);
2920
2921         btn.render(this.el.select('.modal-footer div').first());
2922
2923         return btn;
2924
2925     },
2926
2927     setDefaultButton : function(btn)
2928     {
2929         //this.el.select('.modal-footer').()
2930     },
2931     diff : false,
2932
2933     resizeTo: function(w,h)
2934     {
2935         // skip.. ?? why??
2936
2937         this.dialogEl.setWidth(w);
2938         if (this.diff === false) {
2939             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2940         }
2941
2942         this.bodyEl.setHeight(h-this.diff);
2943
2944         this.fireEvent('resize', this);
2945
2946     },
2947     setContentSize  : function(w, h)
2948     {
2949
2950     },
2951     onButtonClick: function(btn,e)
2952     {
2953         //Roo.log([a,b,c]);
2954         this.fireEvent('btnclick', btn.name, e);
2955     },
2956      /**
2957      * Set the title of the Dialog
2958      * @param {String} str new Title
2959      */
2960     setTitle: function(str) {
2961         this.titleEl.dom.innerHTML = str;
2962     },
2963     /**
2964      * Set the body of the Dialog
2965      * @param {String} str new Title
2966      */
2967     setBody: function(str) {
2968         this.bodyEl.dom.innerHTML = str;
2969     },
2970     /**
2971      * Set the body of the Dialog using the template
2972      * @param {Obj} data - apply this data to the template and replace the body contents.
2973      */
2974     applyBody: function(obj)
2975     {
2976         if (!this.tmpl) {
2977             Roo.log("Error - using apply Body without a template");
2978             //code
2979         }
2980         this.tmpl.overwrite(this.bodyEl, obj);
2981     }
2982
2983 });
2984
2985
2986 Roo.apply(Roo.bootstrap.Modal,  {
2987     /**
2988          * Button config that displays a single OK button
2989          * @type Object
2990          */
2991         OK :  [{
2992             name : 'ok',
2993             weight : 'primary',
2994             html : 'OK'
2995         }],
2996         /**
2997          * Button config that displays Yes and No buttons
2998          * @type Object
2999          */
3000         YESNO : [
3001             {
3002                 name  : 'no',
3003                 html : 'No'
3004             },
3005             {
3006                 name  :'yes',
3007                 weight : 'primary',
3008                 html : 'Yes'
3009             }
3010         ],
3011
3012         /**
3013          * Button config that displays OK and Cancel buttons
3014          * @type Object
3015          */
3016         OKCANCEL : [
3017             {
3018                name : 'cancel',
3019                 html : 'Cancel'
3020             },
3021             {
3022                 name : 'ok',
3023                 weight : 'primary',
3024                 html : 'OK'
3025             }
3026         ],
3027         /**
3028          * Button config that displays Yes, No and Cancel buttons
3029          * @type Object
3030          */
3031         YESNOCANCEL : [
3032             {
3033                 name : 'yes',
3034                 weight : 'primary',
3035                 html : 'Yes'
3036             },
3037             {
3038                 name : 'no',
3039                 html : 'No'
3040             },
3041             {
3042                 name : 'cancel',
3043                 html : 'Cancel'
3044             }
3045         ],
3046         
3047         zIndex : 10001
3048 });
3049 /*
3050  * - LGPL
3051  *
3052  * messagebox - can be used as a replace
3053  * 
3054  */
3055 /**
3056  * @class Roo.MessageBox
3057  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3058  * Example usage:
3059  *<pre><code>
3060 // Basic alert:
3061 Roo.Msg.alert('Status', 'Changes saved successfully.');
3062
3063 // Prompt for user data:
3064 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3065     if (btn == 'ok'){
3066         // process text value...
3067     }
3068 });
3069
3070 // Show a dialog using config options:
3071 Roo.Msg.show({
3072    title:'Save Changes?',
3073    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3074    buttons: Roo.Msg.YESNOCANCEL,
3075    fn: processResult,
3076    animEl: 'elId'
3077 });
3078 </code></pre>
3079  * @singleton
3080  */
3081 Roo.bootstrap.MessageBox = function(){
3082     var dlg, opt, mask, waitTimer;
3083     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3084     var buttons, activeTextEl, bwidth;
3085
3086     
3087     // private
3088     var handleButton = function(button){
3089         dlg.hide();
3090         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3091     };
3092
3093     // private
3094     var handleHide = function(){
3095         if(opt && opt.cls){
3096             dlg.el.removeClass(opt.cls);
3097         }
3098         //if(waitTimer){
3099         //    Roo.TaskMgr.stop(waitTimer);
3100         //    waitTimer = null;
3101         //}
3102     };
3103
3104     // private
3105     var updateButtons = function(b){
3106         var width = 0;
3107         if(!b){
3108             buttons["ok"].hide();
3109             buttons["cancel"].hide();
3110             buttons["yes"].hide();
3111             buttons["no"].hide();
3112             //dlg.footer.dom.style.display = 'none';
3113             return width;
3114         }
3115         dlg.footerEl.dom.style.display = '';
3116         for(var k in buttons){
3117             if(typeof buttons[k] != "function"){
3118                 if(b[k]){
3119                     buttons[k].show();
3120                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3121                     width += buttons[k].el.getWidth()+15;
3122                 }else{
3123                     buttons[k].hide();
3124                 }
3125             }
3126         }
3127         return width;
3128     };
3129
3130     // private
3131     var handleEsc = function(d, k, e){
3132         if(opt && opt.closable !== false){
3133             dlg.hide();
3134         }
3135         if(e){
3136             e.stopEvent();
3137         }
3138     };
3139
3140     return {
3141         /**
3142          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3143          * @return {Roo.BasicDialog} The BasicDialog element
3144          */
3145         getDialog : function(){
3146            if(!dlg){
3147                 dlg = new Roo.bootstrap.Modal( {
3148                     //draggable: true,
3149                     //resizable:false,
3150                     //constraintoviewport:false,
3151                     //fixedcenter:true,
3152                     //collapsible : false,
3153                     //shim:true,
3154                     //modal: true,
3155                 //    width: 'auto',
3156                   //  height:100,
3157                     //buttonAlign:"center",
3158                     closeClick : function(){
3159                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3160                             handleButton("no");
3161                         }else{
3162                             handleButton("cancel");
3163                         }
3164                     }
3165                 });
3166                 dlg.render();
3167                 dlg.on("hide", handleHide);
3168                 mask = dlg.mask;
3169                 //dlg.addKeyListener(27, handleEsc);
3170                 buttons = {};
3171                 this.buttons = buttons;
3172                 var bt = this.buttonText;
3173                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3174                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3175                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3176                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3177                 //Roo.log(buttons);
3178                 bodyEl = dlg.bodyEl.createChild({
3179
3180                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3181                         '<textarea class="roo-mb-textarea"></textarea>' +
3182                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3183                 });
3184                 msgEl = bodyEl.dom.firstChild;
3185                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3186                 textboxEl.enableDisplayMode();
3187                 textboxEl.addKeyListener([10,13], function(){
3188                     if(dlg.isVisible() && opt && opt.buttons){
3189                         if(opt.buttons.ok){
3190                             handleButton("ok");
3191                         }else if(opt.buttons.yes){
3192                             handleButton("yes");
3193                         }
3194                     }
3195                 });
3196                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3197                 textareaEl.enableDisplayMode();
3198                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3199                 progressEl.enableDisplayMode();
3200                 
3201                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3202                 var pf = progressEl.dom.firstChild;
3203                 if (pf) {
3204                     pp = Roo.get(pf.firstChild);
3205                     pp.setHeight(pf.offsetHeight);
3206                 }
3207                 
3208             }
3209             return dlg;
3210         },
3211
3212         /**
3213          * Updates the message box body text
3214          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3215          * the XHTML-compliant non-breaking space character '&amp;#160;')
3216          * @return {Roo.MessageBox} This message box
3217          */
3218         updateText : function(text)
3219         {
3220             if(!dlg.isVisible() && !opt.width){
3221                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3222                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3223             }
3224             msgEl.innerHTML = text || '&#160;';
3225       
3226             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3227             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3228             var w = Math.max(
3229                     Math.min(opt.width || cw , this.maxWidth), 
3230                     Math.max(opt.minWidth || this.minWidth, bwidth)
3231             );
3232             if(opt.prompt){
3233                 activeTextEl.setWidth(w);
3234             }
3235             if(dlg.isVisible()){
3236                 dlg.fixedcenter = false;
3237             }
3238             // to big, make it scroll. = But as usual stupid IE does not support
3239             // !important..
3240             
3241             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3242                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3243                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3244             } else {
3245                 bodyEl.dom.style.height = '';
3246                 bodyEl.dom.style.overflowY = '';
3247             }
3248             if (cw > w) {
3249                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3250             } else {
3251                 bodyEl.dom.style.overflowX = '';
3252             }
3253             
3254             dlg.setContentSize(w, bodyEl.getHeight());
3255             if(dlg.isVisible()){
3256                 dlg.fixedcenter = true;
3257             }
3258             return this;
3259         },
3260
3261         /**
3262          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3263          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3264          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3265          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3266          * @return {Roo.MessageBox} This message box
3267          */
3268         updateProgress : function(value, text){
3269             if(text){
3270                 this.updateText(text);
3271             }
3272             
3273             if (pp) { // weird bug on my firefox - for some reason this is not defined
3274                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3275                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3276             }
3277             return this;
3278         },        
3279
3280         /**
3281          * Returns true if the message box is currently displayed
3282          * @return {Boolean} True if the message box is visible, else false
3283          */
3284         isVisible : function(){
3285             return dlg && dlg.isVisible();  
3286         },
3287
3288         /**
3289          * Hides the message box if it is displayed
3290          */
3291         hide : function(){
3292             if(this.isVisible()){
3293                 dlg.hide();
3294             }  
3295         },
3296
3297         /**
3298          * Displays a new message box, or reinitializes an existing message box, based on the config options
3299          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3300          * The following config object properties are supported:
3301          * <pre>
3302 Property    Type             Description
3303 ----------  ---------------  ------------------------------------------------------------------------------------
3304 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3305                                    closes (defaults to undefined)
3306 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3307                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3308 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3309                                    progress and wait dialogs will ignore this property and always hide the
3310                                    close button as they can only be closed programmatically.
3311 cls               String           A custom CSS class to apply to the message box element
3312 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3313                                    displayed (defaults to 75)
3314 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3315                                    function will be btn (the name of the button that was clicked, if applicable,
3316                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3317                                    Progress and wait dialogs will ignore this option since they do not respond to
3318                                    user actions and can only be closed programmatically, so any required function
3319                                    should be called by the same code after it closes the dialog.
3320 icon              String           A CSS class that provides a background image to be used as an icon for
3321                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3322 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3323 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3324 modal             Boolean          False to allow user interaction with the page while the message box is
3325                                    displayed (defaults to true)
3326 msg               String           A string that will replace the existing message box body text (defaults
3327                                    to the XHTML-compliant non-breaking space character '&#160;')
3328 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3329 progress          Boolean          True to display a progress bar (defaults to false)
3330 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3331 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3332 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3333 title             String           The title text
3334 value             String           The string value to set into the active textbox element if displayed
3335 wait              Boolean          True to display a progress bar (defaults to false)
3336 width             Number           The width of the dialog in pixels
3337 </pre>
3338          *
3339          * Example usage:
3340          * <pre><code>
3341 Roo.Msg.show({
3342    title: 'Address',
3343    msg: 'Please enter your address:',
3344    width: 300,
3345    buttons: Roo.MessageBox.OKCANCEL,
3346    multiline: true,
3347    fn: saveAddress,
3348    animEl: 'addAddressBtn'
3349 });
3350 </code></pre>
3351          * @param {Object} config Configuration options
3352          * @return {Roo.MessageBox} This message box
3353          */
3354         show : function(options)
3355         {
3356             
3357             // this causes nightmares if you show one dialog after another
3358             // especially on callbacks..
3359              
3360             if(this.isVisible()){
3361                 
3362                 this.hide();
3363                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3364                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3365                 Roo.log("New Dialog Message:" +  options.msg )
3366                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3367                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3368                 
3369             }
3370             var d = this.getDialog();
3371             opt = options;
3372             d.setTitle(opt.title || "&#160;");
3373             d.closeEl.setDisplayed(opt.closable !== false);
3374             activeTextEl = textboxEl;
3375             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3376             if(opt.prompt){
3377                 if(opt.multiline){
3378                     textboxEl.hide();
3379                     textareaEl.show();
3380                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3381                         opt.multiline : this.defaultTextHeight);
3382                     activeTextEl = textareaEl;
3383                 }else{
3384                     textboxEl.show();
3385                     textareaEl.hide();
3386                 }
3387             }else{
3388                 textboxEl.hide();
3389                 textareaEl.hide();
3390             }
3391             progressEl.setDisplayed(opt.progress === true);
3392             this.updateProgress(0);
3393             activeTextEl.dom.value = opt.value || "";
3394             if(opt.prompt){
3395                 dlg.setDefaultButton(activeTextEl);
3396             }else{
3397                 var bs = opt.buttons;
3398                 var db = null;
3399                 if(bs && bs.ok){
3400                     db = buttons["ok"];
3401                 }else if(bs && bs.yes){
3402                     db = buttons["yes"];
3403                 }
3404                 dlg.setDefaultButton(db);
3405             }
3406             bwidth = updateButtons(opt.buttons);
3407             this.updateText(opt.msg);
3408             if(opt.cls){
3409                 d.el.addClass(opt.cls);
3410             }
3411             d.proxyDrag = opt.proxyDrag === true;
3412             d.modal = opt.modal !== false;
3413             d.mask = opt.modal !== false ? mask : false;
3414             if(!d.isVisible()){
3415                 // force it to the end of the z-index stack so it gets a cursor in FF
3416                 document.body.appendChild(dlg.el.dom);
3417                 d.animateTarget = null;
3418                 d.show(options.animEl);
3419             }
3420             return this;
3421         },
3422
3423         /**
3424          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3425          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3426          * and closing the message box when the process is complete.
3427          * @param {String} title The title bar text
3428          * @param {String} msg The message box body text
3429          * @return {Roo.MessageBox} This message box
3430          */
3431         progress : function(title, msg){
3432             this.show({
3433                 title : title,
3434                 msg : msg,
3435                 buttons: false,
3436                 progress:true,
3437                 closable:false,
3438                 minWidth: this.minProgressWidth,
3439                 modal : true
3440             });
3441             return this;
3442         },
3443
3444         /**
3445          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3446          * If a callback function is passed it will be called after the user clicks the button, and the
3447          * id of the button that was clicked will be passed as the only parameter to the callback
3448          * (could also be the top-right close button).
3449          * @param {String} title The title bar text
3450          * @param {String} msg The message box body text
3451          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3452          * @param {Object} scope (optional) The scope of the callback function
3453          * @return {Roo.MessageBox} This message box
3454          */
3455         alert : function(title, msg, fn, scope)
3456         {
3457             this.show({
3458                 title : title,
3459                 msg : msg,
3460                 buttons: this.OK,
3461                 fn: fn,
3462                 closable : false,
3463                 scope : scope,
3464                 modal : true
3465             });
3466             return this;
3467         },
3468
3469         /**
3470          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3471          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3472          * You are responsible for closing the message box when the process is complete.
3473          * @param {String} msg The message box body text
3474          * @param {String} title (optional) The title bar text
3475          * @return {Roo.MessageBox} This message box
3476          */
3477         wait : function(msg, title){
3478             this.show({
3479                 title : title,
3480                 msg : msg,
3481                 buttons: false,
3482                 closable:false,
3483                 progress:true,
3484                 modal:true,
3485                 width:300,
3486                 wait:true
3487             });
3488             waitTimer = Roo.TaskMgr.start({
3489                 run: function(i){
3490                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3491                 },
3492                 interval: 1000
3493             });
3494             return this;
3495         },
3496
3497         /**
3498          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3499          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3500          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3501          * @param {String} title The title bar text
3502          * @param {String} msg The message box body text
3503          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3504          * @param {Object} scope (optional) The scope of the callback function
3505          * @return {Roo.MessageBox} This message box
3506          */
3507         confirm : function(title, msg, fn, scope){
3508             this.show({
3509                 title : title,
3510                 msg : msg,
3511                 buttons: this.YESNO,
3512                 fn: fn,
3513                 scope : scope,
3514                 modal : true
3515             });
3516             return this;
3517         },
3518
3519         /**
3520          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3521          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3522          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3523          * (could also be the top-right close button) and the text that was entered will be passed as the two
3524          * parameters to the callback.
3525          * @param {String} title The title bar text
3526          * @param {String} msg The message box body text
3527          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3528          * @param {Object} scope (optional) The scope of the callback function
3529          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3530          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3531          * @return {Roo.MessageBox} This message box
3532          */
3533         prompt : function(title, msg, fn, scope, multiline){
3534             this.show({
3535                 title : title,
3536                 msg : msg,
3537                 buttons: this.OKCANCEL,
3538                 fn: fn,
3539                 minWidth:250,
3540                 scope : scope,
3541                 prompt:true,
3542                 multiline: multiline,
3543                 modal : true
3544             });
3545             return this;
3546         },
3547
3548         /**
3549          * Button config that displays a single OK button
3550          * @type Object
3551          */
3552         OK : {ok:true},
3553         /**
3554          * Button config that displays Yes and No buttons
3555          * @type Object
3556          */
3557         YESNO : {yes:true, no:true},
3558         /**
3559          * Button config that displays OK and Cancel buttons
3560          * @type Object
3561          */
3562         OKCANCEL : {ok:true, cancel:true},
3563         /**
3564          * Button config that displays Yes, No and Cancel buttons
3565          * @type Object
3566          */
3567         YESNOCANCEL : {yes:true, no:true, cancel:true},
3568
3569         /**
3570          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3571          * @type Number
3572          */
3573         defaultTextHeight : 75,
3574         /**
3575          * The maximum width in pixels of the message box (defaults to 600)
3576          * @type Number
3577          */
3578         maxWidth : 600,
3579         /**
3580          * The minimum width in pixels of the message box (defaults to 100)
3581          * @type Number
3582          */
3583         minWidth : 100,
3584         /**
3585          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3586          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3587          * @type Number
3588          */
3589         minProgressWidth : 250,
3590         /**
3591          * An object containing the default button text strings that can be overriden for localized language support.
3592          * Supported properties are: ok, cancel, yes and no.
3593          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3594          * @type Object
3595          */
3596         buttonText : {
3597             ok : "OK",
3598             cancel : "Cancel",
3599             yes : "Yes",
3600             no : "No"
3601         }
3602     };
3603 }();
3604
3605 /**
3606  * Shorthand for {@link Roo.MessageBox}
3607  */
3608 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3609 Roo.Msg = Roo.Msg || Roo.MessageBox;
3610 /*
3611  * - LGPL
3612  *
3613  * navbar
3614  * 
3615  */
3616
3617 /**
3618  * @class Roo.bootstrap.Navbar
3619  * @extends Roo.bootstrap.Component
3620  * Bootstrap Navbar class
3621
3622  * @constructor
3623  * Create a new Navbar
3624  * @param {Object} config The config object
3625  */
3626
3627
3628 Roo.bootstrap.Navbar = function(config){
3629     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3630     this.addEvents({
3631         // raw events
3632         /**
3633          * @event beforetoggle
3634          * Fire before toggle the menu
3635          * @param {Roo.EventObject} e
3636          */
3637         "beforetoggle" : true
3638     });
3639 };
3640
3641 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3642     
3643     
3644    
3645     // private
3646     navItems : false,
3647     loadMask : false,
3648     
3649     
3650     getAutoCreate : function(){
3651         
3652         
3653         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3654         
3655     },
3656     
3657     initEvents :function ()
3658     {
3659         //Roo.log(this.el.select('.navbar-toggle',true));
3660         this.el.select('.navbar-toggle',true).on('click', function() {
3661             if(this.fireEvent('beforetoggle', this) !== false){
3662                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3663             }
3664             
3665         }, this);
3666         
3667         var mark = {
3668             tag: "div",
3669             cls:"x-dlg-mask"
3670         };
3671         
3672         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3673         
3674         var size = this.el.getSize();
3675         this.maskEl.setSize(size.width, size.height);
3676         this.maskEl.enableDisplayMode("block");
3677         this.maskEl.hide();
3678         
3679         if(this.loadMask){
3680             this.maskEl.show();
3681         }
3682     },
3683     
3684     
3685     getChildContainer : function()
3686     {
3687         if (this.el.select('.collapse').getCount()) {
3688             return this.el.select('.collapse',true).first();
3689         }
3690         
3691         return this.el;
3692     },
3693     
3694     mask : function()
3695     {
3696         this.maskEl.show();
3697     },
3698     
3699     unmask : function()
3700     {
3701         this.maskEl.hide();
3702     } 
3703     
3704     
3705     
3706     
3707 });
3708
3709
3710
3711  
3712
3713  /*
3714  * - LGPL
3715  *
3716  * navbar
3717  * 
3718  */
3719
3720 /**
3721  * @class Roo.bootstrap.NavSimplebar
3722  * @extends Roo.bootstrap.Navbar
3723  * Bootstrap Sidebar class
3724  *
3725  * @cfg {Boolean} inverse is inverted color
3726  * 
3727  * @cfg {String} type (nav | pills | tabs)
3728  * @cfg {Boolean} arrangement stacked | justified
3729  * @cfg {String} align (left | right) alignment
3730  * 
3731  * @cfg {Boolean} main (true|false) main nav bar? default false
3732  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3733  * 
3734  * @cfg {String} tag (header|footer|nav|div) default is nav 
3735
3736  * 
3737  * 
3738  * 
3739  * @constructor
3740  * Create a new Sidebar
3741  * @param {Object} config The config object
3742  */
3743
3744
3745 Roo.bootstrap.NavSimplebar = function(config){
3746     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3747 };
3748
3749 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3750     
3751     inverse: false,
3752     
3753     type: false,
3754     arrangement: '',
3755     align : false,
3756     
3757     
3758     
3759     main : false,
3760     
3761     
3762     tag : false,
3763     
3764     
3765     getAutoCreate : function(){
3766         
3767         
3768         var cfg = {
3769             tag : this.tag || 'div',
3770             cls : 'navbar'
3771         };
3772           
3773         
3774         cfg.cn = [
3775             {
3776                 cls: 'nav',
3777                 tag : 'ul'
3778             }
3779         ];
3780         
3781          
3782         this.type = this.type || 'nav';
3783         if (['tabs','pills'].indexOf(this.type)!==-1) {
3784             cfg.cn[0].cls += ' nav-' + this.type
3785         
3786         
3787         } else {
3788             if (this.type!=='nav') {
3789                 Roo.log('nav type must be nav/tabs/pills')
3790             }
3791             cfg.cn[0].cls += ' navbar-nav'
3792         }
3793         
3794         
3795         
3796         
3797         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3798             cfg.cn[0].cls += ' nav-' + this.arrangement;
3799         }
3800         
3801         
3802         if (this.align === 'right') {
3803             cfg.cn[0].cls += ' navbar-right';
3804         }
3805         
3806         if (this.inverse) {
3807             cfg.cls += ' navbar-inverse';
3808             
3809         }
3810         
3811         
3812         return cfg;
3813     
3814         
3815     }
3816     
3817     
3818     
3819 });
3820
3821
3822
3823  
3824
3825  
3826        /*
3827  * - LGPL
3828  *
3829  * navbar
3830  * 
3831  */
3832
3833 /**
3834  * @class Roo.bootstrap.NavHeaderbar
3835  * @extends Roo.bootstrap.NavSimplebar
3836  * Bootstrap Sidebar class
3837  *
3838  * @cfg {String} brand what is brand
3839  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3840  * @cfg {String} brand_href href of the brand
3841  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3842  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3843  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3844  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3845  * 
3846  * @constructor
3847  * Create a new Sidebar
3848  * @param {Object} config The config object
3849  */
3850
3851
3852 Roo.bootstrap.NavHeaderbar = function(config){
3853     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3854       
3855 };
3856
3857 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3858     
3859     position: '',
3860     brand: '',
3861     brand_href: false,
3862     srButton : true,
3863     autohide : false,
3864     desktopCenter : false,
3865    
3866     
3867     getAutoCreate : function(){
3868         
3869         var   cfg = {
3870             tag: this.nav || 'nav',
3871             cls: 'navbar',
3872             role: 'navigation',
3873             cn: []
3874         };
3875         
3876         var cn = cfg.cn;
3877         if (this.desktopCenter) {
3878             cn.push({cls : 'container', cn : []});
3879             cn = cn[0].cn;
3880         }
3881         
3882         if(this.srButton){
3883             cn.push({
3884                 tag: 'div',
3885                 cls: 'navbar-header',
3886                 cn: [
3887                     {
3888                         tag: 'button',
3889                         type: 'button',
3890                         cls: 'navbar-toggle',
3891                         'data-toggle': 'collapse',
3892                         cn: [
3893                             {
3894                                 tag: 'span',
3895                                 cls: 'sr-only',
3896                                 html: 'Toggle navigation'
3897                             },
3898                             {
3899                                 tag: 'span',
3900                                 cls: 'icon-bar'
3901                             },
3902                             {
3903                                 tag: 'span',
3904                                 cls: 'icon-bar'
3905                             },
3906                             {
3907                                 tag: 'span',
3908                                 cls: 'icon-bar'
3909                             }
3910                         ]
3911                     }
3912                 ]
3913             });
3914         }
3915         
3916         cn.push({
3917             tag: 'div',
3918             cls: 'collapse navbar-collapse',
3919             cn : []
3920         });
3921         
3922         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3923         
3924         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3925             cfg.cls += ' navbar-' + this.position;
3926             
3927             // tag can override this..
3928             
3929             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3930         }
3931         
3932         if (this.brand !== '') {
3933             cn[0].cn.push({
3934                 tag: 'a',
3935                 href: this.brand_href ? this.brand_href : '#',
3936                 cls: 'navbar-brand',
3937                 cn: [
3938                 this.brand
3939                 ]
3940             });
3941         }
3942         
3943         if(this.main){
3944             cfg.cls += ' main-nav';
3945         }
3946         
3947         
3948         return cfg;
3949
3950         
3951     },
3952     getHeaderChildContainer : function()
3953     {
3954         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3955             return this.el.select('.navbar-header',true).first();
3956         }
3957         
3958         return this.getChildContainer();
3959     },
3960     
3961     
3962     initEvents : function()
3963     {
3964         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3965         
3966         if (this.autohide) {
3967             
3968             var prevScroll = 0;
3969             var ft = this.el;
3970             
3971             Roo.get(document).on('scroll',function(e) {
3972                 var ns = Roo.get(document).getScroll().top;
3973                 var os = prevScroll;
3974                 prevScroll = ns;
3975                 
3976                 if(ns > os){
3977                     ft.removeClass('slideDown');
3978                     ft.addClass('slideUp');
3979                     return;
3980                 }
3981                 ft.removeClass('slideUp');
3982                 ft.addClass('slideDown');
3983                  
3984               
3985           },this);
3986         }
3987     }    
3988     
3989 });
3990
3991
3992
3993  
3994
3995  /*
3996  * - LGPL
3997  *
3998  * navbar
3999  * 
4000  */
4001
4002 /**
4003  * @class Roo.bootstrap.NavSidebar
4004  * @extends Roo.bootstrap.Navbar
4005  * Bootstrap Sidebar class
4006  * 
4007  * @constructor
4008  * Create a new Sidebar
4009  * @param {Object} config The config object
4010  */
4011
4012
4013 Roo.bootstrap.NavSidebar = function(config){
4014     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4015 };
4016
4017 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4018     
4019     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4020     
4021     getAutoCreate : function(){
4022         
4023         
4024         return  {
4025             tag: 'div',
4026             cls: 'sidebar sidebar-nav'
4027         };
4028     
4029         
4030     }
4031     
4032     
4033     
4034 });
4035
4036
4037
4038  
4039
4040  /*
4041  * - LGPL
4042  *
4043  * nav group
4044  * 
4045  */
4046
4047 /**
4048  * @class Roo.bootstrap.NavGroup
4049  * @extends Roo.bootstrap.Component
4050  * Bootstrap NavGroup class
4051  * @cfg {String} align (left|right)
4052  * @cfg {Boolean} inverse
4053  * @cfg {String} type (nav|pills|tab) default nav
4054  * @cfg {String} navId - reference Id for navbar.
4055
4056  * 
4057  * @constructor
4058  * Create a new nav group
4059  * @param {Object} config The config object
4060  */
4061
4062 Roo.bootstrap.NavGroup = function(config){
4063     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4064     this.navItems = [];
4065    
4066     Roo.bootstrap.NavGroup.register(this);
4067      this.addEvents({
4068         /**
4069              * @event changed
4070              * Fires when the active item changes
4071              * @param {Roo.bootstrap.NavGroup} this
4072              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4073              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4074          */
4075         'changed': true
4076      });
4077     
4078 };
4079
4080 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4081     
4082     align: '',
4083     inverse: false,
4084     form: false,
4085     type: 'nav',
4086     navId : '',
4087     // private
4088     
4089     navItems : false, 
4090     
4091     getAutoCreate : function()
4092     {
4093         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4094         
4095         cfg = {
4096             tag : 'ul',
4097             cls: 'nav' 
4098         };
4099         
4100         if (['tabs','pills'].indexOf(this.type)!==-1) {
4101             cfg.cls += ' nav-' + this.type
4102         } else {
4103             if (this.type!=='nav') {
4104                 Roo.log('nav type must be nav/tabs/pills')
4105             }
4106             cfg.cls += ' navbar-nav'
4107         }
4108         
4109         if (this.parent() && this.parent().sidebar) {
4110             cfg = {
4111                 tag: 'ul',
4112                 cls: 'dashboard-menu sidebar-menu'
4113             };
4114             
4115             return cfg;
4116         }
4117         
4118         if (this.form === true) {
4119             cfg = {
4120                 tag: 'form',
4121                 cls: 'navbar-form'
4122             };
4123             
4124             if (this.align === 'right') {
4125                 cfg.cls += ' navbar-right';
4126             } else {
4127                 cfg.cls += ' navbar-left';
4128             }
4129         }
4130         
4131         if (this.align === 'right') {
4132             cfg.cls += ' navbar-right';
4133         }
4134         
4135         if (this.inverse) {
4136             cfg.cls += ' navbar-inverse';
4137             
4138         }
4139         
4140         
4141         return cfg;
4142     },
4143     /**
4144     * sets the active Navigation item
4145     * @param {Roo.bootstrap.NavItem} the new current navitem
4146     */
4147     setActiveItem : function(item)
4148     {
4149         var prev = false;
4150         Roo.each(this.navItems, function(v){
4151             if (v == item) {
4152                 return ;
4153             }
4154             if (v.isActive()) {
4155                 v.setActive(false, true);
4156                 prev = v;
4157                 
4158             }
4159             
4160         });
4161
4162         item.setActive(true, true);
4163         this.fireEvent('changed', this, item, prev);
4164         
4165         
4166     },
4167     /**
4168     * gets the active Navigation item
4169     * @return {Roo.bootstrap.NavItem} the current navitem
4170     */
4171     getActive : function()
4172     {
4173         
4174         var prev = false;
4175         Roo.each(this.navItems, function(v){
4176             
4177             if (v.isActive()) {
4178                 prev = v;
4179                 
4180             }
4181             
4182         });
4183         return prev;
4184     },
4185     
4186     indexOfNav : function()
4187     {
4188         
4189         var prev = false;
4190         Roo.each(this.navItems, function(v,i){
4191             
4192             if (v.isActive()) {
4193                 prev = i;
4194                 
4195             }
4196             
4197         });
4198         return prev;
4199     },
4200     /**
4201     * adds a Navigation item
4202     * @param {Roo.bootstrap.NavItem} the navitem to add
4203     */
4204     addItem : function(cfg)
4205     {
4206         var cn = new Roo.bootstrap.NavItem(cfg);
4207         this.register(cn);
4208         cn.parentId = this.id;
4209         cn.onRender(this.el, null);
4210         return cn;
4211     },
4212     /**
4213     * register a Navigation item
4214     * @param {Roo.bootstrap.NavItem} the navitem to add
4215     */
4216     register : function(item)
4217     {
4218         this.navItems.push( item);
4219         item.navId = this.navId;
4220     
4221     },
4222     
4223     /**
4224     * clear all the Navigation item
4225     */
4226    
4227     clearAll : function()
4228     {
4229         this.navItems = [];
4230         this.el.dom.innerHTML = '';
4231     },
4232     
4233     getNavItem: function(tabId)
4234     {
4235         var ret = false;
4236         Roo.each(this.navItems, function(e) {
4237             if (e.tabId == tabId) {
4238                ret =  e;
4239                return false;
4240             }
4241             return true;
4242             
4243         });
4244         return ret;
4245     },
4246     
4247     setActiveNext : function()
4248     {
4249         var i = this.indexOfNav(this.getActive());
4250         if (i > this.navItems.length) {
4251             return;
4252         }
4253         this.setActiveItem(this.navItems[i+1]);
4254     },
4255     setActivePrev : function()
4256     {
4257         var i = this.indexOfNav(this.getActive());
4258         if (i  < 1) {
4259             return;
4260         }
4261         this.setActiveItem(this.navItems[i-1]);
4262     },
4263     clearWasActive : function(except) {
4264         Roo.each(this.navItems, function(e) {
4265             if (e.tabId != except.tabId && e.was_active) {
4266                e.was_active = false;
4267                return false;
4268             }
4269             return true;
4270             
4271         });
4272     },
4273     getWasActive : function ()
4274     {
4275         var r = false;
4276         Roo.each(this.navItems, function(e) {
4277             if (e.was_active) {
4278                r = e;
4279                return false;
4280             }
4281             return true;
4282             
4283         });
4284         return r;
4285     }
4286     
4287     
4288 });
4289
4290  
4291 Roo.apply(Roo.bootstrap.NavGroup, {
4292     
4293     groups: {},
4294      /**
4295     * register a Navigation Group
4296     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4297     */
4298     register : function(navgrp)
4299     {
4300         this.groups[navgrp.navId] = navgrp;
4301         
4302     },
4303     /**
4304     * fetch a Navigation Group based on the navigation ID
4305     * @param {string} the navgroup to add
4306     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4307     */
4308     get: function(navId) {
4309         if (typeof(this.groups[navId]) == 'undefined') {
4310             return false;
4311             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4312         }
4313         return this.groups[navId] ;
4314     }
4315     
4316     
4317     
4318 });
4319
4320  /*
4321  * - LGPL
4322  *
4323  * row
4324  * 
4325  */
4326
4327 /**
4328  * @class Roo.bootstrap.NavItem
4329  * @extends Roo.bootstrap.Component
4330  * Bootstrap Navbar.NavItem class
4331  * @cfg {String} href  link to
4332  * @cfg {String} html content of button
4333  * @cfg {String} badge text inside badge
4334  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4335  * @cfg {String} glyphicon name of glyphicon
4336  * @cfg {String} icon name of font awesome icon
4337  * @cfg {Boolean} active Is item active
4338  * @cfg {Boolean} disabled Is item disabled
4339  
4340  * @cfg {Boolean} preventDefault (true | false) default false
4341  * @cfg {String} tabId the tab that this item activates.
4342  * @cfg {String} tagtype (a|span) render as a href or span?
4343  * @cfg {Boolean} animateRef (true|false) link to element default false  
4344   
4345  * @constructor
4346  * Create a new Navbar Item
4347  * @param {Object} config The config object
4348  */
4349 Roo.bootstrap.NavItem = function(config){
4350     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4351     this.addEvents({
4352         // raw events
4353         /**
4354          * @event click
4355          * The raw click event for the entire grid.
4356          * @param {Roo.EventObject} e
4357          */
4358         "click" : true,
4359          /**
4360             * @event changed
4361             * Fires when the active item active state changes
4362             * @param {Roo.bootstrap.NavItem} this
4363             * @param {boolean} state the new state
4364              
4365          */
4366         'changed': true,
4367         /**
4368             * @event scrollto
4369             * Fires when scroll to element
4370             * @param {Roo.bootstrap.NavItem} this
4371             * @param {Object} options
4372             * @param {Roo.EventObject} e
4373              
4374          */
4375         'scrollto': true
4376     });
4377    
4378 };
4379
4380 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4381     
4382     href: false,
4383     html: '',
4384     badge: '',
4385     icon: false,
4386     glyphicon: false,
4387     active: false,
4388     preventDefault : false,
4389     tabId : false,
4390     tagtype : 'a',
4391     disabled : false,
4392     animateRef : false,
4393     was_active : false,
4394     
4395     getAutoCreate : function(){
4396          
4397         var cfg = {
4398             tag: 'li',
4399             cls: 'nav-item'
4400             
4401         };
4402         
4403         if (this.active) {
4404             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4405         }
4406         if (this.disabled) {
4407             cfg.cls += ' disabled';
4408         }
4409         
4410         if (this.href || this.html || this.glyphicon || this.icon) {
4411             cfg.cn = [
4412                 {
4413                     tag: this.tagtype,
4414                     href : this.href || "#",
4415                     html: this.html || ''
4416                 }
4417             ];
4418             
4419             if (this.icon) {
4420                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4421             }
4422
4423             if(this.glyphicon) {
4424                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4425             }
4426             
4427             if (this.menu) {
4428                 
4429                 cfg.cn[0].html += " <span class='caret'></span>";
4430              
4431             }
4432             
4433             if (this.badge !== '') {
4434                  
4435                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4436             }
4437         }
4438         
4439         
4440         
4441         return cfg;
4442     },
4443     initEvents: function() 
4444     {
4445         if (typeof (this.menu) != 'undefined') {
4446             this.menu.parentType = this.xtype;
4447             this.menu.triggerEl = this.el;
4448             this.menu = this.addxtype(Roo.apply({}, this.menu));
4449         }
4450         
4451         this.el.select('a',true).on('click', this.onClick, this);
4452         
4453         if(this.tagtype == 'span'){
4454             this.el.select('span',true).on('click', this.onClick, this);
4455         }
4456        
4457         // at this point parent should be available..
4458         this.parent().register(this);
4459     },
4460     
4461     onClick : function(e)
4462     {
4463         if (e.getTarget('.dropdown-menu-item')) {
4464             // did you click on a menu itemm.... - then don't trigger onclick..
4465             return;
4466         }
4467         
4468         if(
4469                 this.preventDefault || 
4470                 this.href == '#' 
4471         ){
4472             Roo.log("NavItem - prevent Default?");
4473             e.preventDefault();
4474         }
4475         
4476         if (this.disabled) {
4477             return;
4478         }
4479         
4480         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4481         if (tg && tg.transition) {
4482             Roo.log("waiting for the transitionend");
4483             return;
4484         }
4485         
4486         
4487         
4488         //Roo.log("fire event clicked");
4489         if(this.fireEvent('click', this, e) === false){
4490             return;
4491         };
4492         
4493         if(this.tagtype == 'span'){
4494             return;
4495         }
4496         
4497         //Roo.log(this.href);
4498         var ael = this.el.select('a',true).first();
4499         //Roo.log(ael);
4500         
4501         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4502             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4503             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4504                 return; // ignore... - it's a 'hash' to another page.
4505             }
4506             Roo.log("NavItem - prevent Default?");
4507             e.preventDefault();
4508             this.scrollToElement(e);
4509         }
4510         
4511         
4512         var p =  this.parent();
4513    
4514         if (['tabs','pills'].indexOf(p.type)!==-1) {
4515             if (typeof(p.setActiveItem) !== 'undefined') {
4516                 p.setActiveItem(this);
4517             }
4518         }
4519         
4520         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4521         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4522             // remove the collapsed menu expand...
4523             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4524         }
4525     },
4526     
4527     isActive: function () {
4528         return this.active
4529     },
4530     setActive : function(state, fire, is_was_active)
4531     {
4532         if (this.active && !state && this.navId) {
4533             this.was_active = true;
4534             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4535             if (nv) {
4536                 nv.clearWasActive(this);
4537             }
4538             
4539         }
4540         this.active = state;
4541         
4542         if (!state ) {
4543             this.el.removeClass('active');
4544         } else if (!this.el.hasClass('active')) {
4545             this.el.addClass('active');
4546         }
4547         if (fire) {
4548             this.fireEvent('changed', this, state);
4549         }
4550         
4551         // show a panel if it's registered and related..
4552         
4553         if (!this.navId || !this.tabId || !state || is_was_active) {
4554             return;
4555         }
4556         
4557         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4558         if (!tg) {
4559             return;
4560         }
4561         var pan = tg.getPanelByName(this.tabId);
4562         if (!pan) {
4563             return;
4564         }
4565         // if we can not flip to new panel - go back to old nav highlight..
4566         if (false == tg.showPanel(pan)) {
4567             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4568             if (nv) {
4569                 var onav = nv.getWasActive();
4570                 if (onav) {
4571                     onav.setActive(true, false, true);
4572                 }
4573             }
4574             
4575         }
4576         
4577         
4578         
4579     },
4580      // this should not be here...
4581     setDisabled : function(state)
4582     {
4583         this.disabled = state;
4584         if (!state ) {
4585             this.el.removeClass('disabled');
4586         } else if (!this.el.hasClass('disabled')) {
4587             this.el.addClass('disabled');
4588         }
4589         
4590     },
4591     
4592     /**
4593      * Fetch the element to display the tooltip on.
4594      * @return {Roo.Element} defaults to this.el
4595      */
4596     tooltipEl : function()
4597     {
4598         return this.el.select('' + this.tagtype + '', true).first();
4599     },
4600     
4601     scrollToElement : function(e)
4602     {
4603         var c = document.body;
4604         
4605         /*
4606          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4607          */
4608         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4609             c = document.documentElement;
4610         }
4611         
4612         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4613         
4614         if(!target){
4615             return;
4616         }
4617
4618         var o = target.calcOffsetsTo(c);
4619         
4620         var options = {
4621             target : target,
4622             value : o[1]
4623         };
4624         
4625         this.fireEvent('scrollto', this, options, e);
4626         
4627         Roo.get(c).scrollTo('top', options.value, true);
4628         
4629         return;
4630     }
4631 });
4632  
4633
4634  /*
4635  * - LGPL
4636  *
4637  * sidebar item
4638  *
4639  *  li
4640  *    <span> icon </span>
4641  *    <span> text </span>
4642  *    <span>badge </span>
4643  */
4644
4645 /**
4646  * @class Roo.bootstrap.NavSidebarItem
4647  * @extends Roo.bootstrap.NavItem
4648  * Bootstrap Navbar.NavSidebarItem class
4649  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4650  * {Boolean} open is the menu open
4651  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4652  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4653  * {String} buttonSize (sm|md|lg)the extra classes for the button
4654  * {Boolean} showArrow show arrow next to the text (default true)
4655  * @constructor
4656  * Create a new Navbar Button
4657  * @param {Object} config The config object
4658  */
4659 Roo.bootstrap.NavSidebarItem = function(config){
4660     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4661     this.addEvents({
4662         // raw events
4663         /**
4664          * @event click
4665          * The raw click event for the entire grid.
4666          * @param {Roo.EventObject} e
4667          */
4668         "click" : true,
4669          /**
4670             * @event changed
4671             * Fires when the active item active state changes
4672             * @param {Roo.bootstrap.NavSidebarItem} this
4673             * @param {boolean} state the new state
4674              
4675          */
4676         'changed': true
4677     });
4678    
4679 };
4680
4681 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4682     
4683     badgeWeight : 'default',
4684     
4685     open: false,
4686     
4687     buttonView : false,
4688     
4689     buttonWeight : 'default',
4690     
4691     buttonSize : 'md',
4692     
4693     showArrow : true,
4694     
4695     getAutoCreate : function(){
4696         
4697         
4698         var a = {
4699                 tag: 'a',
4700                 href : this.href || '#',
4701                 cls: '',
4702                 html : '',
4703                 cn : []
4704         };
4705         
4706         if(this.buttonView){
4707             a = {
4708                 tag: 'button',
4709                 href : this.href || '#',
4710                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4711                 html : this.html,
4712                 cn : []
4713             };
4714         }
4715         
4716         var cfg = {
4717             tag: 'li',
4718             cls: '',
4719             cn: [ a ]
4720         };
4721         
4722         if (this.active) {
4723             cfg.cls += ' active';
4724         }
4725         
4726         if (this.disabled) {
4727             cfg.cls += ' disabled';
4728         }
4729         if (this.open) {
4730             cfg.cls += ' open x-open';
4731         }
4732         // left icon..
4733         if (this.glyphicon || this.icon) {
4734             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4735             a.cn.push({ tag : 'i', cls : c }) ;
4736         }
4737         
4738         if(!this.buttonView){
4739             var span = {
4740                 tag: 'span',
4741                 html : this.html || ''
4742             };
4743
4744             a.cn.push(span);
4745             
4746         }
4747         
4748         if (this.badge !== '') {
4749             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4750         }
4751         
4752         if (this.menu) {
4753             
4754             if(this.showArrow){
4755                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4756             }
4757             
4758             a.cls += ' dropdown-toggle treeview' ;
4759         }
4760         
4761         return cfg;
4762     },
4763     
4764     initEvents : function()
4765     { 
4766         if (typeof (this.menu) != 'undefined') {
4767             this.menu.parentType = this.xtype;
4768             this.menu.triggerEl = this.el;
4769             this.menu = this.addxtype(Roo.apply({}, this.menu));
4770         }
4771         
4772         this.el.on('click', this.onClick, this);
4773         
4774         if(this.badge !== ''){
4775             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4776         }
4777         
4778     },
4779     
4780     onClick : function(e)
4781     {
4782         if(this.disabled){
4783             e.preventDefault();
4784             return;
4785         }
4786         
4787         if(this.preventDefault){
4788             e.preventDefault();
4789         }
4790         
4791         this.fireEvent('click', this);
4792     },
4793     
4794     disable : function()
4795     {
4796         this.setDisabled(true);
4797     },
4798     
4799     enable : function()
4800     {
4801         this.setDisabled(false);
4802     },
4803     
4804     setDisabled : function(state)
4805     {
4806         if(this.disabled == state){
4807             return;
4808         }
4809         
4810         this.disabled = state;
4811         
4812         if (state) {
4813             this.el.addClass('disabled');
4814             return;
4815         }
4816         
4817         this.el.removeClass('disabled');
4818         
4819         return;
4820     },
4821     
4822     setActive : function(state)
4823     {
4824         if(this.active == state){
4825             return;
4826         }
4827         
4828         this.active = state;
4829         
4830         if (state) {
4831             this.el.addClass('active');
4832             return;
4833         }
4834         
4835         this.el.removeClass('active');
4836         
4837         return;
4838     },
4839     
4840     isActive: function () 
4841     {
4842         return this.active;
4843     },
4844     
4845     setBadge : function(str)
4846     {
4847         if(!this.badgeEl){
4848             return;
4849         }
4850         
4851         this.badgeEl.dom.innerHTML = str;
4852     }
4853     
4854    
4855      
4856  
4857 });
4858  
4859
4860  /*
4861  * - LGPL
4862  *
4863  * row
4864  * 
4865  */
4866
4867 /**
4868  * @class Roo.bootstrap.Row
4869  * @extends Roo.bootstrap.Component
4870  * Bootstrap Row class (contains columns...)
4871  * 
4872  * @constructor
4873  * Create a new Row
4874  * @param {Object} config The config object
4875  */
4876
4877 Roo.bootstrap.Row = function(config){
4878     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4879 };
4880
4881 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4882     
4883     getAutoCreate : function(){
4884        return {
4885             cls: 'row clearfix'
4886        };
4887     }
4888     
4889     
4890 });
4891
4892  
4893
4894  /*
4895  * - LGPL
4896  *
4897  * element
4898  * 
4899  */
4900
4901 /**
4902  * @class Roo.bootstrap.Element
4903  * @extends Roo.bootstrap.Component
4904  * Bootstrap Element class
4905  * @cfg {String} html contents of the element
4906  * @cfg {String} tag tag of the element
4907  * @cfg {String} cls class of the element
4908  * @cfg {Boolean} preventDefault (true|false) default false
4909  * @cfg {Boolean} clickable (true|false) default false
4910  * 
4911  * @constructor
4912  * Create a new Element
4913  * @param {Object} config The config object
4914  */
4915
4916 Roo.bootstrap.Element = function(config){
4917     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4918     
4919     this.addEvents({
4920         // raw events
4921         /**
4922          * @event click
4923          * When a element is chick
4924          * @param {Roo.bootstrap.Element} this
4925          * @param {Roo.EventObject} e
4926          */
4927         "click" : true
4928     });
4929 };
4930
4931 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4932     
4933     tag: 'div',
4934     cls: '',
4935     html: '',
4936     preventDefault: false, 
4937     clickable: false,
4938     
4939     getAutoCreate : function(){
4940         
4941         var cfg = {
4942             tag: this.tag,
4943             // cls: this.cls, double assign in parent class Component.js :: onRender
4944             html: this.html
4945         };
4946         
4947         return cfg;
4948     },
4949     
4950     initEvents: function() 
4951     {
4952         Roo.bootstrap.Element.superclass.initEvents.call(this);
4953         
4954         if(this.clickable){
4955             this.el.on('click', this.onClick, this);
4956         }
4957         
4958     },
4959     
4960     onClick : function(e)
4961     {
4962         if(this.preventDefault){
4963             e.preventDefault();
4964         }
4965         
4966         this.fireEvent('click', this, e);
4967     },
4968     
4969     getValue : function()
4970     {
4971         return this.el.dom.innerHTML;
4972     },
4973     
4974     setValue : function(value)
4975     {
4976         this.el.dom.innerHTML = value;
4977     }
4978    
4979 });
4980
4981  
4982
4983  /*
4984  * - LGPL
4985  *
4986  * pagination
4987  * 
4988  */
4989
4990 /**
4991  * @class Roo.bootstrap.Pagination
4992  * @extends Roo.bootstrap.Component
4993  * Bootstrap Pagination class
4994  * @cfg {String} size xs | sm | md | lg
4995  * @cfg {Boolean} inverse false | true
4996  * 
4997  * @constructor
4998  * Create a new Pagination
4999  * @param {Object} config The config object
5000  */
5001
5002 Roo.bootstrap.Pagination = function(config){
5003     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5004 };
5005
5006 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5007     
5008     cls: false,
5009     size: false,
5010     inverse: false,
5011     
5012     getAutoCreate : function(){
5013         var cfg = {
5014             tag: 'ul',
5015                 cls: 'pagination'
5016         };
5017         if (this.inverse) {
5018             cfg.cls += ' inverse';
5019         }
5020         if (this.html) {
5021             cfg.html=this.html;
5022         }
5023         if (this.cls) {
5024             cfg.cls += " " + this.cls;
5025         }
5026         return cfg;
5027     }
5028    
5029 });
5030
5031  
5032
5033  /*
5034  * - LGPL
5035  *
5036  * Pagination item
5037  * 
5038  */
5039
5040
5041 /**
5042  * @class Roo.bootstrap.PaginationItem
5043  * @extends Roo.bootstrap.Component
5044  * Bootstrap PaginationItem class
5045  * @cfg {String} html text
5046  * @cfg {String} href the link
5047  * @cfg {Boolean} preventDefault (true | false) default true
5048  * @cfg {Boolean} active (true | false) default false
5049  * @cfg {Boolean} disabled default false
5050  * 
5051  * 
5052  * @constructor
5053  * Create a new PaginationItem
5054  * @param {Object} config The config object
5055  */
5056
5057
5058 Roo.bootstrap.PaginationItem = function(config){
5059     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5060     this.addEvents({
5061         // raw events
5062         /**
5063          * @event click
5064          * The raw click event for the entire grid.
5065          * @param {Roo.EventObject} e
5066          */
5067         "click" : true
5068     });
5069 };
5070
5071 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5072     
5073     href : false,
5074     html : false,
5075     preventDefault: true,
5076     active : false,
5077     cls : false,
5078     disabled: false,
5079     
5080     getAutoCreate : function(){
5081         var cfg= {
5082             tag: 'li',
5083             cn: [
5084                 {
5085                     tag : 'a',
5086                     href : this.href ? this.href : '#',
5087                     html : this.html ? this.html : ''
5088                 }
5089             ]
5090         };
5091         
5092         if(this.cls){
5093             cfg.cls = this.cls;
5094         }
5095         
5096         if(this.disabled){
5097             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5098         }
5099         
5100         if(this.active){
5101             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5102         }
5103         
5104         return cfg;
5105     },
5106     
5107     initEvents: function() {
5108         
5109         this.el.on('click', this.onClick, this);
5110         
5111     },
5112     onClick : function(e)
5113     {
5114         Roo.log('PaginationItem on click ');
5115         if(this.preventDefault){
5116             e.preventDefault();
5117         }
5118         
5119         if(this.disabled){
5120             return;
5121         }
5122         
5123         this.fireEvent('click', this, e);
5124     }
5125    
5126 });
5127
5128  
5129
5130  /*
5131  * - LGPL
5132  *
5133  * slider
5134  * 
5135  */
5136
5137
5138 /**
5139  * @class Roo.bootstrap.Slider
5140  * @extends Roo.bootstrap.Component
5141  * Bootstrap Slider class
5142  *    
5143  * @constructor
5144  * Create a new Slider
5145  * @param {Object} config The config object
5146  */
5147
5148 Roo.bootstrap.Slider = function(config){
5149     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5150 };
5151
5152 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5153     
5154     getAutoCreate : function(){
5155         
5156         var cfg = {
5157             tag: 'div',
5158             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5159             cn: [
5160                 {
5161                     tag: 'a',
5162                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5163                 }
5164             ]
5165         };
5166         
5167         return cfg;
5168     }
5169    
5170 });
5171
5172  /*
5173  * Based on:
5174  * Ext JS Library 1.1.1
5175  * Copyright(c) 2006-2007, Ext JS, LLC.
5176  *
5177  * Originally Released Under LGPL - original licence link has changed is not relivant.
5178  *
5179  * Fork - LGPL
5180  * <script type="text/javascript">
5181  */
5182  
5183
5184 /**
5185  * @class Roo.grid.ColumnModel
5186  * @extends Roo.util.Observable
5187  * This is the default implementation of a ColumnModel used by the Grid. It defines
5188  * the columns in the grid.
5189  * <br>Usage:<br>
5190  <pre><code>
5191  var colModel = new Roo.grid.ColumnModel([
5192         {header: "Ticker", width: 60, sortable: true, locked: true},
5193         {header: "Company Name", width: 150, sortable: true},
5194         {header: "Market Cap.", width: 100, sortable: true},
5195         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5196         {header: "Employees", width: 100, sortable: true, resizable: false}
5197  ]);
5198  </code></pre>
5199  * <p>
5200  
5201  * The config options listed for this class are options which may appear in each
5202  * individual column definition.
5203  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5204  * @constructor
5205  * @param {Object} config An Array of column config objects. See this class's
5206  * config objects for details.
5207 */
5208 Roo.grid.ColumnModel = function(config){
5209         /**
5210      * The config passed into the constructor
5211      */
5212     this.config = config;
5213     this.lookup = {};
5214
5215     // if no id, create one
5216     // if the column does not have a dataIndex mapping,
5217     // map it to the order it is in the config
5218     for(var i = 0, len = config.length; i < len; i++){
5219         var c = config[i];
5220         if(typeof c.dataIndex == "undefined"){
5221             c.dataIndex = i;
5222         }
5223         if(typeof c.renderer == "string"){
5224             c.renderer = Roo.util.Format[c.renderer];
5225         }
5226         if(typeof c.id == "undefined"){
5227             c.id = Roo.id();
5228         }
5229         if(c.editor && c.editor.xtype){
5230             c.editor  = Roo.factory(c.editor, Roo.grid);
5231         }
5232         if(c.editor && c.editor.isFormField){
5233             c.editor = new Roo.grid.GridEditor(c.editor);
5234         }
5235         this.lookup[c.id] = c;
5236     }
5237
5238     /**
5239      * The width of columns which have no width specified (defaults to 100)
5240      * @type Number
5241      */
5242     this.defaultWidth = 100;
5243
5244     /**
5245      * Default sortable of columns which have no sortable specified (defaults to false)
5246      * @type Boolean
5247      */
5248     this.defaultSortable = false;
5249
5250     this.addEvents({
5251         /**
5252              * @event widthchange
5253              * Fires when the width of a column changes.
5254              * @param {ColumnModel} this
5255              * @param {Number} columnIndex The column index
5256              * @param {Number} newWidth The new width
5257              */
5258             "widthchange": true,
5259         /**
5260              * @event headerchange
5261              * Fires when the text of a header changes.
5262              * @param {ColumnModel} this
5263              * @param {Number} columnIndex The column index
5264              * @param {Number} newText The new header text
5265              */
5266             "headerchange": true,
5267         /**
5268              * @event hiddenchange
5269              * Fires when a column is hidden or "unhidden".
5270              * @param {ColumnModel} this
5271              * @param {Number} columnIndex The column index
5272              * @param {Boolean} hidden true if hidden, false otherwise
5273              */
5274             "hiddenchange": true,
5275             /**
5276          * @event columnmoved
5277          * Fires when a column is moved.
5278          * @param {ColumnModel} this
5279          * @param {Number} oldIndex
5280          * @param {Number} newIndex
5281          */
5282         "columnmoved" : true,
5283         /**
5284          * @event columlockchange
5285          * Fires when a column's locked state is changed
5286          * @param {ColumnModel} this
5287          * @param {Number} colIndex
5288          * @param {Boolean} locked true if locked
5289          */
5290         "columnlockchange" : true
5291     });
5292     Roo.grid.ColumnModel.superclass.constructor.call(this);
5293 };
5294 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5295     /**
5296      * @cfg {String} header The header text to display in the Grid view.
5297      */
5298     /**
5299      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5300      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5301      * specified, the column's index is used as an index into the Record's data Array.
5302      */
5303     /**
5304      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5305      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5306      */
5307     /**
5308      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5309      * Defaults to the value of the {@link #defaultSortable} property.
5310      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5311      */
5312     /**
5313      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5314      */
5315     /**
5316      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5317      */
5318     /**
5319      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5320      */
5321     /**
5322      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5323      */
5324     /**
5325      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5326      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5327      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5328      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5329      */
5330        /**
5331      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5332      */
5333     /**
5334      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5335      */
5336     /**
5337      * @cfg {String} cursor (Optional)
5338      */
5339     /**
5340      * @cfg {String} tooltip (Optional)
5341      */
5342     /**
5343      * @cfg {Number} xs (Optional)
5344      */
5345     /**
5346      * @cfg {Number} sm (Optional)
5347      */
5348     /**
5349      * @cfg {Number} md (Optional)
5350      */
5351     /**
5352      * @cfg {Number} lg (Optional)
5353      */
5354     /**
5355      * Returns the id of the column at the specified index.
5356      * @param {Number} index The column index
5357      * @return {String} the id
5358      */
5359     getColumnId : function(index){
5360         return this.config[index].id;
5361     },
5362
5363     /**
5364      * Returns the column for a specified id.
5365      * @param {String} id The column id
5366      * @return {Object} the column
5367      */
5368     getColumnById : function(id){
5369         return this.lookup[id];
5370     },
5371
5372     
5373     /**
5374      * Returns the column for a specified dataIndex.
5375      * @param {String} dataIndex The column dataIndex
5376      * @return {Object|Boolean} the column or false if not found
5377      */
5378     getColumnByDataIndex: function(dataIndex){
5379         var index = this.findColumnIndex(dataIndex);
5380         return index > -1 ? this.config[index] : false;
5381     },
5382     
5383     /**
5384      * Returns the index for a specified column id.
5385      * @param {String} id The column id
5386      * @return {Number} the index, or -1 if not found
5387      */
5388     getIndexById : function(id){
5389         for(var i = 0, len = this.config.length; i < len; i++){
5390             if(this.config[i].id == id){
5391                 return i;
5392             }
5393         }
5394         return -1;
5395     },
5396     
5397     /**
5398      * Returns the index for a specified column dataIndex.
5399      * @param {String} dataIndex The column dataIndex
5400      * @return {Number} the index, or -1 if not found
5401      */
5402     
5403     findColumnIndex : function(dataIndex){
5404         for(var i = 0, len = this.config.length; i < len; i++){
5405             if(this.config[i].dataIndex == dataIndex){
5406                 return i;
5407             }
5408         }
5409         return -1;
5410     },
5411     
5412     
5413     moveColumn : function(oldIndex, newIndex){
5414         var c = this.config[oldIndex];
5415         this.config.splice(oldIndex, 1);
5416         this.config.splice(newIndex, 0, c);
5417         this.dataMap = null;
5418         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5419     },
5420
5421     isLocked : function(colIndex){
5422         return this.config[colIndex].locked === true;
5423     },
5424
5425     setLocked : function(colIndex, value, suppressEvent){
5426         if(this.isLocked(colIndex) == value){
5427             return;
5428         }
5429         this.config[colIndex].locked = value;
5430         if(!suppressEvent){
5431             this.fireEvent("columnlockchange", this, colIndex, value);
5432         }
5433     },
5434
5435     getTotalLockedWidth : function(){
5436         var totalWidth = 0;
5437         for(var i = 0; i < this.config.length; i++){
5438             if(this.isLocked(i) && !this.isHidden(i)){
5439                 this.totalWidth += this.getColumnWidth(i);
5440             }
5441         }
5442         return totalWidth;
5443     },
5444
5445     getLockedCount : function(){
5446         for(var i = 0, len = this.config.length; i < len; i++){
5447             if(!this.isLocked(i)){
5448                 return i;
5449             }
5450         }
5451         
5452         return this.config.length;
5453     },
5454
5455     /**
5456      * Returns the number of columns.
5457      * @return {Number}
5458      */
5459     getColumnCount : function(visibleOnly){
5460         if(visibleOnly === true){
5461             var c = 0;
5462             for(var i = 0, len = this.config.length; i < len; i++){
5463                 if(!this.isHidden(i)){
5464                     c++;
5465                 }
5466             }
5467             return c;
5468         }
5469         return this.config.length;
5470     },
5471
5472     /**
5473      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5474      * @param {Function} fn
5475      * @param {Object} scope (optional)
5476      * @return {Array} result
5477      */
5478     getColumnsBy : function(fn, scope){
5479         var r = [];
5480         for(var i = 0, len = this.config.length; i < len; i++){
5481             var c = this.config[i];
5482             if(fn.call(scope||this, c, i) === true){
5483                 r[r.length] = c;
5484             }
5485         }
5486         return r;
5487     },
5488
5489     /**
5490      * Returns true if the specified column is sortable.
5491      * @param {Number} col The column index
5492      * @return {Boolean}
5493      */
5494     isSortable : function(col){
5495         if(typeof this.config[col].sortable == "undefined"){
5496             return this.defaultSortable;
5497         }
5498         return this.config[col].sortable;
5499     },
5500
5501     /**
5502      * Returns the rendering (formatting) function defined for the column.
5503      * @param {Number} col The column index.
5504      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5505      */
5506     getRenderer : function(col){
5507         if(!this.config[col].renderer){
5508             return Roo.grid.ColumnModel.defaultRenderer;
5509         }
5510         return this.config[col].renderer;
5511     },
5512
5513     /**
5514      * Sets the rendering (formatting) function for a column.
5515      * @param {Number} col The column index
5516      * @param {Function} fn The function to use to process the cell's raw data
5517      * to return HTML markup for the grid view. The render function is called with
5518      * the following parameters:<ul>
5519      * <li>Data value.</li>
5520      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5521      * <li>css A CSS style string to apply to the table cell.</li>
5522      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5523      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5524      * <li>Row index</li>
5525      * <li>Column index</li>
5526      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5527      */
5528     setRenderer : function(col, fn){
5529         this.config[col].renderer = fn;
5530     },
5531
5532     /**
5533      * Returns the width for the specified column.
5534      * @param {Number} col The column index
5535      * @return {Number}
5536      */
5537     getColumnWidth : function(col){
5538         return this.config[col].width * 1 || this.defaultWidth;
5539     },
5540
5541     /**
5542      * Sets the width for a column.
5543      * @param {Number} col The column index
5544      * @param {Number} width The new width
5545      */
5546     setColumnWidth : function(col, width, suppressEvent){
5547         this.config[col].width = width;
5548         this.totalWidth = null;
5549         if(!suppressEvent){
5550              this.fireEvent("widthchange", this, col, width);
5551         }
5552     },
5553
5554     /**
5555      * Returns the total width of all columns.
5556      * @param {Boolean} includeHidden True to include hidden column widths
5557      * @return {Number}
5558      */
5559     getTotalWidth : function(includeHidden){
5560         if(!this.totalWidth){
5561             this.totalWidth = 0;
5562             for(var i = 0, len = this.config.length; i < len; i++){
5563                 if(includeHidden || !this.isHidden(i)){
5564                     this.totalWidth += this.getColumnWidth(i);
5565                 }
5566             }
5567         }
5568         return this.totalWidth;
5569     },
5570
5571     /**
5572      * Returns the header for the specified column.
5573      * @param {Number} col The column index
5574      * @return {String}
5575      */
5576     getColumnHeader : function(col){
5577         return this.config[col].header;
5578     },
5579
5580     /**
5581      * Sets the header for a column.
5582      * @param {Number} col The column index
5583      * @param {String} header The new header
5584      */
5585     setColumnHeader : function(col, header){
5586         this.config[col].header = header;
5587         this.fireEvent("headerchange", this, col, header);
5588     },
5589
5590     /**
5591      * Returns the tooltip for the specified column.
5592      * @param {Number} col The column index
5593      * @return {String}
5594      */
5595     getColumnTooltip : function(col){
5596             return this.config[col].tooltip;
5597     },
5598     /**
5599      * Sets the tooltip for a column.
5600      * @param {Number} col The column index
5601      * @param {String} tooltip The new tooltip
5602      */
5603     setColumnTooltip : function(col, tooltip){
5604             this.config[col].tooltip = tooltip;
5605     },
5606
5607     /**
5608      * Returns the dataIndex for the specified column.
5609      * @param {Number} col The column index
5610      * @return {Number}
5611      */
5612     getDataIndex : function(col){
5613         return this.config[col].dataIndex;
5614     },
5615
5616     /**
5617      * Sets the dataIndex for a column.
5618      * @param {Number} col The column index
5619      * @param {Number} dataIndex The new dataIndex
5620      */
5621     setDataIndex : function(col, dataIndex){
5622         this.config[col].dataIndex = dataIndex;
5623     },
5624
5625     
5626     
5627     /**
5628      * Returns true if the cell is editable.
5629      * @param {Number} colIndex The column index
5630      * @param {Number} rowIndex The row index - this is nto actually used..?
5631      * @return {Boolean}
5632      */
5633     isCellEditable : function(colIndex, rowIndex){
5634         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5635     },
5636
5637     /**
5638      * Returns the editor defined for the cell/column.
5639      * return false or null to disable editing.
5640      * @param {Number} colIndex The column index
5641      * @param {Number} rowIndex The row index
5642      * @return {Object}
5643      */
5644     getCellEditor : function(colIndex, rowIndex){
5645         return this.config[colIndex].editor;
5646     },
5647
5648     /**
5649      * Sets if a column is editable.
5650      * @param {Number} col The column index
5651      * @param {Boolean} editable True if the column is editable
5652      */
5653     setEditable : function(col, editable){
5654         this.config[col].editable = editable;
5655     },
5656
5657
5658     /**
5659      * Returns true if the column is hidden.
5660      * @param {Number} colIndex The column index
5661      * @return {Boolean}
5662      */
5663     isHidden : function(colIndex){
5664         return this.config[colIndex].hidden;
5665     },
5666
5667
5668     /**
5669      * Returns true if the column width cannot be changed
5670      */
5671     isFixed : function(colIndex){
5672         return this.config[colIndex].fixed;
5673     },
5674
5675     /**
5676      * Returns true if the column can be resized
5677      * @return {Boolean}
5678      */
5679     isResizable : function(colIndex){
5680         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5681     },
5682     /**
5683      * Sets if a column is hidden.
5684      * @param {Number} colIndex The column index
5685      * @param {Boolean} hidden True if the column is hidden
5686      */
5687     setHidden : function(colIndex, hidden){
5688         this.config[colIndex].hidden = hidden;
5689         this.totalWidth = null;
5690         this.fireEvent("hiddenchange", this, colIndex, hidden);
5691     },
5692
5693     /**
5694      * Sets the editor for a column.
5695      * @param {Number} col The column index
5696      * @param {Object} editor The editor object
5697      */
5698     setEditor : function(col, editor){
5699         this.config[col].editor = editor;
5700     }
5701 });
5702
5703 Roo.grid.ColumnModel.defaultRenderer = function(value)
5704 {
5705     if(typeof value == "object") {
5706         return value;
5707     }
5708         if(typeof value == "string" && value.length < 1){
5709             return "&#160;";
5710         }
5711     
5712         return String.format("{0}", value);
5713 };
5714
5715 // Alias for backwards compatibility
5716 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5717 /*
5718  * Based on:
5719  * Ext JS Library 1.1.1
5720  * Copyright(c) 2006-2007, Ext JS, LLC.
5721  *
5722  * Originally Released Under LGPL - original licence link has changed is not relivant.
5723  *
5724  * Fork - LGPL
5725  * <script type="text/javascript">
5726  */
5727  
5728 /**
5729  * @class Roo.LoadMask
5730  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5731  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5732  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5733  * element's UpdateManager load indicator and will be destroyed after the initial load.
5734  * @constructor
5735  * Create a new LoadMask
5736  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5737  * @param {Object} config The config object
5738  */
5739 Roo.LoadMask = function(el, config){
5740     this.el = Roo.get(el);
5741     Roo.apply(this, config);
5742     if(this.store){
5743         this.store.on('beforeload', this.onBeforeLoad, this);
5744         this.store.on('load', this.onLoad, this);
5745         this.store.on('loadexception', this.onLoadException, this);
5746         this.removeMask = false;
5747     }else{
5748         var um = this.el.getUpdateManager();
5749         um.showLoadIndicator = false; // disable the default indicator
5750         um.on('beforeupdate', this.onBeforeLoad, this);
5751         um.on('update', this.onLoad, this);
5752         um.on('failure', this.onLoad, this);
5753         this.removeMask = true;
5754     }
5755 };
5756
5757 Roo.LoadMask.prototype = {
5758     /**
5759      * @cfg {Boolean} removeMask
5760      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5761      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5762      */
5763     /**
5764      * @cfg {String} msg
5765      * The text to display in a centered loading message box (defaults to 'Loading...')
5766      */
5767     msg : 'Loading...',
5768     /**
5769      * @cfg {String} msgCls
5770      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5771      */
5772     msgCls : 'x-mask-loading',
5773
5774     /**
5775      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5776      * @type Boolean
5777      */
5778     disabled: false,
5779
5780     /**
5781      * Disables the mask to prevent it from being displayed
5782      */
5783     disable : function(){
5784        this.disabled = true;
5785     },
5786
5787     /**
5788      * Enables the mask so that it can be displayed
5789      */
5790     enable : function(){
5791         this.disabled = false;
5792     },
5793     
5794     onLoadException : function()
5795     {
5796         Roo.log(arguments);
5797         
5798         if (typeof(arguments[3]) != 'undefined') {
5799             Roo.MessageBox.alert("Error loading",arguments[3]);
5800         } 
5801         /*
5802         try {
5803             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5804                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5805             }   
5806         } catch(e) {
5807             
5808         }
5809         */
5810     
5811         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5812     },
5813     // private
5814     onLoad : function()
5815     {
5816         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5817     },
5818
5819     // private
5820     onBeforeLoad : function(){
5821         if(!this.disabled){
5822             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5823         }
5824     },
5825
5826     // private
5827     destroy : function(){
5828         if(this.store){
5829             this.store.un('beforeload', this.onBeforeLoad, this);
5830             this.store.un('load', this.onLoad, this);
5831             this.store.un('loadexception', this.onLoadException, this);
5832         }else{
5833             var um = this.el.getUpdateManager();
5834             um.un('beforeupdate', this.onBeforeLoad, this);
5835             um.un('update', this.onLoad, this);
5836             um.un('failure', this.onLoad, this);
5837         }
5838     }
5839 };/*
5840  * - LGPL
5841  *
5842  * table
5843  * 
5844  */
5845
5846 /**
5847  * @class Roo.bootstrap.Table
5848  * @extends Roo.bootstrap.Component
5849  * Bootstrap Table class
5850  * @cfg {String} cls table class
5851  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5852  * @cfg {String} bgcolor Specifies the background color for a table
5853  * @cfg {Number} border Specifies whether the table cells should have borders or not
5854  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5855  * @cfg {Number} cellspacing Specifies the space between cells
5856  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5857  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5858  * @cfg {String} sortable Specifies that the table should be sortable
5859  * @cfg {String} summary Specifies a summary of the content of a table
5860  * @cfg {Number} width Specifies the width of a table
5861  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5862  * 
5863  * @cfg {boolean} striped Should the rows be alternative striped
5864  * @cfg {boolean} bordered Add borders to the table
5865  * @cfg {boolean} hover Add hover highlighting
5866  * @cfg {boolean} condensed Format condensed
5867  * @cfg {boolean} responsive Format condensed
5868  * @cfg {Boolean} loadMask (true|false) default false
5869  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5870  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5871  * @cfg {Boolean} rowSelection (true|false) default false
5872  * @cfg {Boolean} cellSelection (true|false) default false
5873  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5874  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5875  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5876  
5877  * 
5878  * @constructor
5879  * Create a new Table
5880  * @param {Object} config The config object
5881  */
5882
5883 Roo.bootstrap.Table = function(config){
5884     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5885     
5886   
5887     
5888     // BC...
5889     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5890     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5891     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5892     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5893     
5894     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5895     if (this.sm) {
5896         this.sm.grid = this;
5897         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5898         this.sm = this.selModel;
5899         this.sm.xmodule = this.xmodule || false;
5900     }
5901     
5902     if (this.cm && typeof(this.cm.config) == 'undefined') {
5903         this.colModel = new Roo.grid.ColumnModel(this.cm);
5904         this.cm = this.colModel;
5905         this.cm.xmodule = this.xmodule || false;
5906     }
5907     if (this.store) {
5908         this.store= Roo.factory(this.store, Roo.data);
5909         this.ds = this.store;
5910         this.ds.xmodule = this.xmodule || false;
5911          
5912     }
5913     if (this.footer && this.store) {
5914         this.footer.dataSource = this.ds;
5915         this.footer = Roo.factory(this.footer);
5916     }
5917     
5918     /** @private */
5919     this.addEvents({
5920         /**
5921          * @event cellclick
5922          * Fires when a cell is clicked
5923          * @param {Roo.bootstrap.Table} this
5924          * @param {Roo.Element} el
5925          * @param {Number} rowIndex
5926          * @param {Number} columnIndex
5927          * @param {Roo.EventObject} e
5928          */
5929         "cellclick" : true,
5930         /**
5931          * @event celldblclick
5932          * Fires when a cell is double clicked
5933          * @param {Roo.bootstrap.Table} this
5934          * @param {Roo.Element} el
5935          * @param {Number} rowIndex
5936          * @param {Number} columnIndex
5937          * @param {Roo.EventObject} e
5938          */
5939         "celldblclick" : true,
5940         /**
5941          * @event rowclick
5942          * Fires when a row is clicked
5943          * @param {Roo.bootstrap.Table} this
5944          * @param {Roo.Element} el
5945          * @param {Number} rowIndex
5946          * @param {Roo.EventObject} e
5947          */
5948         "rowclick" : true,
5949         /**
5950          * @event rowdblclick
5951          * Fires when a row is double clicked
5952          * @param {Roo.bootstrap.Table} this
5953          * @param {Roo.Element} el
5954          * @param {Number} rowIndex
5955          * @param {Roo.EventObject} e
5956          */
5957         "rowdblclick" : true,
5958         /**
5959          * @event mouseover
5960          * Fires when a mouseover occur
5961          * @param {Roo.bootstrap.Table} this
5962          * @param {Roo.Element} el
5963          * @param {Number} rowIndex
5964          * @param {Number} columnIndex
5965          * @param {Roo.EventObject} e
5966          */
5967         "mouseover" : true,
5968         /**
5969          * @event mouseout
5970          * Fires when a mouseout occur
5971          * @param {Roo.bootstrap.Table} this
5972          * @param {Roo.Element} el
5973          * @param {Number} rowIndex
5974          * @param {Number} columnIndex
5975          * @param {Roo.EventObject} e
5976          */
5977         "mouseout" : true,
5978         /**
5979          * @event rowclass
5980          * Fires when a row is rendered, so you can change add a style to it.
5981          * @param {Roo.bootstrap.Table} this
5982          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5983          */
5984         'rowclass' : true,
5985           /**
5986          * @event rowsrendered
5987          * Fires when all the  rows have been rendered
5988          * @param {Roo.bootstrap.Table} this
5989          */
5990         'rowsrendered' : true,
5991         /**
5992          * @event contextmenu
5993          * The raw contextmenu event for the entire grid.
5994          * @param {Roo.EventObject} e
5995          */
5996         "contextmenu" : true,
5997         /**
5998          * @event rowcontextmenu
5999          * Fires when a row is right clicked
6000          * @param {Roo.bootstrap.Table} this
6001          * @param {Number} rowIndex
6002          * @param {Roo.EventObject} e
6003          */
6004         "rowcontextmenu" : true,
6005         /**
6006          * @event cellcontextmenu
6007          * Fires when a cell is right clicked
6008          * @param {Roo.bootstrap.Table} this
6009          * @param {Number} rowIndex
6010          * @param {Number} cellIndex
6011          * @param {Roo.EventObject} e
6012          */
6013          "cellcontextmenu" : true,
6014          /**
6015          * @event headercontextmenu
6016          * Fires when a header is right clicked
6017          * @param {Roo.bootstrap.Table} this
6018          * @param {Number} columnIndex
6019          * @param {Roo.EventObject} e
6020          */
6021         "headercontextmenu" : true
6022     });
6023 };
6024
6025 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6026     
6027     cls: false,
6028     align: false,
6029     bgcolor: false,
6030     border: false,
6031     cellpadding: false,
6032     cellspacing: false,
6033     frame: false,
6034     rules: false,
6035     sortable: false,
6036     summary: false,
6037     width: false,
6038     striped : false,
6039     scrollBody : false,
6040     bordered: false,
6041     hover:  false,
6042     condensed : false,
6043     responsive : false,
6044     sm : false,
6045     cm : false,
6046     store : false,
6047     loadMask : false,
6048     footerShow : true,
6049     headerShow : true,
6050   
6051     rowSelection : false,
6052     cellSelection : false,
6053     layout : false,
6054     
6055     // Roo.Element - the tbody
6056     mainBody: false,
6057     // Roo.Element - thead element
6058     mainHead: false,
6059     
6060     container: false, // used by gridpanel...
6061     
6062     lazyLoad : false,
6063     
6064     CSS : Roo.util.CSS,
6065     
6066     getAutoCreate : function()
6067     {
6068         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6069         
6070         cfg = {
6071             tag: 'table',
6072             cls : 'table',
6073             cn : []
6074         };
6075         if (this.scrollBody) {
6076             cfg.cls += ' table-body-fixed';
6077         }    
6078         if (this.striped) {
6079             cfg.cls += ' table-striped';
6080         }
6081         
6082         if (this.hover) {
6083             cfg.cls += ' table-hover';
6084         }
6085         if (this.bordered) {
6086             cfg.cls += ' table-bordered';
6087         }
6088         if (this.condensed) {
6089             cfg.cls += ' table-condensed';
6090         }
6091         if (this.responsive) {
6092             cfg.cls += ' table-responsive';
6093         }
6094         
6095         if (this.cls) {
6096             cfg.cls+=  ' ' +this.cls;
6097         }
6098         
6099         // this lot should be simplifed...
6100         
6101         if (this.align) {
6102             cfg.align=this.align;
6103         }
6104         if (this.bgcolor) {
6105             cfg.bgcolor=this.bgcolor;
6106         }
6107         if (this.border) {
6108             cfg.border=this.border;
6109         }
6110         if (this.cellpadding) {
6111             cfg.cellpadding=this.cellpadding;
6112         }
6113         if (this.cellspacing) {
6114             cfg.cellspacing=this.cellspacing;
6115         }
6116         if (this.frame) {
6117             cfg.frame=this.frame;
6118         }
6119         if (this.rules) {
6120             cfg.rules=this.rules;
6121         }
6122         if (this.sortable) {
6123             cfg.sortable=this.sortable;
6124         }
6125         if (this.summary) {
6126             cfg.summary=this.summary;
6127         }
6128         if (this.width) {
6129             cfg.width=this.width;
6130         }
6131         if (this.layout) {
6132             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6133         }
6134         
6135         if(this.store || this.cm){
6136             if(this.headerShow){
6137                 cfg.cn.push(this.renderHeader());
6138             }
6139             
6140             cfg.cn.push(this.renderBody());
6141             
6142             if(this.footerShow){
6143                 cfg.cn.push(this.renderFooter());
6144             }
6145             // where does this come from?
6146             //cfg.cls+=  ' TableGrid';
6147         }
6148         
6149         return { cn : [ cfg ] };
6150     },
6151     
6152     initEvents : function()
6153     {   
6154         if(!this.store || !this.cm){
6155             return;
6156         }
6157         if (this.selModel) {
6158             this.selModel.initEvents();
6159         }
6160         
6161         
6162         //Roo.log('initEvents with ds!!!!');
6163         
6164         this.mainBody = this.el.select('tbody', true).first();
6165         this.mainHead = this.el.select('thead', true).first();
6166         
6167         
6168         
6169         
6170         var _this = this;
6171         
6172         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6173             e.on('click', _this.sort, _this);
6174         });
6175         
6176         this.mainBody.on("click", this.onClick, this);
6177         this.mainBody.on("dblclick", this.onDblClick, this);
6178         
6179         // why is this done????? = it breaks dialogs??
6180         //this.parent().el.setStyle('position', 'relative');
6181         
6182         
6183         if (this.footer) {
6184             this.footer.parentId = this.id;
6185             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6186             
6187             if(this.lazyLoad){
6188                 this.el.select('tfoot tr td').first().addClass('hide');
6189             }
6190         } 
6191         
6192         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6193         
6194         this.store.on('load', this.onLoad, this);
6195         this.store.on('beforeload', this.onBeforeLoad, this);
6196         this.store.on('update', this.onUpdate, this);
6197         this.store.on('add', this.onAdd, this);
6198         this.store.on("clear", this.clear, this);
6199         
6200         this.el.on("contextmenu", this.onContextMenu, this);
6201         
6202         this.mainBody.on('scroll', this.onBodyScroll, this);
6203         
6204         this.cm.on("headerchange", this.onHeaderChange, this);
6205         
6206         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6207         
6208     },
6209     
6210     onContextMenu : function(e, t)
6211     {
6212         this.processEvent("contextmenu", e);
6213     },
6214     
6215     processEvent : function(name, e)
6216     {
6217         if (name != 'touchstart' ) {
6218             this.fireEvent(name, e);    
6219         }
6220         
6221         var t = e.getTarget();
6222         
6223         var cell = Roo.get(t);
6224         
6225         if(!cell){
6226             return;
6227         }
6228         
6229         if(cell.findParent('tfoot', false, true)){
6230             return;
6231         }
6232         
6233         if(cell.findParent('thead', false, true)){
6234             
6235             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6236                 cell = Roo.get(t).findParent('th', false, true);
6237                 if (!cell) {
6238                     Roo.log("failed to find th in thead?");
6239                     Roo.log(e.getTarget());
6240                     return;
6241                 }
6242             }
6243             
6244             var cellIndex = cell.dom.cellIndex;
6245             
6246             var ename = name == 'touchstart' ? 'click' : name;
6247             this.fireEvent("header" + ename, this, cellIndex, e);
6248             
6249             return;
6250         }
6251         
6252         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6253             cell = Roo.get(t).findParent('td', false, true);
6254             if (!cell) {
6255                 Roo.log("failed to find th in tbody?");
6256                 Roo.log(e.getTarget());
6257                 return;
6258             }
6259         }
6260         
6261         var row = cell.findParent('tr', false, true);
6262         var cellIndex = cell.dom.cellIndex;
6263         var rowIndex = row.dom.rowIndex - 1;
6264         
6265         if(row !== false){
6266             
6267             this.fireEvent("row" + name, this, rowIndex, e);
6268             
6269             if(cell !== false){
6270             
6271                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6272             }
6273         }
6274         
6275     },
6276     
6277     onMouseover : function(e, el)
6278     {
6279         var cell = Roo.get(el);
6280         
6281         if(!cell){
6282             return;
6283         }
6284         
6285         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6286             cell = cell.findParent('td', false, true);
6287         }
6288         
6289         var row = cell.findParent('tr', false, true);
6290         var cellIndex = cell.dom.cellIndex;
6291         var rowIndex = row.dom.rowIndex - 1; // start from 0
6292         
6293         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6294         
6295     },
6296     
6297     onMouseout : function(e, el)
6298     {
6299         var cell = Roo.get(el);
6300         
6301         if(!cell){
6302             return;
6303         }
6304         
6305         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6306             cell = cell.findParent('td', false, true);
6307         }
6308         
6309         var row = cell.findParent('tr', false, true);
6310         var cellIndex = cell.dom.cellIndex;
6311         var rowIndex = row.dom.rowIndex - 1; // start from 0
6312         
6313         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6314         
6315     },
6316     
6317     onClick : function(e, el)
6318     {
6319         var cell = Roo.get(el);
6320         
6321         if(!cell || (!this.cellSelection && !this.rowSelection)){
6322             return;
6323         }
6324         
6325         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6326             cell = cell.findParent('td', false, true);
6327         }
6328         
6329         if(!cell || typeof(cell) == 'undefined'){
6330             return;
6331         }
6332         
6333         var row = cell.findParent('tr', false, true);
6334         
6335         if(!row || typeof(row) == 'undefined'){
6336             return;
6337         }
6338         
6339         var cellIndex = cell.dom.cellIndex;
6340         var rowIndex = this.getRowIndex(row);
6341         
6342         // why??? - should these not be based on SelectionModel?
6343         if(this.cellSelection){
6344             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6345         }
6346         
6347         if(this.rowSelection){
6348             this.fireEvent('rowclick', this, row, rowIndex, e);
6349         }
6350         
6351         
6352     },
6353         
6354     onDblClick : function(e,el)
6355     {
6356         var cell = Roo.get(el);
6357         
6358         if(!cell || (!this.cellSelection && !this.rowSelection)){
6359             return;
6360         }
6361         
6362         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6363             cell = cell.findParent('td', false, true);
6364         }
6365         
6366         if(!cell || typeof(cell) == 'undefined'){
6367             return;
6368         }
6369         
6370         var row = cell.findParent('tr', false, true);
6371         
6372         if(!row || typeof(row) == 'undefined'){
6373             return;
6374         }
6375         
6376         var cellIndex = cell.dom.cellIndex;
6377         var rowIndex = this.getRowIndex(row);
6378         
6379         if(this.cellSelection){
6380             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6381         }
6382         
6383         if(this.rowSelection){
6384             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6385         }
6386     },
6387     
6388     sort : function(e,el)
6389     {
6390         var col = Roo.get(el);
6391         
6392         if(!col.hasClass('sortable')){
6393             return;
6394         }
6395         
6396         var sort = col.attr('sort');
6397         var dir = 'ASC';
6398         
6399         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6400             dir = 'DESC';
6401         }
6402         
6403         this.store.sortInfo = {field : sort, direction : dir};
6404         
6405         if (this.footer) {
6406             Roo.log("calling footer first");
6407             this.footer.onClick('first');
6408         } else {
6409         
6410             this.store.load({ params : { start : 0 } });
6411         }
6412     },
6413     
6414     renderHeader : function()
6415     {
6416         var header = {
6417             tag: 'thead',
6418             cn : []
6419         };
6420         
6421         var cm = this.cm;
6422         this.totalWidth = 0;
6423         
6424         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6425             
6426             var config = cm.config[i];
6427             
6428             var c = {
6429                 tag: 'th',
6430                 cls : 'x-hcol-' + i,
6431                 style : '',
6432                 html: cm.getColumnHeader(i)
6433             };
6434             
6435             var hh = '';
6436             
6437             if(typeof(config.sortable) != 'undefined' && config.sortable){
6438                 c.cls = 'sortable';
6439                 c.html = '<i class="glyphicon"></i>' + c.html;
6440             }
6441             
6442             if(typeof(config.lgHeader) != 'undefined'){
6443                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6444             }
6445             
6446             if(typeof(config.mdHeader) != 'undefined'){
6447                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6448             }
6449             
6450             if(typeof(config.smHeader) != 'undefined'){
6451                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6452             }
6453             
6454             if(typeof(config.xsHeader) != 'undefined'){
6455                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6456             }
6457             
6458             if(hh.length){
6459                 c.html = hh;
6460             }
6461             
6462             if(typeof(config.tooltip) != 'undefined'){
6463                 c.tooltip = config.tooltip;
6464             }
6465             
6466             if(typeof(config.colspan) != 'undefined'){
6467                 c.colspan = config.colspan;
6468             }
6469             
6470             if(typeof(config.hidden) != 'undefined' && config.hidden){
6471                 c.style += ' display:none;';
6472             }
6473             
6474             if(typeof(config.dataIndex) != 'undefined'){
6475                 c.sort = config.dataIndex;
6476             }
6477             
6478            
6479             
6480             if(typeof(config.align) != 'undefined' && config.align.length){
6481                 c.style += ' text-align:' + config.align + ';';
6482             }
6483             
6484             if(typeof(config.width) != 'undefined'){
6485                 c.style += ' width:' + config.width + 'px;';
6486                 this.totalWidth += config.width;
6487             } else {
6488                 this.totalWidth += 100; // assume minimum of 100 per column?
6489             }
6490             
6491             if(typeof(config.cls) != 'undefined'){
6492                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6493             }
6494             
6495             ['xs','sm','md','lg'].map(function(size){
6496                 
6497                 if(typeof(config[size]) == 'undefined'){
6498                     return;
6499                 }
6500                 
6501                 if (!config[size]) { // 0 = hidden
6502                     c.cls += ' hidden-' + size;
6503                     return;
6504                 }
6505                 
6506                 c.cls += ' col-' + size + '-' + config[size];
6507
6508             });
6509             
6510             header.cn.push(c)
6511         }
6512         
6513         return header;
6514     },
6515     
6516     renderBody : function()
6517     {
6518         var body = {
6519             tag: 'tbody',
6520             cn : [
6521                 {
6522                     tag: 'tr',
6523                     cn : [
6524                         {
6525                             tag : 'td',
6526                             colspan :  this.cm.getColumnCount()
6527                         }
6528                     ]
6529                 }
6530             ]
6531         };
6532         
6533         return body;
6534     },
6535     
6536     renderFooter : function()
6537     {
6538         var footer = {
6539             tag: 'tfoot',
6540             cn : [
6541                 {
6542                     tag: 'tr',
6543                     cn : [
6544                         {
6545                             tag : 'td',
6546                             colspan :  this.cm.getColumnCount()
6547                         }
6548                     ]
6549                 }
6550             ]
6551         };
6552         
6553         return footer;
6554     },
6555     
6556     
6557     
6558     onLoad : function()
6559     {
6560 //        Roo.log('ds onload');
6561         this.clear();
6562         
6563         var _this = this;
6564         var cm = this.cm;
6565         var ds = this.store;
6566         
6567         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6568             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6569             if (_this.store.sortInfo) {
6570                     
6571                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6572                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6573                 }
6574                 
6575                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6576                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6577                 }
6578             }
6579         });
6580         
6581         var tbody =  this.mainBody;
6582               
6583         if(ds.getCount() > 0){
6584             ds.data.each(function(d,rowIndex){
6585                 var row =  this.renderRow(cm, ds, rowIndex);
6586                 
6587                 tbody.createChild(row);
6588                 
6589                 var _this = this;
6590                 
6591                 if(row.cellObjects.length){
6592                     Roo.each(row.cellObjects, function(r){
6593                         _this.renderCellObject(r);
6594                     })
6595                 }
6596                 
6597             }, this);
6598         }
6599         
6600         Roo.each(this.el.select('tbody td', true).elements, function(e){
6601             e.on('mouseover', _this.onMouseover, _this);
6602         });
6603         
6604         Roo.each(this.el.select('tbody td', true).elements, function(e){
6605             e.on('mouseout', _this.onMouseout, _this);
6606         });
6607         this.fireEvent('rowsrendered', this);
6608         //if(this.loadMask){
6609         //    this.maskEl.hide();
6610         //}
6611         
6612         this.autoSize();
6613     },
6614     
6615     
6616     onUpdate : function(ds,record)
6617     {
6618         this.refreshRow(record);
6619         this.autoSize();
6620     },
6621     
6622     onRemove : function(ds, record, index, isUpdate){
6623         if(isUpdate !== true){
6624             this.fireEvent("beforerowremoved", this, index, record);
6625         }
6626         var bt = this.mainBody.dom;
6627         
6628         var rows = this.el.select('tbody > tr', true).elements;
6629         
6630         if(typeof(rows[index]) != 'undefined'){
6631             bt.removeChild(rows[index].dom);
6632         }
6633         
6634 //        if(bt.rows[index]){
6635 //            bt.removeChild(bt.rows[index]);
6636 //        }
6637         
6638         if(isUpdate !== true){
6639             //this.stripeRows(index);
6640             //this.syncRowHeights(index, index);
6641             //this.layout();
6642             this.fireEvent("rowremoved", this, index, record);
6643         }
6644     },
6645     
6646     onAdd : function(ds, records, rowIndex)
6647     {
6648         //Roo.log('on Add called');
6649         // - note this does not handle multiple adding very well..
6650         var bt = this.mainBody.dom;
6651         for (var i =0 ; i < records.length;i++) {
6652             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6653             //Roo.log(records[i]);
6654             //Roo.log(this.store.getAt(rowIndex+i));
6655             this.insertRow(this.store, rowIndex + i, false);
6656             return;
6657         }
6658         
6659     },
6660     
6661     
6662     refreshRow : function(record){
6663         var ds = this.store, index;
6664         if(typeof record == 'number'){
6665             index = record;
6666             record = ds.getAt(index);
6667         }else{
6668             index = ds.indexOf(record);
6669         }
6670         this.insertRow(ds, index, true);
6671         this.autoSize();
6672         this.onRemove(ds, record, index+1, true);
6673         this.autoSize();
6674         //this.syncRowHeights(index, index);
6675         //this.layout();
6676         this.fireEvent("rowupdated", this, index, record);
6677     },
6678     
6679     insertRow : function(dm, rowIndex, isUpdate){
6680         
6681         if(!isUpdate){
6682             this.fireEvent("beforerowsinserted", this, rowIndex);
6683         }
6684             //var s = this.getScrollState();
6685         var row = this.renderRow(this.cm, this.store, rowIndex);
6686         // insert before rowIndex..
6687         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6688         
6689         var _this = this;
6690                 
6691         if(row.cellObjects.length){
6692             Roo.each(row.cellObjects, function(r){
6693                 _this.renderCellObject(r);
6694             })
6695         }
6696             
6697         if(!isUpdate){
6698             this.fireEvent("rowsinserted", this, rowIndex);
6699             //this.syncRowHeights(firstRow, lastRow);
6700             //this.stripeRows(firstRow);
6701             //this.layout();
6702         }
6703         
6704     },
6705     
6706     
6707     getRowDom : function(rowIndex)
6708     {
6709         var rows = this.el.select('tbody > tr', true).elements;
6710         
6711         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6712         
6713     },
6714     // returns the object tree for a tr..
6715   
6716     
6717     renderRow : function(cm, ds, rowIndex) 
6718     {
6719         var d = ds.getAt(rowIndex);
6720         
6721         var row = {
6722             tag : 'tr',
6723             cls : 'x-row-' + rowIndex,
6724             cn : []
6725         };
6726             
6727         var cellObjects = [];
6728         
6729         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6730             var config = cm.config[i];
6731             
6732             var renderer = cm.getRenderer(i);
6733             var value = '';
6734             var id = false;
6735             
6736             if(typeof(renderer) !== 'undefined'){
6737                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6738             }
6739             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6740             // and are rendered into the cells after the row is rendered - using the id for the element.
6741             
6742             if(typeof(value) === 'object'){
6743                 id = Roo.id();
6744                 cellObjects.push({
6745                     container : id,
6746                     cfg : value 
6747                 })
6748             }
6749             
6750             var rowcfg = {
6751                 record: d,
6752                 rowIndex : rowIndex,
6753                 colIndex : i,
6754                 rowClass : ''
6755             };
6756
6757             this.fireEvent('rowclass', this, rowcfg);
6758             
6759             var td = {
6760                 tag: 'td',
6761                 cls : rowcfg.rowClass + ' x-col-' + i,
6762                 style: '',
6763                 html: (typeof(value) === 'object') ? '' : value
6764             };
6765             
6766             if (id) {
6767                 td.id = id;
6768             }
6769             
6770             if(typeof(config.colspan) != 'undefined'){
6771                 td.colspan = config.colspan;
6772             }
6773             
6774             if(typeof(config.hidden) != 'undefined' && config.hidden){
6775                 td.style += ' display:none;';
6776             }
6777             
6778             if(typeof(config.align) != 'undefined' && config.align.length){
6779                 td.style += ' text-align:' + config.align + ';';
6780             }
6781             
6782             if(typeof(config.width) != 'undefined'){
6783                 td.style += ' width:' +  config.width + 'px;';
6784             }
6785             
6786             if(typeof(config.cursor) != 'undefined'){
6787                 td.style += ' cursor:' +  config.cursor + ';';
6788             }
6789             
6790             if(typeof(config.cls) != 'undefined'){
6791                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6792             }
6793             
6794             ['xs','sm','md','lg'].map(function(size){
6795                 
6796                 if(typeof(config[size]) == 'undefined'){
6797                     return;
6798                 }
6799                 
6800                 if (!config[size]) { // 0 = hidden
6801                     td.cls += ' hidden-' + size;
6802                     return;
6803                 }
6804                 
6805                 td.cls += ' col-' + size + '-' + config[size];
6806
6807             });
6808             
6809             row.cn.push(td);
6810            
6811         }
6812         
6813         row.cellObjects = cellObjects;
6814         
6815         return row;
6816           
6817     },
6818     
6819     
6820     
6821     onBeforeLoad : function()
6822     {
6823         //Roo.log('ds onBeforeLoad');
6824         
6825         //this.clear();
6826         
6827         //if(this.loadMask){
6828         //    this.maskEl.show();
6829         //}
6830     },
6831      /**
6832      * Remove all rows
6833      */
6834     clear : function()
6835     {
6836         this.el.select('tbody', true).first().dom.innerHTML = '';
6837     },
6838     /**
6839      * Show or hide a row.
6840      * @param {Number} rowIndex to show or hide
6841      * @param {Boolean} state hide
6842      */
6843     setRowVisibility : function(rowIndex, state)
6844     {
6845         var bt = this.mainBody.dom;
6846         
6847         var rows = this.el.select('tbody > tr', true).elements;
6848         
6849         if(typeof(rows[rowIndex]) == 'undefined'){
6850             return;
6851         }
6852         rows[rowIndex].dom.style.display = state ? '' : 'none';
6853     },
6854     
6855     
6856     getSelectionModel : function(){
6857         if(!this.selModel){
6858             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6859         }
6860         return this.selModel;
6861     },
6862     /*
6863      * Render the Roo.bootstrap object from renderder
6864      */
6865     renderCellObject : function(r)
6866     {
6867         var _this = this;
6868         
6869         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6870         
6871         var t = r.cfg.render(r.container);
6872         
6873         if(r.cfg.cn){
6874             Roo.each(r.cfg.cn, function(c){
6875                 var child = {
6876                     container: t.getChildContainer(),
6877                     cfg: c
6878                 };
6879                 _this.renderCellObject(child);
6880             })
6881         }
6882     },
6883     
6884     getRowIndex : function(row)
6885     {
6886         var rowIndex = -1;
6887         
6888         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6889             if(el != row){
6890                 return;
6891             }
6892             
6893             rowIndex = index;
6894         });
6895         
6896         return rowIndex;
6897     },
6898      /**
6899      * Returns the grid's underlying element = used by panel.Grid
6900      * @return {Element} The element
6901      */
6902     getGridEl : function(){
6903         return this.el;
6904     },
6905      /**
6906      * Forces a resize - used by panel.Grid
6907      * @return {Element} The element
6908      */
6909     autoSize : function()
6910     {
6911         //var ctr = Roo.get(this.container.dom.parentElement);
6912         var ctr = Roo.get(this.el.dom);
6913         
6914         var thd = this.getGridEl().select('thead',true).first();
6915         var tbd = this.getGridEl().select('tbody', true).first();
6916         var tfd = this.getGridEl().select('tfoot', true).first();
6917         
6918         var cw = ctr.getWidth();
6919         
6920         if (tbd) {
6921             
6922             tbd.setSize(ctr.getWidth(),
6923                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6924             );
6925             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6926             cw -= barsize;
6927         }
6928         cw = Math.max(cw, this.totalWidth);
6929         this.getGridEl().select('tr',true).setWidth(cw);
6930         // resize 'expandable coloumn?
6931         
6932         return; // we doe not have a view in this design..
6933         
6934     },
6935     onBodyScroll: function()
6936     {
6937         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6938         if(this.mainHead){
6939             this.mainHead.setStyle({
6940                 'position' : 'relative',
6941                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6942             });
6943         }
6944         
6945         if(this.lazyLoad){
6946             
6947             var scrollHeight = this.mainBody.dom.scrollHeight;
6948             
6949             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6950             
6951             var height = this.mainBody.getHeight();
6952             
6953             if(scrollHeight - height == scrollTop) {
6954                 
6955                 var total = this.ds.getTotalCount();
6956                 
6957                 if(this.footer.cursor + this.footer.pageSize < total){
6958                     
6959                     this.footer.ds.load({
6960                         params : {
6961                             start : this.footer.cursor + this.footer.pageSize,
6962                             limit : this.footer.pageSize
6963                         },
6964                         add : true
6965                     });
6966                 }
6967             }
6968             
6969         }
6970     },
6971     
6972     onHeaderChange : function()
6973     {
6974         var header = this.renderHeader();
6975         var table = this.el.select('table', true).first();
6976         
6977         this.mainHead.remove();
6978         this.mainHead = table.createChild(header, this.mainBody, false);
6979     },
6980     
6981     onHiddenChange : function(colModel, colIndex, hidden)
6982     {
6983         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6984         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6985         
6986         this.CSS.updateRule(thSelector, "display", "");
6987         this.CSS.updateRule(tdSelector, "display", "");
6988         
6989         if(hidden){
6990             this.CSS.updateRule(thSelector, "display", "none");
6991             this.CSS.updateRule(tdSelector, "display", "none");
6992         }
6993         
6994         this.onHeaderChange();
6995         this.onLoad();
6996         
6997     }
6998     
6999 });
7000
7001  
7002
7003  /*
7004  * - LGPL
7005  *
7006  * table cell
7007  * 
7008  */
7009
7010 /**
7011  * @class Roo.bootstrap.TableCell
7012  * @extends Roo.bootstrap.Component
7013  * Bootstrap TableCell class
7014  * @cfg {String} html cell contain text
7015  * @cfg {String} cls cell class
7016  * @cfg {String} tag cell tag (td|th) default td
7017  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7018  * @cfg {String} align Aligns the content in a cell
7019  * @cfg {String} axis Categorizes cells
7020  * @cfg {String} bgcolor Specifies the background color of a cell
7021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7022  * @cfg {Number} colspan Specifies the number of columns a cell should span
7023  * @cfg {String} headers Specifies one or more header cells a cell is related to
7024  * @cfg {Number} height Sets the height of a cell
7025  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7026  * @cfg {Number} rowspan Sets the number of rows a cell should span
7027  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7028  * @cfg {String} valign Vertical aligns the content in a cell
7029  * @cfg {Number} width Specifies the width of a cell
7030  * 
7031  * @constructor
7032  * Create a new TableCell
7033  * @param {Object} config The config object
7034  */
7035
7036 Roo.bootstrap.TableCell = function(config){
7037     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7038 };
7039
7040 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7041     
7042     html: false,
7043     cls: false,
7044     tag: false,
7045     abbr: false,
7046     align: false,
7047     axis: false,
7048     bgcolor: false,
7049     charoff: false,
7050     colspan: false,
7051     headers: false,
7052     height: false,
7053     nowrap: false,
7054     rowspan: false,
7055     scope: false,
7056     valign: false,
7057     width: false,
7058     
7059     
7060     getAutoCreate : function(){
7061         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7062         
7063         cfg = {
7064             tag: 'td'
7065         };
7066         
7067         if(this.tag){
7068             cfg.tag = this.tag;
7069         }
7070         
7071         if (this.html) {
7072             cfg.html=this.html
7073         }
7074         if (this.cls) {
7075             cfg.cls=this.cls
7076         }
7077         if (this.abbr) {
7078             cfg.abbr=this.abbr
7079         }
7080         if (this.align) {
7081             cfg.align=this.align
7082         }
7083         if (this.axis) {
7084             cfg.axis=this.axis
7085         }
7086         if (this.bgcolor) {
7087             cfg.bgcolor=this.bgcolor
7088         }
7089         if (this.charoff) {
7090             cfg.charoff=this.charoff
7091         }
7092         if (this.colspan) {
7093             cfg.colspan=this.colspan
7094         }
7095         if (this.headers) {
7096             cfg.headers=this.headers
7097         }
7098         if (this.height) {
7099             cfg.height=this.height
7100         }
7101         if (this.nowrap) {
7102             cfg.nowrap=this.nowrap
7103         }
7104         if (this.rowspan) {
7105             cfg.rowspan=this.rowspan
7106         }
7107         if (this.scope) {
7108             cfg.scope=this.scope
7109         }
7110         if (this.valign) {
7111             cfg.valign=this.valign
7112         }
7113         if (this.width) {
7114             cfg.width=this.width
7115         }
7116         
7117         
7118         return cfg;
7119     }
7120    
7121 });
7122
7123  
7124
7125  /*
7126  * - LGPL
7127  *
7128  * table row
7129  * 
7130  */
7131
7132 /**
7133  * @class Roo.bootstrap.TableRow
7134  * @extends Roo.bootstrap.Component
7135  * Bootstrap TableRow class
7136  * @cfg {String} cls row class
7137  * @cfg {String} align Aligns the content in a table row
7138  * @cfg {String} bgcolor Specifies a background color for a table row
7139  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7140  * @cfg {String} valign Vertical aligns the content in a table row
7141  * 
7142  * @constructor
7143  * Create a new TableRow
7144  * @param {Object} config The config object
7145  */
7146
7147 Roo.bootstrap.TableRow = function(config){
7148     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7149 };
7150
7151 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7152     
7153     cls: false,
7154     align: false,
7155     bgcolor: false,
7156     charoff: false,
7157     valign: false,
7158     
7159     getAutoCreate : function(){
7160         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7161         
7162         cfg = {
7163             tag: 'tr'
7164         };
7165             
7166         if(this.cls){
7167             cfg.cls = this.cls;
7168         }
7169         if(this.align){
7170             cfg.align = this.align;
7171         }
7172         if(this.bgcolor){
7173             cfg.bgcolor = this.bgcolor;
7174         }
7175         if(this.charoff){
7176             cfg.charoff = this.charoff;
7177         }
7178         if(this.valign){
7179             cfg.valign = this.valign;
7180         }
7181         
7182         return cfg;
7183     }
7184    
7185 });
7186
7187  
7188
7189  /*
7190  * - LGPL
7191  *
7192  * table body
7193  * 
7194  */
7195
7196 /**
7197  * @class Roo.bootstrap.TableBody
7198  * @extends Roo.bootstrap.Component
7199  * Bootstrap TableBody class
7200  * @cfg {String} cls element class
7201  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7202  * @cfg {String} align Aligns the content inside the element
7203  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7204  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7205  * 
7206  * @constructor
7207  * Create a new TableBody
7208  * @param {Object} config The config object
7209  */
7210
7211 Roo.bootstrap.TableBody = function(config){
7212     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7213 };
7214
7215 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7216     
7217     cls: false,
7218     tag: false,
7219     align: false,
7220     charoff: false,
7221     valign: false,
7222     
7223     getAutoCreate : function(){
7224         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7225         
7226         cfg = {
7227             tag: 'tbody'
7228         };
7229             
7230         if (this.cls) {
7231             cfg.cls=this.cls
7232         }
7233         if(this.tag){
7234             cfg.tag = this.tag;
7235         }
7236         
7237         if(this.align){
7238             cfg.align = this.align;
7239         }
7240         if(this.charoff){
7241             cfg.charoff = this.charoff;
7242         }
7243         if(this.valign){
7244             cfg.valign = this.valign;
7245         }
7246         
7247         return cfg;
7248     }
7249     
7250     
7251 //    initEvents : function()
7252 //    {
7253 //        
7254 //        if(!this.store){
7255 //            return;
7256 //        }
7257 //        
7258 //        this.store = Roo.factory(this.store, Roo.data);
7259 //        this.store.on('load', this.onLoad, this);
7260 //        
7261 //        this.store.load();
7262 //        
7263 //    },
7264 //    
7265 //    onLoad: function () 
7266 //    {   
7267 //        this.fireEvent('load', this);
7268 //    }
7269 //    
7270 //   
7271 });
7272
7273  
7274
7275  /*
7276  * Based on:
7277  * Ext JS Library 1.1.1
7278  * Copyright(c) 2006-2007, Ext JS, LLC.
7279  *
7280  * Originally Released Under LGPL - original licence link has changed is not relivant.
7281  *
7282  * Fork - LGPL
7283  * <script type="text/javascript">
7284  */
7285
7286 // as we use this in bootstrap.
7287 Roo.namespace('Roo.form');
7288  /**
7289  * @class Roo.form.Action
7290  * Internal Class used to handle form actions
7291  * @constructor
7292  * @param {Roo.form.BasicForm} el The form element or its id
7293  * @param {Object} config Configuration options
7294  */
7295
7296  
7297  
7298 // define the action interface
7299 Roo.form.Action = function(form, options){
7300     this.form = form;
7301     this.options = options || {};
7302 };
7303 /**
7304  * Client Validation Failed
7305  * @const 
7306  */
7307 Roo.form.Action.CLIENT_INVALID = 'client';
7308 /**
7309  * Server Validation Failed
7310  * @const 
7311  */
7312 Roo.form.Action.SERVER_INVALID = 'server';
7313  /**
7314  * Connect to Server Failed
7315  * @const 
7316  */
7317 Roo.form.Action.CONNECT_FAILURE = 'connect';
7318 /**
7319  * Reading Data from Server Failed
7320  * @const 
7321  */
7322 Roo.form.Action.LOAD_FAILURE = 'load';
7323
7324 Roo.form.Action.prototype = {
7325     type : 'default',
7326     failureType : undefined,
7327     response : undefined,
7328     result : undefined,
7329
7330     // interface method
7331     run : function(options){
7332
7333     },
7334
7335     // interface method
7336     success : function(response){
7337
7338     },
7339
7340     // interface method
7341     handleResponse : function(response){
7342
7343     },
7344
7345     // default connection failure
7346     failure : function(response){
7347         
7348         this.response = response;
7349         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7350         this.form.afterAction(this, false);
7351     },
7352
7353     processResponse : function(response){
7354         this.response = response;
7355         if(!response.responseText){
7356             return true;
7357         }
7358         this.result = this.handleResponse(response);
7359         return this.result;
7360     },
7361
7362     // utility functions used internally
7363     getUrl : function(appendParams){
7364         var url = this.options.url || this.form.url || this.form.el.dom.action;
7365         if(appendParams){
7366             var p = this.getParams();
7367             if(p){
7368                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7369             }
7370         }
7371         return url;
7372     },
7373
7374     getMethod : function(){
7375         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7376     },
7377
7378     getParams : function(){
7379         var bp = this.form.baseParams;
7380         var p = this.options.params;
7381         if(p){
7382             if(typeof p == "object"){
7383                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7384             }else if(typeof p == 'string' && bp){
7385                 p += '&' + Roo.urlEncode(bp);
7386             }
7387         }else if(bp){
7388             p = Roo.urlEncode(bp);
7389         }
7390         return p;
7391     },
7392
7393     createCallback : function(){
7394         return {
7395             success: this.success,
7396             failure: this.failure,
7397             scope: this,
7398             timeout: (this.form.timeout*1000),
7399             upload: this.form.fileUpload ? this.success : undefined
7400         };
7401     }
7402 };
7403
7404 Roo.form.Action.Submit = function(form, options){
7405     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7406 };
7407
7408 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7409     type : 'submit',
7410
7411     haveProgress : false,
7412     uploadComplete : false,
7413     
7414     // uploadProgress indicator.
7415     uploadProgress : function()
7416     {
7417         if (!this.form.progressUrl) {
7418             return;
7419         }
7420         
7421         if (!this.haveProgress) {
7422             Roo.MessageBox.progress("Uploading", "Uploading");
7423         }
7424         if (this.uploadComplete) {
7425            Roo.MessageBox.hide();
7426            return;
7427         }
7428         
7429         this.haveProgress = true;
7430    
7431         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7432         
7433         var c = new Roo.data.Connection();
7434         c.request({
7435             url : this.form.progressUrl,
7436             params: {
7437                 id : uid
7438             },
7439             method: 'GET',
7440             success : function(req){
7441                //console.log(data);
7442                 var rdata = false;
7443                 var edata;
7444                 try  {
7445                    rdata = Roo.decode(req.responseText)
7446                 } catch (e) {
7447                     Roo.log("Invalid data from server..");
7448                     Roo.log(edata);
7449                     return;
7450                 }
7451                 if (!rdata || !rdata.success) {
7452                     Roo.log(rdata);
7453                     Roo.MessageBox.alert(Roo.encode(rdata));
7454                     return;
7455                 }
7456                 var data = rdata.data;
7457                 
7458                 if (this.uploadComplete) {
7459                    Roo.MessageBox.hide();
7460                    return;
7461                 }
7462                    
7463                 if (data){
7464                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7465                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7466                     );
7467                 }
7468                 this.uploadProgress.defer(2000,this);
7469             },
7470        
7471             failure: function(data) {
7472                 Roo.log('progress url failed ');
7473                 Roo.log(data);
7474             },
7475             scope : this
7476         });
7477            
7478     },
7479     
7480     
7481     run : function()
7482     {
7483         // run get Values on the form, so it syncs any secondary forms.
7484         this.form.getValues();
7485         
7486         var o = this.options;
7487         var method = this.getMethod();
7488         var isPost = method == 'POST';
7489         if(o.clientValidation === false || this.form.isValid()){
7490             
7491             if (this.form.progressUrl) {
7492                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7493                     (new Date() * 1) + '' + Math.random());
7494                     
7495             } 
7496             
7497             
7498             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7499                 form:this.form.el.dom,
7500                 url:this.getUrl(!isPost),
7501                 method: method,
7502                 params:isPost ? this.getParams() : null,
7503                 isUpload: this.form.fileUpload
7504             }));
7505             
7506             this.uploadProgress();
7507
7508         }else if (o.clientValidation !== false){ // client validation failed
7509             this.failureType = Roo.form.Action.CLIENT_INVALID;
7510             this.form.afterAction(this, false);
7511         }
7512     },
7513
7514     success : function(response)
7515     {
7516         this.uploadComplete= true;
7517         if (this.haveProgress) {
7518             Roo.MessageBox.hide();
7519         }
7520         
7521         
7522         var result = this.processResponse(response);
7523         if(result === true || result.success){
7524             this.form.afterAction(this, true);
7525             return;
7526         }
7527         if(result.errors){
7528             this.form.markInvalid(result.errors);
7529             this.failureType = Roo.form.Action.SERVER_INVALID;
7530         }
7531         this.form.afterAction(this, false);
7532     },
7533     failure : function(response)
7534     {
7535         this.uploadComplete= true;
7536         if (this.haveProgress) {
7537             Roo.MessageBox.hide();
7538         }
7539         
7540         this.response = response;
7541         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7542         this.form.afterAction(this, false);
7543     },
7544     
7545     handleResponse : function(response){
7546         if(this.form.errorReader){
7547             var rs = this.form.errorReader.read(response);
7548             var errors = [];
7549             if(rs.records){
7550                 for(var i = 0, len = rs.records.length; i < len; i++) {
7551                     var r = rs.records[i];
7552                     errors[i] = r.data;
7553                 }
7554             }
7555             if(errors.length < 1){
7556                 errors = null;
7557             }
7558             return {
7559                 success : rs.success,
7560                 errors : errors
7561             };
7562         }
7563         var ret = false;
7564         try {
7565             ret = Roo.decode(response.responseText);
7566         } catch (e) {
7567             ret = {
7568                 success: false,
7569                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7570                 errors : []
7571             };
7572         }
7573         return ret;
7574         
7575     }
7576 });
7577
7578
7579 Roo.form.Action.Load = function(form, options){
7580     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7581     this.reader = this.form.reader;
7582 };
7583
7584 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7585     type : 'load',
7586
7587     run : function(){
7588         
7589         Roo.Ajax.request(Roo.apply(
7590                 this.createCallback(), {
7591                     method:this.getMethod(),
7592                     url:this.getUrl(false),
7593                     params:this.getParams()
7594         }));
7595     },
7596
7597     success : function(response){
7598         
7599         var result = this.processResponse(response);
7600         if(result === true || !result.success || !result.data){
7601             this.failureType = Roo.form.Action.LOAD_FAILURE;
7602             this.form.afterAction(this, false);
7603             return;
7604         }
7605         this.form.clearInvalid();
7606         this.form.setValues(result.data);
7607         this.form.afterAction(this, true);
7608     },
7609
7610     handleResponse : function(response){
7611         if(this.form.reader){
7612             var rs = this.form.reader.read(response);
7613             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7614             return {
7615                 success : rs.success,
7616                 data : data
7617             };
7618         }
7619         return Roo.decode(response.responseText);
7620     }
7621 });
7622
7623 Roo.form.Action.ACTION_TYPES = {
7624     'load' : Roo.form.Action.Load,
7625     'submit' : Roo.form.Action.Submit
7626 };/*
7627  * - LGPL
7628  *
7629  * form
7630  *
7631  */
7632
7633 /**
7634  * @class Roo.bootstrap.Form
7635  * @extends Roo.bootstrap.Component
7636  * Bootstrap Form class
7637  * @cfg {String} method  GET | POST (default POST)
7638  * @cfg {String} labelAlign top | left (default top)
7639  * @cfg {String} align left  | right - for navbars
7640  * @cfg {Boolean} loadMask load mask when submit (default true)
7641
7642  *
7643  * @constructor
7644  * Create a new Form
7645  * @param {Object} config The config object
7646  */
7647
7648
7649 Roo.bootstrap.Form = function(config){
7650     
7651     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7652     
7653     Roo.bootstrap.Form.popover.apply();
7654     
7655     this.addEvents({
7656         /**
7657          * @event clientvalidation
7658          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7659          * @param {Form} this
7660          * @param {Boolean} valid true if the form has passed client-side validation
7661          */
7662         clientvalidation: true,
7663         /**
7664          * @event beforeaction
7665          * Fires before any action is performed. Return false to cancel the action.
7666          * @param {Form} this
7667          * @param {Action} action The action to be performed
7668          */
7669         beforeaction: true,
7670         /**
7671          * @event actionfailed
7672          * Fires when an action fails.
7673          * @param {Form} this
7674          * @param {Action} action The action that failed
7675          */
7676         actionfailed : true,
7677         /**
7678          * @event actioncomplete
7679          * Fires when an action is completed.
7680          * @param {Form} this
7681          * @param {Action} action The action that completed
7682          */
7683         actioncomplete : true
7684     });
7685 };
7686
7687 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7688
7689      /**
7690      * @cfg {String} method
7691      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7692      */
7693     method : 'POST',
7694     /**
7695      * @cfg {String} url
7696      * The URL to use for form actions if one isn't supplied in the action options.
7697      */
7698     /**
7699      * @cfg {Boolean} fileUpload
7700      * Set to true if this form is a file upload.
7701      */
7702
7703     /**
7704      * @cfg {Object} baseParams
7705      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7706      */
7707
7708     /**
7709      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7710      */
7711     timeout: 30,
7712     /**
7713      * @cfg {Sting} align (left|right) for navbar forms
7714      */
7715     align : 'left',
7716
7717     // private
7718     activeAction : null,
7719
7720     /**
7721      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7722      * element by passing it or its id or mask the form itself by passing in true.
7723      * @type Mixed
7724      */
7725     waitMsgTarget : false,
7726
7727     loadMask : true,
7728     
7729     /**
7730      * @cfg {Boolean} errorMask (true|false) default false
7731      */
7732     errorMask : false,
7733     
7734     /**
7735      * @cfg {Number} maskOffset Default 100
7736      */
7737     maskOffset : 100,
7738     
7739     /**
7740      * @cfg {Boolean} maskBody
7741      */
7742     maskBody : false,
7743
7744     getAutoCreate : function(){
7745
7746         var cfg = {
7747             tag: 'form',
7748             method : this.method || 'POST',
7749             id : this.id || Roo.id(),
7750             cls : ''
7751         };
7752         if (this.parent().xtype.match(/^Nav/)) {
7753             cfg.cls = 'navbar-form navbar-' + this.align;
7754
7755         }
7756
7757         if (this.labelAlign == 'left' ) {
7758             cfg.cls += ' form-horizontal';
7759         }
7760
7761
7762         return cfg;
7763     },
7764     initEvents : function()
7765     {
7766         this.el.on('submit', this.onSubmit, this);
7767         // this was added as random key presses on the form where triggering form submit.
7768         this.el.on('keypress', function(e) {
7769             if (e.getCharCode() != 13) {
7770                 return true;
7771             }
7772             // we might need to allow it for textareas.. and some other items.
7773             // check e.getTarget().
7774
7775             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7776                 return true;
7777             }
7778
7779             Roo.log("keypress blocked");
7780
7781             e.preventDefault();
7782             return false;
7783         });
7784         
7785     },
7786     // private
7787     onSubmit : function(e){
7788         e.stopEvent();
7789     },
7790
7791      /**
7792      * Returns true if client-side validation on the form is successful.
7793      * @return Boolean
7794      */
7795     isValid : function(){
7796         var items = this.getItems();
7797         var valid = true;
7798         var target = false;
7799         
7800         items.each(function(f){
7801             
7802             if(f.validate()){
7803                 return;
7804             }
7805             valid = false;
7806
7807             if(!target && f.el.isVisible(true)){
7808                 target = f;
7809             }
7810            
7811         });
7812         
7813         if(this.errorMask && !valid){
7814             Roo.bootstrap.Form.popover.mask(this, target);
7815         }
7816         
7817         return valid;
7818     },
7819     
7820     /**
7821      * Returns true if any fields in this form have changed since their original load.
7822      * @return Boolean
7823      */
7824     isDirty : function(){
7825         var dirty = false;
7826         var items = this.getItems();
7827         items.each(function(f){
7828            if(f.isDirty()){
7829                dirty = true;
7830                return false;
7831            }
7832            return true;
7833         });
7834         return dirty;
7835     },
7836      /**
7837      * Performs a predefined action (submit or load) or custom actions you define on this form.
7838      * @param {String} actionName The name of the action type
7839      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7840      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7841      * accept other config options):
7842      * <pre>
7843 Property          Type             Description
7844 ----------------  ---------------  ----------------------------------------------------------------------------------
7845 url               String           The url for the action (defaults to the form's url)
7846 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7847 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7848 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7849                                    validate the form on the client (defaults to false)
7850      * </pre>
7851      * @return {BasicForm} this
7852      */
7853     doAction : function(action, options){
7854         if(typeof action == 'string'){
7855             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7856         }
7857         if(this.fireEvent('beforeaction', this, action) !== false){
7858             this.beforeAction(action);
7859             action.run.defer(100, action);
7860         }
7861         return this;
7862     },
7863
7864     // private
7865     beforeAction : function(action){
7866         var o = action.options;
7867         
7868         if(this.loadMask){
7869             
7870             if(this.maskBody){
7871                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7872             } else {
7873                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7874             }
7875         }
7876         // not really supported yet.. ??
7877
7878         //if(this.waitMsgTarget === true){
7879         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7880         //}else if(this.waitMsgTarget){
7881         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7882         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7883         //}else {
7884         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7885        // }
7886
7887     },
7888
7889     // private
7890     afterAction : function(action, success){
7891         this.activeAction = null;
7892         var o = action.options;
7893
7894         if(this.loadMask){
7895             
7896             if(this.maskBody){
7897                 Roo.get(document.body).unmask();
7898             } else {
7899                 this.el.unmask();
7900             }
7901         }
7902         
7903         //if(this.waitMsgTarget === true){
7904 //            this.el.unmask();
7905         //}else if(this.waitMsgTarget){
7906         //    this.waitMsgTarget.unmask();
7907         //}else{
7908         //    Roo.MessageBox.updateProgress(1);
7909         //    Roo.MessageBox.hide();
7910        // }
7911         //
7912         if(success){
7913             if(o.reset){
7914                 this.reset();
7915             }
7916             Roo.callback(o.success, o.scope, [this, action]);
7917             this.fireEvent('actioncomplete', this, action);
7918
7919         }else{
7920
7921             // failure condition..
7922             // we have a scenario where updates need confirming.
7923             // eg. if a locking scenario exists..
7924             // we look for { errors : { needs_confirm : true }} in the response.
7925             if (
7926                 (typeof(action.result) != 'undefined')  &&
7927                 (typeof(action.result.errors) != 'undefined')  &&
7928                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7929            ){
7930                 var _t = this;
7931                 Roo.log("not supported yet");
7932                  /*
7933
7934                 Roo.MessageBox.confirm(
7935                     "Change requires confirmation",
7936                     action.result.errorMsg,
7937                     function(r) {
7938                         if (r != 'yes') {
7939                             return;
7940                         }
7941                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7942                     }
7943
7944                 );
7945                 */
7946
7947
7948                 return;
7949             }
7950
7951             Roo.callback(o.failure, o.scope, [this, action]);
7952             // show an error message if no failed handler is set..
7953             if (!this.hasListener('actionfailed')) {
7954                 Roo.log("need to add dialog support");
7955                 /*
7956                 Roo.MessageBox.alert("Error",
7957                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7958                         action.result.errorMsg :
7959                         "Saving Failed, please check your entries or try again"
7960                 );
7961                 */
7962             }
7963
7964             this.fireEvent('actionfailed', this, action);
7965         }
7966
7967     },
7968     /**
7969      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7970      * @param {String} id The value to search for
7971      * @return Field
7972      */
7973     findField : function(id){
7974         var items = this.getItems();
7975         var field = items.get(id);
7976         if(!field){
7977              items.each(function(f){
7978                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7979                     field = f;
7980                     return false;
7981                 }
7982                 return true;
7983             });
7984         }
7985         return field || null;
7986     },
7987      /**
7988      * Mark fields in this form invalid in bulk.
7989      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7990      * @return {BasicForm} this
7991      */
7992     markInvalid : function(errors){
7993         if(errors instanceof Array){
7994             for(var i = 0, len = errors.length; i < len; i++){
7995                 var fieldError = errors[i];
7996                 var f = this.findField(fieldError.id);
7997                 if(f){
7998                     f.markInvalid(fieldError.msg);
7999                 }
8000             }
8001         }else{
8002             var field, id;
8003             for(id in errors){
8004                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8005                     field.markInvalid(errors[id]);
8006                 }
8007             }
8008         }
8009         //Roo.each(this.childForms || [], function (f) {
8010         //    f.markInvalid(errors);
8011         //});
8012
8013         return this;
8014     },
8015
8016     /**
8017      * Set values for fields in this form in bulk.
8018      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8019      * @return {BasicForm} this
8020      */
8021     setValues : function(values){
8022         if(values instanceof Array){ // array of objects
8023             for(var i = 0, len = values.length; i < len; i++){
8024                 var v = values[i];
8025                 var f = this.findField(v.id);
8026                 if(f){
8027                     f.setValue(v.value);
8028                     if(this.trackResetOnLoad){
8029                         f.originalValue = f.getValue();
8030                     }
8031                 }
8032             }
8033         }else{ // object hash
8034             var field, id;
8035             for(id in values){
8036                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8037
8038                     if (field.setFromData &&
8039                         field.valueField &&
8040                         field.displayField &&
8041                         // combos' with local stores can
8042                         // be queried via setValue()
8043                         // to set their value..
8044                         (field.store && !field.store.isLocal)
8045                         ) {
8046                         // it's a combo
8047                         var sd = { };
8048                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8049                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8050                         field.setFromData(sd);
8051
8052                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8053                         
8054                         field.setFromData(values);
8055                         
8056                     } else {
8057                         field.setValue(values[id]);
8058                     }
8059
8060
8061                     if(this.trackResetOnLoad){
8062                         field.originalValue = field.getValue();
8063                     }
8064                 }
8065             }
8066         }
8067
8068         //Roo.each(this.childForms || [], function (f) {
8069         //    f.setValues(values);
8070         //});
8071
8072         return this;
8073     },
8074
8075     /**
8076      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8077      * they are returned as an array.
8078      * @param {Boolean} asString
8079      * @return {Object}
8080      */
8081     getValues : function(asString){
8082         //if (this.childForms) {
8083             // copy values from the child forms
8084         //    Roo.each(this.childForms, function (f) {
8085         //        this.setValues(f.getValues());
8086         //    }, this);
8087         //}
8088
8089
8090
8091         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8092         if(asString === true){
8093             return fs;
8094         }
8095         return Roo.urlDecode(fs);
8096     },
8097
8098     /**
8099      * Returns the fields in this form as an object with key/value pairs.
8100      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8101      * @return {Object}
8102      */
8103     getFieldValues : function(with_hidden)
8104     {
8105         var items = this.getItems();
8106         var ret = {};
8107         items.each(function(f){
8108             
8109             if (!f.getName()) {
8110                 return;
8111             }
8112             
8113             var v = f.getValue();
8114             
8115             if (f.inputType =='radio') {
8116                 if (typeof(ret[f.getName()]) == 'undefined') {
8117                     ret[f.getName()] = ''; // empty..
8118                 }
8119
8120                 if (!f.el.dom.checked) {
8121                     return;
8122
8123                 }
8124                 v = f.el.dom.value;
8125
8126             }
8127             
8128             if(f.xtype == 'MoneyField'){
8129                 ret[f.currencyName] = f.getCurrency();
8130             }
8131
8132             // not sure if this supported any more..
8133             if ((typeof(v) == 'object') && f.getRawValue) {
8134                 v = f.getRawValue() ; // dates..
8135             }
8136             // combo boxes where name != hiddenName...
8137             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8138                 ret[f.name] = f.getRawValue();
8139             }
8140             ret[f.getName()] = v;
8141         });
8142
8143         return ret;
8144     },
8145
8146     /**
8147      * Clears all invalid messages in this form.
8148      * @return {BasicForm} this
8149      */
8150     clearInvalid : function(){
8151         var items = this.getItems();
8152
8153         items.each(function(f){
8154            f.clearInvalid();
8155         });
8156
8157         return this;
8158     },
8159
8160     /**
8161      * Resets this form.
8162      * @return {BasicForm} this
8163      */
8164     reset : function(){
8165         var items = this.getItems();
8166         items.each(function(f){
8167             f.reset();
8168         });
8169
8170         Roo.each(this.childForms || [], function (f) {
8171             f.reset();
8172         });
8173
8174
8175         return this;
8176     },
8177     
8178     getItems : function()
8179     {
8180         var r=new Roo.util.MixedCollection(false, function(o){
8181             return o.id || (o.id = Roo.id());
8182         });
8183         var iter = function(el) {
8184             if (el.inputEl) {
8185                 r.add(el);
8186             }
8187             if (!el.items) {
8188                 return;
8189             }
8190             Roo.each(el.items,function(e) {
8191                 iter(e);
8192             });
8193         };
8194
8195         iter(this);
8196         return r;
8197     },
8198     
8199     hideFields : function(items)
8200     {
8201         Roo.each(items, function(i){
8202             
8203             var f = this.findField(i);
8204             
8205             if(!f){
8206                 return;
8207             }
8208             
8209             if(f.xtype == 'DateField'){
8210                 f.setVisible(false);
8211                 return;
8212             }
8213             
8214             f.hide();
8215             
8216         }, this);
8217     },
8218     
8219     showFields : function(items)
8220     {
8221         Roo.each(items, function(i){
8222             
8223             var f = this.findField(i);
8224             
8225             if(!f){
8226                 return;
8227             }
8228             
8229             if(f.xtype == 'DateField'){
8230                 f.setVisible(true);
8231                 return;
8232             }
8233             
8234             f.show();
8235             
8236         }, this);
8237     }
8238
8239 });
8240
8241 Roo.apply(Roo.bootstrap.Form, {
8242     
8243     popover : {
8244         
8245         padding : 5,
8246         
8247         isApplied : false,
8248         
8249         isMasked : false,
8250         
8251         form : false,
8252         
8253         target : false,
8254         
8255         toolTip : false,
8256         
8257         intervalID : false,
8258         
8259         maskEl : false,
8260         
8261         apply : function()
8262         {
8263             if(this.isApplied){
8264                 return;
8265             }
8266             
8267             this.maskEl = {
8268                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8269                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8270                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8271                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8272             };
8273             
8274             this.maskEl.top.enableDisplayMode("block");
8275             this.maskEl.left.enableDisplayMode("block");
8276             this.maskEl.bottom.enableDisplayMode("block");
8277             this.maskEl.right.enableDisplayMode("block");
8278             
8279             this.toolTip = new Roo.bootstrap.Tooltip({
8280                 cls : 'roo-form-error-popover',
8281                 alignment : {
8282                     'left' : ['r-l', [-2,0], 'right'],
8283                     'right' : ['l-r', [2,0], 'left'],
8284                     'bottom' : ['tl-bl', [0,2], 'top'],
8285                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8286                 }
8287             });
8288             
8289             this.toolTip.render(Roo.get(document.body));
8290
8291             this.toolTip.el.enableDisplayMode("block");
8292             
8293             Roo.get(document.body).on('click', function(){
8294                 this.unmask();
8295             }, this);
8296             
8297             Roo.get(document.body).on('touchstart', function(){
8298                 this.unmask();
8299             }, this);
8300             
8301             this.isApplied = true
8302         },
8303         
8304         mask : function(form, target)
8305         {
8306             this.form = form;
8307             
8308             this.target = target;
8309             
8310             if(!this.form.errorMask || !target.el){
8311                 return;
8312             }
8313             
8314             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8315             
8316             Roo.log(scrollable);
8317             
8318             var ot = this.target.el.calcOffsetsTo(scrollable);
8319             
8320             var scrollTo = ot[1] - this.form.maskOffset;
8321             
8322             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8323             
8324             scrollable.scrollTo('top', scrollTo);
8325             
8326             var box = this.target.el.getBox();
8327             Roo.log(box);
8328             var zIndex = Roo.bootstrap.Modal.zIndex++;
8329
8330             
8331             this.maskEl.top.setStyle('position', 'absolute');
8332             this.maskEl.top.setStyle('z-index', zIndex);
8333             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8334             this.maskEl.top.setLeft(0);
8335             this.maskEl.top.setTop(0);
8336             this.maskEl.top.show();
8337             
8338             this.maskEl.left.setStyle('position', 'absolute');
8339             this.maskEl.left.setStyle('z-index', zIndex);
8340             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8341             this.maskEl.left.setLeft(0);
8342             this.maskEl.left.setTop(box.y - this.padding);
8343             this.maskEl.left.show();
8344
8345             this.maskEl.bottom.setStyle('position', 'absolute');
8346             this.maskEl.bottom.setStyle('z-index', zIndex);
8347             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8348             this.maskEl.bottom.setLeft(0);
8349             this.maskEl.bottom.setTop(box.bottom + this.padding);
8350             this.maskEl.bottom.show();
8351
8352             this.maskEl.right.setStyle('position', 'absolute');
8353             this.maskEl.right.setStyle('z-index', zIndex);
8354             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8355             this.maskEl.right.setLeft(box.right + this.padding);
8356             this.maskEl.right.setTop(box.y - this.padding);
8357             this.maskEl.right.show();
8358
8359             this.toolTip.bindEl = this.target.el;
8360
8361             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8362
8363             var tip = this.target.blankText;
8364
8365             if(this.target.getValue() !== '' ) {
8366                 
8367                 if (this.target.invalidText.length) {
8368                     tip = this.target.invalidText;
8369                 } else if (this.target.regexText.length){
8370                     tip = this.target.regexText;
8371                 }
8372             }
8373
8374             this.toolTip.show(tip);
8375
8376             this.intervalID = window.setInterval(function() {
8377                 Roo.bootstrap.Form.popover.unmask();
8378             }, 10000);
8379
8380             window.onwheel = function(){ return false;};
8381             
8382             (function(){ this.isMasked = true; }).defer(500, this);
8383             
8384         },
8385         
8386         unmask : function()
8387         {
8388             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8389                 return;
8390             }
8391             
8392             this.maskEl.top.setStyle('position', 'absolute');
8393             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8394             this.maskEl.top.hide();
8395
8396             this.maskEl.left.setStyle('position', 'absolute');
8397             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8398             this.maskEl.left.hide();
8399
8400             this.maskEl.bottom.setStyle('position', 'absolute');
8401             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8402             this.maskEl.bottom.hide();
8403
8404             this.maskEl.right.setStyle('position', 'absolute');
8405             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8406             this.maskEl.right.hide();
8407             
8408             this.toolTip.hide();
8409             
8410             this.toolTip.el.hide();
8411             
8412             window.onwheel = function(){ return true;};
8413             
8414             if(this.intervalID){
8415                 window.clearInterval(this.intervalID);
8416                 this.intervalID = false;
8417             }
8418             
8419             this.isMasked = false;
8420             
8421         }
8422         
8423     }
8424     
8425 });
8426
8427 /*
8428  * Based on:
8429  * Ext JS Library 1.1.1
8430  * Copyright(c) 2006-2007, Ext JS, LLC.
8431  *
8432  * Originally Released Under LGPL - original licence link has changed is not relivant.
8433  *
8434  * Fork - LGPL
8435  * <script type="text/javascript">
8436  */
8437 /**
8438  * @class Roo.form.VTypes
8439  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8440  * @singleton
8441  */
8442 Roo.form.VTypes = function(){
8443     // closure these in so they are only created once.
8444     var alpha = /^[a-zA-Z_]+$/;
8445     var alphanum = /^[a-zA-Z0-9_]+$/;
8446     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8447     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8448
8449     // All these messages and functions are configurable
8450     return {
8451         /**
8452          * The function used to validate email addresses
8453          * @param {String} value The email address
8454          */
8455         'email' : function(v){
8456             return email.test(v);
8457         },
8458         /**
8459          * The error text to display when the email validation function returns false
8460          * @type String
8461          */
8462         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8463         /**
8464          * The keystroke filter mask to be applied on email input
8465          * @type RegExp
8466          */
8467         'emailMask' : /[a-z0-9_\.\-@]/i,
8468
8469         /**
8470          * The function used to validate URLs
8471          * @param {String} value The URL
8472          */
8473         'url' : function(v){
8474             return url.test(v);
8475         },
8476         /**
8477          * The error text to display when the url validation function returns false
8478          * @type String
8479          */
8480         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8481         
8482         /**
8483          * The function used to validate alpha values
8484          * @param {String} value The value
8485          */
8486         'alpha' : function(v){
8487             return alpha.test(v);
8488         },
8489         /**
8490          * The error text to display when the alpha validation function returns false
8491          * @type String
8492          */
8493         'alphaText' : 'This field should only contain letters and _',
8494         /**
8495          * The keystroke filter mask to be applied on alpha input
8496          * @type RegExp
8497          */
8498         'alphaMask' : /[a-z_]/i,
8499
8500         /**
8501          * The function used to validate alphanumeric values
8502          * @param {String} value The value
8503          */
8504         'alphanum' : function(v){
8505             return alphanum.test(v);
8506         },
8507         /**
8508          * The error text to display when the alphanumeric validation function returns false
8509          * @type String
8510          */
8511         'alphanumText' : 'This field should only contain letters, numbers and _',
8512         /**
8513          * The keystroke filter mask to be applied on alphanumeric input
8514          * @type RegExp
8515          */
8516         'alphanumMask' : /[a-z0-9_]/i
8517     };
8518 }();/*
8519  * - LGPL
8520  *
8521  * Input
8522  * 
8523  */
8524
8525 /**
8526  * @class Roo.bootstrap.Input
8527  * @extends Roo.bootstrap.Component
8528  * Bootstrap Input class
8529  * @cfg {Boolean} disabled is it disabled
8530  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8531  * @cfg {String} name name of the input
8532  * @cfg {string} fieldLabel - the label associated
8533  * @cfg {string} placeholder - placeholder to put in text.
8534  * @cfg {string}  before - input group add on before
8535  * @cfg {string} after - input group add on after
8536  * @cfg {string} size - (lg|sm) or leave empty..
8537  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8538  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8539  * @cfg {Number} md colspan out of 12 for computer-sized screens
8540  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8541  * @cfg {string} value default value of the input
8542  * @cfg {Number} labelWidth set the width of label 
8543  * @cfg {Number} labellg set the width of label (1-12)
8544  * @cfg {Number} labelmd set the width of label (1-12)
8545  * @cfg {Number} labelsm set the width of label (1-12)
8546  * @cfg {Number} labelxs set the width of label (1-12)
8547  * @cfg {String} labelAlign (top|left)
8548  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8549  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8550  * @cfg {String} indicatorpos (left|right) default left
8551  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8552
8553  * @cfg {String} align (left|center|right) Default left
8554  * @cfg {Boolean} forceFeedback (true|false) Default false
8555  * 
8556  * @constructor
8557  * Create a new Input
8558  * @param {Object} config The config object
8559  */
8560
8561 Roo.bootstrap.Input = function(config){
8562     
8563     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8564     
8565     this.addEvents({
8566         /**
8567          * @event focus
8568          * Fires when this field receives input focus.
8569          * @param {Roo.form.Field} this
8570          */
8571         focus : true,
8572         /**
8573          * @event blur
8574          * Fires when this field loses input focus.
8575          * @param {Roo.form.Field} this
8576          */
8577         blur : true,
8578         /**
8579          * @event specialkey
8580          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8581          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8582          * @param {Roo.form.Field} this
8583          * @param {Roo.EventObject} e The event object
8584          */
8585         specialkey : true,
8586         /**
8587          * @event change
8588          * Fires just before the field blurs if the field value has changed.
8589          * @param {Roo.form.Field} this
8590          * @param {Mixed} newValue The new value
8591          * @param {Mixed} oldValue The original value
8592          */
8593         change : true,
8594         /**
8595          * @event invalid
8596          * Fires after the field has been marked as invalid.
8597          * @param {Roo.form.Field} this
8598          * @param {String} msg The validation message
8599          */
8600         invalid : true,
8601         /**
8602          * @event valid
8603          * Fires after the field has been validated with no errors.
8604          * @param {Roo.form.Field} this
8605          */
8606         valid : true,
8607          /**
8608          * @event keyup
8609          * Fires after the key up
8610          * @param {Roo.form.Field} this
8611          * @param {Roo.EventObject}  e The event Object
8612          */
8613         keyup : true
8614     });
8615 };
8616
8617 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8618      /**
8619      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8620       automatic validation (defaults to "keyup").
8621      */
8622     validationEvent : "keyup",
8623      /**
8624      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8625      */
8626     validateOnBlur : true,
8627     /**
8628      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8629      */
8630     validationDelay : 250,
8631      /**
8632      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8633      */
8634     focusClass : "x-form-focus",  // not needed???
8635     
8636        
8637     /**
8638      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8639      */
8640     invalidClass : "has-warning",
8641     
8642     /**
8643      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8644      */
8645     validClass : "has-success",
8646     
8647     /**
8648      * @cfg {Boolean} hasFeedback (true|false) default true
8649      */
8650     hasFeedback : true,
8651     
8652     /**
8653      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8654      */
8655     invalidFeedbackClass : "glyphicon-warning-sign",
8656     
8657     /**
8658      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8659      */
8660     validFeedbackClass : "glyphicon-ok",
8661     
8662     /**
8663      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8664      */
8665     selectOnFocus : false,
8666     
8667      /**
8668      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8669      */
8670     maskRe : null,
8671        /**
8672      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8673      */
8674     vtype : null,
8675     
8676       /**
8677      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8678      */
8679     disableKeyFilter : false,
8680     
8681        /**
8682      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8683      */
8684     disabled : false,
8685      /**
8686      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8687      */
8688     allowBlank : true,
8689     /**
8690      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8691      */
8692     blankText : "Please complete this mandatory field",
8693     
8694      /**
8695      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8696      */
8697     minLength : 0,
8698     /**
8699      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8700      */
8701     maxLength : Number.MAX_VALUE,
8702     /**
8703      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8704      */
8705     minLengthText : "The minimum length for this field is {0}",
8706     /**
8707      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8708      */
8709     maxLengthText : "The maximum length for this field is {0}",
8710   
8711     
8712     /**
8713      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8714      * If available, this function will be called only after the basic validators all return true, and will be passed the
8715      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8716      */
8717     validator : null,
8718     /**
8719      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8720      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8721      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8722      */
8723     regex : null,
8724     /**
8725      * @cfg {String} regexText -- Depricated - use Invalid Text
8726      */
8727     regexText : "",
8728     
8729     /**
8730      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8731      */
8732     invalidText : "",
8733     
8734     
8735     
8736     autocomplete: false,
8737     
8738     
8739     fieldLabel : '',
8740     inputType : 'text',
8741     
8742     name : false,
8743     placeholder: false,
8744     before : false,
8745     after : false,
8746     size : false,
8747     hasFocus : false,
8748     preventMark: false,
8749     isFormField : true,
8750     value : '',
8751     labelWidth : 2,
8752     labelAlign : false,
8753     readOnly : false,
8754     align : false,
8755     formatedValue : false,
8756     forceFeedback : false,
8757     
8758     indicatorpos : 'left',
8759     
8760     labellg : 0,
8761     labelmd : 0,
8762     labelsm : 0,
8763     labelxs : 0,
8764     
8765     capture : '',
8766     
8767     parentLabelAlign : function()
8768     {
8769         var parent = this;
8770         while (parent.parent()) {
8771             parent = parent.parent();
8772             if (typeof(parent.labelAlign) !='undefined') {
8773                 return parent.labelAlign;
8774             }
8775         }
8776         return 'left';
8777         
8778     },
8779     
8780     getAutoCreate : function()
8781     {
8782         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8783         
8784         var id = Roo.id();
8785         
8786         var cfg = {};
8787         
8788         if(this.inputType != 'hidden'){
8789             cfg.cls = 'form-group' //input-group
8790         }
8791         
8792         var input =  {
8793             tag: 'input',
8794             id : id,
8795             type : this.inputType,
8796             value : this.value,
8797             cls : 'form-control',
8798             placeholder : this.placeholder || '',
8799             autocomplete : this.autocomplete || 'new-password'
8800         };
8801         
8802         if(this.capture.length){
8803             input.capture = this.capture;
8804         }
8805         
8806         if(this.align){
8807             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8808         }
8809         
8810         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8811             input.maxLength = this.maxLength;
8812         }
8813         
8814         if (this.disabled) {
8815             input.disabled=true;
8816         }
8817         
8818         if (this.readOnly) {
8819             input.readonly=true;
8820         }
8821         
8822         if (this.name) {
8823             input.name = this.name;
8824         }
8825         
8826         if (this.size) {
8827             input.cls += ' input-' + this.size;
8828         }
8829         
8830         var settings=this;
8831         ['xs','sm','md','lg'].map(function(size){
8832             if (settings[size]) {
8833                 cfg.cls += ' col-' + size + '-' + settings[size];
8834             }
8835         });
8836         
8837         var inputblock = input;
8838         
8839         var feedback = {
8840             tag: 'span',
8841             cls: 'glyphicon form-control-feedback'
8842         };
8843             
8844         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8845             
8846             inputblock = {
8847                 cls : 'has-feedback',
8848                 cn :  [
8849                     input,
8850                     feedback
8851                 ] 
8852             };  
8853         }
8854         
8855         if (this.before || this.after) {
8856             
8857             inputblock = {
8858                 cls : 'input-group',
8859                 cn :  [] 
8860             };
8861             
8862             if (this.before && typeof(this.before) == 'string') {
8863                 
8864                 inputblock.cn.push({
8865                     tag :'span',
8866                     cls : 'roo-input-before input-group-addon',
8867                     html : this.before
8868                 });
8869             }
8870             if (this.before && typeof(this.before) == 'object') {
8871                 this.before = Roo.factory(this.before);
8872                 
8873                 inputblock.cn.push({
8874                     tag :'span',
8875                     cls : 'roo-input-before input-group-' +
8876                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8877                 });
8878             }
8879             
8880             inputblock.cn.push(input);
8881             
8882             if (this.after && typeof(this.after) == 'string') {
8883                 inputblock.cn.push({
8884                     tag :'span',
8885                     cls : 'roo-input-after input-group-addon',
8886                     html : this.after
8887                 });
8888             }
8889             if (this.after && typeof(this.after) == 'object') {
8890                 this.after = Roo.factory(this.after);
8891                 
8892                 inputblock.cn.push({
8893                     tag :'span',
8894                     cls : 'roo-input-after input-group-' +
8895                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8896                 });
8897             }
8898             
8899             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8900                 inputblock.cls += ' has-feedback';
8901                 inputblock.cn.push(feedback);
8902             }
8903         };
8904         
8905         if (align ==='left' && this.fieldLabel.length) {
8906             
8907             cfg.cls += ' roo-form-group-label-left';
8908             
8909             cfg.cn = [
8910                 {
8911                     tag : 'i',
8912                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8913                     tooltip : 'This field is required'
8914                 },
8915                 {
8916                     tag: 'label',
8917                     'for' :  id,
8918                     cls : 'control-label',
8919                     html : this.fieldLabel
8920
8921                 },
8922                 {
8923                     cls : "", 
8924                     cn: [
8925                         inputblock
8926                     ]
8927                 }
8928             ];
8929             
8930             var labelCfg = cfg.cn[1];
8931             var contentCfg = cfg.cn[2];
8932             
8933             if(this.indicatorpos == 'right'){
8934                 cfg.cn = [
8935                     {
8936                         tag: 'label',
8937                         'for' :  id,
8938                         cls : 'control-label',
8939                         cn : [
8940                             {
8941                                 tag : 'span',
8942                                 html : this.fieldLabel
8943                             },
8944                             {
8945                                 tag : 'i',
8946                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8947                                 tooltip : 'This field is required'
8948                             }
8949                         ]
8950                     },
8951                     {
8952                         cls : "",
8953                         cn: [
8954                             inputblock
8955                         ]
8956                     }
8957
8958                 ];
8959                 
8960                 labelCfg = cfg.cn[0];
8961                 contentCfg = cfg.cn[1];
8962             
8963             }
8964             
8965             if(this.labelWidth > 12){
8966                 labelCfg.style = "width: " + this.labelWidth + 'px';
8967             }
8968             
8969             if(this.labelWidth < 13 && this.labelmd == 0){
8970                 this.labelmd = this.labelWidth;
8971             }
8972             
8973             if(this.labellg > 0){
8974                 labelCfg.cls += ' col-lg-' + this.labellg;
8975                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8976             }
8977             
8978             if(this.labelmd > 0){
8979                 labelCfg.cls += ' col-md-' + this.labelmd;
8980                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8981             }
8982             
8983             if(this.labelsm > 0){
8984                 labelCfg.cls += ' col-sm-' + this.labelsm;
8985                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8986             }
8987             
8988             if(this.labelxs > 0){
8989                 labelCfg.cls += ' col-xs-' + this.labelxs;
8990                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8991             }
8992             
8993             
8994         } else if ( this.fieldLabel.length) {
8995                 
8996             cfg.cn = [
8997                 {
8998                     tag : 'i',
8999                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9000                     tooltip : 'This field is required'
9001                 },
9002                 {
9003                     tag: 'label',
9004                    //cls : 'input-group-addon',
9005                     html : this.fieldLabel
9006
9007                 },
9008
9009                inputblock
9010
9011            ];
9012            
9013            if(this.indicatorpos == 'right'){
9014                 
9015                 cfg.cn = [
9016                     {
9017                         tag: 'label',
9018                        //cls : 'input-group-addon',
9019                         html : this.fieldLabel
9020
9021                     },
9022                     {
9023                         tag : 'i',
9024                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9025                         tooltip : 'This field is required'
9026                     },
9027
9028                    inputblock
9029
9030                ];
9031
9032             }
9033
9034         } else {
9035             
9036             cfg.cn = [
9037
9038                     inputblock
9039
9040             ];
9041                 
9042                 
9043         };
9044         
9045         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9046            cfg.cls += ' navbar-form';
9047         }
9048         
9049         if (this.parentType === 'NavGroup') {
9050            cfg.cls += ' navbar-form';
9051            cfg.tag = 'li';
9052         }
9053         
9054         return cfg;
9055         
9056     },
9057     /**
9058      * return the real input element.
9059      */
9060     inputEl: function ()
9061     {
9062         return this.el.select('input.form-control',true).first();
9063     },
9064     
9065     tooltipEl : function()
9066     {
9067         return this.inputEl();
9068     },
9069     
9070     indicatorEl : function()
9071     {
9072         var indicator = this.el.select('i.roo-required-indicator',true).first();
9073         
9074         if(!indicator){
9075             return false;
9076         }
9077         
9078         return indicator;
9079         
9080     },
9081     
9082     setDisabled : function(v)
9083     {
9084         var i  = this.inputEl().dom;
9085         if (!v) {
9086             i.removeAttribute('disabled');
9087             return;
9088             
9089         }
9090         i.setAttribute('disabled','true');
9091     },
9092     initEvents : function()
9093     {
9094           
9095         this.inputEl().on("keydown" , this.fireKey,  this);
9096         this.inputEl().on("focus", this.onFocus,  this);
9097         this.inputEl().on("blur", this.onBlur,  this);
9098         
9099         this.inputEl().relayEvent('keyup', this);
9100         
9101         this.indicator = this.indicatorEl();
9102         
9103         if(this.indicator){
9104             this.indicator.addClass('invisible');
9105         }
9106  
9107         // reference to original value for reset
9108         this.originalValue = this.getValue();
9109         //Roo.form.TextField.superclass.initEvents.call(this);
9110         if(this.validationEvent == 'keyup'){
9111             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9112             this.inputEl().on('keyup', this.filterValidation, this);
9113         }
9114         else if(this.validationEvent !== false){
9115             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9116         }
9117         
9118         if(this.selectOnFocus){
9119             this.on("focus", this.preFocus, this);
9120             
9121         }
9122         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9123             this.inputEl().on("keypress", this.filterKeys, this);
9124         } else {
9125             this.inputEl().relayEvent('keypress', this);
9126         }
9127        /* if(this.grow){
9128             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9129             this.el.on("click", this.autoSize,  this);
9130         }
9131         */
9132         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9133             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9134         }
9135         
9136         if (typeof(this.before) == 'object') {
9137             this.before.render(this.el.select('.roo-input-before',true).first());
9138         }
9139         if (typeof(this.after) == 'object') {
9140             this.after.render(this.el.select('.roo-input-after',true).first());
9141         }
9142         
9143         this.inputEl().on('change', this.onChange, this);
9144         
9145     },
9146     filterValidation : function(e){
9147         if(!e.isNavKeyPress()){
9148             this.validationTask.delay(this.validationDelay);
9149         }
9150     },
9151      /**
9152      * Validates the field value
9153      * @return {Boolean} True if the value is valid, else false
9154      */
9155     validate : function(){
9156         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9157         if(this.disabled || this.validateValue(this.getRawValue())){
9158             this.markValid();
9159             return true;
9160         }
9161         
9162         this.markInvalid();
9163         return false;
9164     },
9165     
9166     
9167     /**
9168      * Validates a value according to the field's validation rules and marks the field as invalid
9169      * if the validation fails
9170      * @param {Mixed} value The value to validate
9171      * @return {Boolean} True if the value is valid, else false
9172      */
9173     validateValue : function(value)
9174     {
9175         if(this.getVisibilityEl().hasClass('hidden')){
9176             return true;
9177         }
9178         
9179         if(value.length < 1)  { // if it's blank
9180             if(this.allowBlank){
9181                 return true;
9182             }
9183             return false;
9184         }
9185         
9186         if(value.length < this.minLength){
9187             return false;
9188         }
9189         if(value.length > this.maxLength){
9190             return false;
9191         }
9192         if(this.vtype){
9193             var vt = Roo.form.VTypes;
9194             if(!vt[this.vtype](value, this)){
9195                 return false;
9196             }
9197         }
9198         if(typeof this.validator == "function"){
9199             var msg = this.validator(value);
9200             if(msg !== true){
9201                 return false;
9202             }
9203             if (typeof(msg) == 'string') {
9204                 this.invalidText = msg;
9205             }
9206         }
9207         
9208         if(this.regex && !this.regex.test(value)){
9209             return false;
9210         }
9211         
9212         return true;
9213     },
9214     
9215      // private
9216     fireKey : function(e){
9217         //Roo.log('field ' + e.getKey());
9218         if(e.isNavKeyPress()){
9219             this.fireEvent("specialkey", this, e);
9220         }
9221     },
9222     focus : function (selectText){
9223         if(this.rendered){
9224             this.inputEl().focus();
9225             if(selectText === true){
9226                 this.inputEl().dom.select();
9227             }
9228         }
9229         return this;
9230     } ,
9231     
9232     onFocus : function(){
9233         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9234            // this.el.addClass(this.focusClass);
9235         }
9236         if(!this.hasFocus){
9237             this.hasFocus = true;
9238             this.startValue = this.getValue();
9239             this.fireEvent("focus", this);
9240         }
9241     },
9242     
9243     beforeBlur : Roo.emptyFn,
9244
9245     
9246     // private
9247     onBlur : function(){
9248         this.beforeBlur();
9249         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9250             //this.el.removeClass(this.focusClass);
9251         }
9252         this.hasFocus = false;
9253         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9254             this.validate();
9255         }
9256         var v = this.getValue();
9257         if(String(v) !== String(this.startValue)){
9258             this.fireEvent('change', this, v, this.startValue);
9259         }
9260         this.fireEvent("blur", this);
9261     },
9262     
9263     onChange : function(e)
9264     {
9265         var v = this.getValue();
9266         if(String(v) !== String(this.startValue)){
9267             this.fireEvent('change', this, v, this.startValue);
9268         }
9269         
9270     },
9271     
9272     /**
9273      * Resets the current field value to the originally loaded value and clears any validation messages
9274      */
9275     reset : function(){
9276         this.setValue(this.originalValue);
9277         this.validate();
9278     },
9279      /**
9280      * Returns the name of the field
9281      * @return {Mixed} name The name field
9282      */
9283     getName: function(){
9284         return this.name;
9285     },
9286      /**
9287      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9288      * @return {Mixed} value The field value
9289      */
9290     getValue : function(){
9291         
9292         var v = this.inputEl().getValue();
9293         
9294         return v;
9295     },
9296     /**
9297      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9298      * @return {Mixed} value The field value
9299      */
9300     getRawValue : function(){
9301         var v = this.inputEl().getValue();
9302         
9303         return v;
9304     },
9305     
9306     /**
9307      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9308      * @param {Mixed} value The value to set
9309      */
9310     setRawValue : function(v){
9311         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9312     },
9313     
9314     selectText : function(start, end){
9315         var v = this.getRawValue();
9316         if(v.length > 0){
9317             start = start === undefined ? 0 : start;
9318             end = end === undefined ? v.length : end;
9319             var d = this.inputEl().dom;
9320             if(d.setSelectionRange){
9321                 d.setSelectionRange(start, end);
9322             }else if(d.createTextRange){
9323                 var range = d.createTextRange();
9324                 range.moveStart("character", start);
9325                 range.moveEnd("character", v.length-end);
9326                 range.select();
9327             }
9328         }
9329     },
9330     
9331     /**
9332      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9333      * @param {Mixed} value The value to set
9334      */
9335     setValue : function(v){
9336         this.value = v;
9337         if(this.rendered){
9338             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9339             this.validate();
9340         }
9341     },
9342     
9343     /*
9344     processValue : function(value){
9345         if(this.stripCharsRe){
9346             var newValue = value.replace(this.stripCharsRe, '');
9347             if(newValue !== value){
9348                 this.setRawValue(newValue);
9349                 return newValue;
9350             }
9351         }
9352         return value;
9353     },
9354   */
9355     preFocus : function(){
9356         
9357         if(this.selectOnFocus){
9358             this.inputEl().dom.select();
9359         }
9360     },
9361     filterKeys : function(e){
9362         var k = e.getKey();
9363         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9364             return;
9365         }
9366         var c = e.getCharCode(), cc = String.fromCharCode(c);
9367         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9368             return;
9369         }
9370         if(!this.maskRe.test(cc)){
9371             e.stopEvent();
9372         }
9373     },
9374      /**
9375      * Clear any invalid styles/messages for this field
9376      */
9377     clearInvalid : function(){
9378         
9379         if(!this.el || this.preventMark){ // not rendered
9380             return;
9381         }
9382         
9383      
9384         this.el.removeClass(this.invalidClass);
9385         
9386         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9387             
9388             var feedback = this.el.select('.form-control-feedback', true).first();
9389             
9390             if(feedback){
9391                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9392             }
9393             
9394         }
9395         
9396         this.fireEvent('valid', this);
9397     },
9398     
9399      /**
9400      * Mark this field as valid
9401      */
9402     markValid : function()
9403     {
9404         if(!this.el  || this.preventMark){ // not rendered...
9405             return;
9406         }
9407         
9408         this.el.removeClass([this.invalidClass, this.validClass]);
9409         
9410         var feedback = this.el.select('.form-control-feedback', true).first();
9411             
9412         if(feedback){
9413             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9414         }
9415         
9416         if(this.indicator){
9417             this.indicator.removeClass('visible');
9418             this.indicator.addClass('invisible');
9419         }
9420         
9421         if(this.disabled){
9422             return;
9423         }
9424         
9425         if(this.allowBlank && !this.getRawValue().length){
9426             return;
9427         }
9428         
9429         this.el.addClass(this.validClass);
9430         
9431         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9432             
9433             var feedback = this.el.select('.form-control-feedback', true).first();
9434             
9435             if(feedback){
9436                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9437                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9438             }
9439             
9440         }
9441         
9442         this.fireEvent('valid', this);
9443     },
9444     
9445      /**
9446      * Mark this field as invalid
9447      * @param {String} msg The validation message
9448      */
9449     markInvalid : function(msg)
9450     {
9451         if(!this.el  || this.preventMark){ // not rendered
9452             return;
9453         }
9454         
9455         this.el.removeClass([this.invalidClass, this.validClass]);
9456         
9457         var feedback = this.el.select('.form-control-feedback', true).first();
9458             
9459         if(feedback){
9460             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9461         }
9462
9463         if(this.disabled){
9464             return;
9465         }
9466         
9467         if(this.allowBlank && !this.getRawValue().length){
9468             return;
9469         }
9470         
9471         if(this.indicator){
9472             this.indicator.removeClass('invisible');
9473             this.indicator.addClass('visible');
9474         }
9475         
9476         this.el.addClass(this.invalidClass);
9477         
9478         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9479             
9480             var feedback = this.el.select('.form-control-feedback', true).first();
9481             
9482             if(feedback){
9483                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9484                 
9485                 if(this.getValue().length || this.forceFeedback){
9486                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9487                 }
9488                 
9489             }
9490             
9491         }
9492         
9493         this.fireEvent('invalid', this, msg);
9494     },
9495     // private
9496     SafariOnKeyDown : function(event)
9497     {
9498         // this is a workaround for a password hang bug on chrome/ webkit.
9499         if (this.inputEl().dom.type != 'password') {
9500             return;
9501         }
9502         
9503         var isSelectAll = false;
9504         
9505         if(this.inputEl().dom.selectionEnd > 0){
9506             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9507         }
9508         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9509             event.preventDefault();
9510             this.setValue('');
9511             return;
9512         }
9513         
9514         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9515             
9516             event.preventDefault();
9517             // this is very hacky as keydown always get's upper case.
9518             //
9519             var cc = String.fromCharCode(event.getCharCode());
9520             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9521             
9522         }
9523     },
9524     adjustWidth : function(tag, w){
9525         tag = tag.toLowerCase();
9526         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9527             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9528                 if(tag == 'input'){
9529                     return w + 2;
9530                 }
9531                 if(tag == 'textarea'){
9532                     return w-2;
9533                 }
9534             }else if(Roo.isOpera){
9535                 if(tag == 'input'){
9536                     return w + 2;
9537                 }
9538                 if(tag == 'textarea'){
9539                     return w-2;
9540                 }
9541             }
9542         }
9543         return w;
9544     },
9545     
9546     setFieldLabel : function(v)
9547     {
9548         if(!this.rendered){
9549             return;
9550         }
9551         
9552         if(this.indicator){
9553             var ar = this.el.select('label > span',true);
9554             
9555             if (ar.elements.length) {
9556                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9557                 this.fieldLabel = v;
9558                 return;
9559             }
9560             
9561             var br = this.el.select('label',true);
9562             
9563             if(br.elements.length) {
9564                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9565                 this.fieldLabel = v;
9566                 return;
9567             }
9568             
9569             Roo.log('Cannot Found any of label > span || label in input');
9570             return;
9571         }
9572         
9573         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9574         this.fieldLabel = v;
9575         
9576         
9577     }
9578 });
9579
9580  
9581 /*
9582  * - LGPL
9583  *
9584  * Input
9585  * 
9586  */
9587
9588 /**
9589  * @class Roo.bootstrap.TextArea
9590  * @extends Roo.bootstrap.Input
9591  * Bootstrap TextArea class
9592  * @cfg {Number} cols Specifies the visible width of a text area
9593  * @cfg {Number} rows Specifies the visible number of lines in a text area
9594  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9595  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9596  * @cfg {string} html text
9597  * 
9598  * @constructor
9599  * Create a new TextArea
9600  * @param {Object} config The config object
9601  */
9602
9603 Roo.bootstrap.TextArea = function(config){
9604     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9605    
9606 };
9607
9608 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9609      
9610     cols : false,
9611     rows : 5,
9612     readOnly : false,
9613     warp : 'soft',
9614     resize : false,
9615     value: false,
9616     html: false,
9617     
9618     getAutoCreate : function(){
9619         
9620         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9621         
9622         var id = Roo.id();
9623         
9624         var cfg = {};
9625         
9626         if(this.inputType != 'hidden'){
9627             cfg.cls = 'form-group' //input-group
9628         }
9629         
9630         var input =  {
9631             tag: 'textarea',
9632             id : id,
9633             warp : this.warp,
9634             rows : this.rows,
9635             value : this.value || '',
9636             html: this.html || '',
9637             cls : 'form-control',
9638             placeholder : this.placeholder || '' 
9639             
9640         };
9641         
9642         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9643             input.maxLength = this.maxLength;
9644         }
9645         
9646         if(this.resize){
9647             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9648         }
9649         
9650         if(this.cols){
9651             input.cols = this.cols;
9652         }
9653         
9654         if (this.readOnly) {
9655             input.readonly = true;
9656         }
9657         
9658         if (this.name) {
9659             input.name = this.name;
9660         }
9661         
9662         if (this.size) {
9663             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9664         }
9665         
9666         var settings=this;
9667         ['xs','sm','md','lg'].map(function(size){
9668             if (settings[size]) {
9669                 cfg.cls += ' col-' + size + '-' + settings[size];
9670             }
9671         });
9672         
9673         var inputblock = input;
9674         
9675         if(this.hasFeedback && !this.allowBlank){
9676             
9677             var feedback = {
9678                 tag: 'span',
9679                 cls: 'glyphicon form-control-feedback'
9680             };
9681
9682             inputblock = {
9683                 cls : 'has-feedback',
9684                 cn :  [
9685                     input,
9686                     feedback
9687                 ] 
9688             };  
9689         }
9690         
9691         
9692         if (this.before || this.after) {
9693             
9694             inputblock = {
9695                 cls : 'input-group',
9696                 cn :  [] 
9697             };
9698             if (this.before) {
9699                 inputblock.cn.push({
9700                     tag :'span',
9701                     cls : 'input-group-addon',
9702                     html : this.before
9703                 });
9704             }
9705             
9706             inputblock.cn.push(input);
9707             
9708             if(this.hasFeedback && !this.allowBlank){
9709                 inputblock.cls += ' has-feedback';
9710                 inputblock.cn.push(feedback);
9711             }
9712             
9713             if (this.after) {
9714                 inputblock.cn.push({
9715                     tag :'span',
9716                     cls : 'input-group-addon',
9717                     html : this.after
9718                 });
9719             }
9720             
9721         }
9722         
9723         if (align ==='left' && this.fieldLabel.length) {
9724             cfg.cn = [
9725                 {
9726                     tag: 'label',
9727                     'for' :  id,
9728                     cls : 'control-label',
9729                     html : this.fieldLabel
9730                 },
9731                 {
9732                     cls : "",
9733                     cn: [
9734                         inputblock
9735                     ]
9736                 }
9737
9738             ];
9739             
9740             if(this.labelWidth > 12){
9741                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9742             }
9743
9744             if(this.labelWidth < 13 && this.labelmd == 0){
9745                 this.labelmd = this.labelWidth;
9746             }
9747
9748             if(this.labellg > 0){
9749                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9750                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9751             }
9752
9753             if(this.labelmd > 0){
9754                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9755                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9756             }
9757
9758             if(this.labelsm > 0){
9759                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9760                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9761             }
9762
9763             if(this.labelxs > 0){
9764                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9765                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9766             }
9767             
9768         } else if ( this.fieldLabel.length) {
9769             cfg.cn = [
9770
9771                {
9772                    tag: 'label',
9773                    //cls : 'input-group-addon',
9774                    html : this.fieldLabel
9775
9776                },
9777
9778                inputblock
9779
9780            ];
9781
9782         } else {
9783
9784             cfg.cn = [
9785
9786                 inputblock
9787
9788             ];
9789                 
9790         }
9791         
9792         if (this.disabled) {
9793             input.disabled=true;
9794         }
9795         
9796         return cfg;
9797         
9798     },
9799     /**
9800      * return the real textarea element.
9801      */
9802     inputEl: function ()
9803     {
9804         return this.el.select('textarea.form-control',true).first();
9805     },
9806     
9807     /**
9808      * Clear any invalid styles/messages for this field
9809      */
9810     clearInvalid : function()
9811     {
9812         
9813         if(!this.el || this.preventMark){ // not rendered
9814             return;
9815         }
9816         
9817         var label = this.el.select('label', true).first();
9818         var icon = this.el.select('i.fa-star', true).first();
9819         
9820         if(label && icon){
9821             icon.remove();
9822         }
9823         
9824         this.el.removeClass(this.invalidClass);
9825         
9826         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9827             
9828             var feedback = this.el.select('.form-control-feedback', true).first();
9829             
9830             if(feedback){
9831                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9832             }
9833             
9834         }
9835         
9836         this.fireEvent('valid', this);
9837     },
9838     
9839      /**
9840      * Mark this field as valid
9841      */
9842     markValid : function()
9843     {
9844         if(!this.el  || this.preventMark){ // not rendered
9845             return;
9846         }
9847         
9848         this.el.removeClass([this.invalidClass, this.validClass]);
9849         
9850         var feedback = this.el.select('.form-control-feedback', true).first();
9851             
9852         if(feedback){
9853             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9854         }
9855
9856         if(this.disabled || this.allowBlank){
9857             return;
9858         }
9859         
9860         var label = this.el.select('label', true).first();
9861         var icon = this.el.select('i.fa-star', true).first();
9862         
9863         if(label && icon){
9864             icon.remove();
9865         }
9866         
9867         this.el.addClass(this.validClass);
9868         
9869         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9870             
9871             var feedback = this.el.select('.form-control-feedback', true).first();
9872             
9873             if(feedback){
9874                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9875                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9876             }
9877             
9878         }
9879         
9880         this.fireEvent('valid', this);
9881     },
9882     
9883      /**
9884      * Mark this field as invalid
9885      * @param {String} msg The validation message
9886      */
9887     markInvalid : function(msg)
9888     {
9889         if(!this.el  || this.preventMark){ // not rendered
9890             return;
9891         }
9892         
9893         this.el.removeClass([this.invalidClass, this.validClass]);
9894         
9895         var feedback = this.el.select('.form-control-feedback', true).first();
9896             
9897         if(feedback){
9898             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9899         }
9900
9901         if(this.disabled || this.allowBlank){
9902             return;
9903         }
9904         
9905         var label = this.el.select('label', true).first();
9906         var icon = this.el.select('i.fa-star', true).first();
9907         
9908         if(!this.getValue().length && label && !icon){
9909             this.el.createChild({
9910                 tag : 'i',
9911                 cls : 'text-danger fa fa-lg fa-star',
9912                 tooltip : 'This field is required',
9913                 style : 'margin-right:5px;'
9914             }, label, true);
9915         }
9916
9917         this.el.addClass(this.invalidClass);
9918         
9919         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9920             
9921             var feedback = this.el.select('.form-control-feedback', true).first();
9922             
9923             if(feedback){
9924                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9925                 
9926                 if(this.getValue().length || this.forceFeedback){
9927                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9928                 }
9929                 
9930             }
9931             
9932         }
9933         
9934         this.fireEvent('invalid', this, msg);
9935     }
9936 });
9937
9938  
9939 /*
9940  * - LGPL
9941  *
9942  * trigger field - base class for combo..
9943  * 
9944  */
9945  
9946 /**
9947  * @class Roo.bootstrap.TriggerField
9948  * @extends Roo.bootstrap.Input
9949  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9950  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9951  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9952  * for which you can provide a custom implementation.  For example:
9953  * <pre><code>
9954 var trigger = new Roo.bootstrap.TriggerField();
9955 trigger.onTriggerClick = myTriggerFn;
9956 trigger.applyTo('my-field');
9957 </code></pre>
9958  *
9959  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9960  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9961  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9962  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9963  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9964
9965  * @constructor
9966  * Create a new TriggerField.
9967  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9968  * to the base TextField)
9969  */
9970 Roo.bootstrap.TriggerField = function(config){
9971     this.mimicing = false;
9972     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9973 };
9974
9975 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9976     /**
9977      * @cfg {String} triggerClass A CSS class to apply to the trigger
9978      */
9979      /**
9980      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9981      */
9982     hideTrigger:false,
9983
9984     /**
9985      * @cfg {Boolean} removable (true|false) special filter default false
9986      */
9987     removable : false,
9988     
9989     /** @cfg {Boolean} grow @hide */
9990     /** @cfg {Number} growMin @hide */
9991     /** @cfg {Number} growMax @hide */
9992
9993     /**
9994      * @hide 
9995      * @method
9996      */
9997     autoSize: Roo.emptyFn,
9998     // private
9999     monitorTab : true,
10000     // private
10001     deferHeight : true,
10002
10003     
10004     actionMode : 'wrap',
10005     
10006     caret : false,
10007     
10008     
10009     getAutoCreate : function(){
10010        
10011         var align = this.labelAlign || this.parentLabelAlign();
10012         
10013         var id = Roo.id();
10014         
10015         var cfg = {
10016             cls: 'form-group' //input-group
10017         };
10018         
10019         
10020         var input =  {
10021             tag: 'input',
10022             id : id,
10023             type : this.inputType,
10024             cls : 'form-control',
10025             autocomplete: 'new-password',
10026             placeholder : this.placeholder || '' 
10027             
10028         };
10029         if (this.name) {
10030             input.name = this.name;
10031         }
10032         if (this.size) {
10033             input.cls += ' input-' + this.size;
10034         }
10035         
10036         if (this.disabled) {
10037             input.disabled=true;
10038         }
10039         
10040         var inputblock = input;
10041         
10042         if(this.hasFeedback && !this.allowBlank){
10043             
10044             var feedback = {
10045                 tag: 'span',
10046                 cls: 'glyphicon form-control-feedback'
10047             };
10048             
10049             if(this.removable && !this.editable && !this.tickable){
10050                 inputblock = {
10051                     cls : 'has-feedback',
10052                     cn :  [
10053                         inputblock,
10054                         {
10055                             tag: 'button',
10056                             html : 'x',
10057                             cls : 'roo-combo-removable-btn close'
10058                         },
10059                         feedback
10060                     ] 
10061                 };
10062             } else {
10063                 inputblock = {
10064                     cls : 'has-feedback',
10065                     cn :  [
10066                         inputblock,
10067                         feedback
10068                     ] 
10069                 };
10070             }
10071
10072         } else {
10073             if(this.removable && !this.editable && !this.tickable){
10074                 inputblock = {
10075                     cls : 'roo-removable',
10076                     cn :  [
10077                         inputblock,
10078                         {
10079                             tag: 'button',
10080                             html : 'x',
10081                             cls : 'roo-combo-removable-btn close'
10082                         }
10083                     ] 
10084                 };
10085             }
10086         }
10087         
10088         if (this.before || this.after) {
10089             
10090             inputblock = {
10091                 cls : 'input-group',
10092                 cn :  [] 
10093             };
10094             if (this.before) {
10095                 inputblock.cn.push({
10096                     tag :'span',
10097                     cls : 'input-group-addon',
10098                     html : this.before
10099                 });
10100             }
10101             
10102             inputblock.cn.push(input);
10103             
10104             if(this.hasFeedback && !this.allowBlank){
10105                 inputblock.cls += ' has-feedback';
10106                 inputblock.cn.push(feedback);
10107             }
10108             
10109             if (this.after) {
10110                 inputblock.cn.push({
10111                     tag :'span',
10112                     cls : 'input-group-addon',
10113                     html : this.after
10114                 });
10115             }
10116             
10117         };
10118         
10119         var box = {
10120             tag: 'div',
10121             cn: [
10122                 {
10123                     tag: 'input',
10124                     type : 'hidden',
10125                     cls: 'form-hidden-field'
10126                 },
10127                 inputblock
10128             ]
10129             
10130         };
10131         
10132         if(this.multiple){
10133             box = {
10134                 tag: 'div',
10135                 cn: [
10136                     {
10137                         tag: 'input',
10138                         type : 'hidden',
10139                         cls: 'form-hidden-field'
10140                     },
10141                     {
10142                         tag: 'ul',
10143                         cls: 'roo-select2-choices',
10144                         cn:[
10145                             {
10146                                 tag: 'li',
10147                                 cls: 'roo-select2-search-field',
10148                                 cn: [
10149
10150                                     inputblock
10151                                 ]
10152                             }
10153                         ]
10154                     }
10155                 ]
10156             }
10157         };
10158         
10159         var combobox = {
10160             cls: 'roo-select2-container input-group',
10161             cn: [
10162                 box
10163 //                {
10164 //                    tag: 'ul',
10165 //                    cls: 'typeahead typeahead-long dropdown-menu',
10166 //                    style: 'display:none'
10167 //                }
10168             ]
10169         };
10170         
10171         if(!this.multiple && this.showToggleBtn){
10172             
10173             var caret = {
10174                         tag: 'span',
10175                         cls: 'caret'
10176              };
10177             if (this.caret != false) {
10178                 caret = {
10179                      tag: 'i',
10180                      cls: 'fa fa-' + this.caret
10181                 };
10182                 
10183             }
10184             
10185             combobox.cn.push({
10186                 tag :'span',
10187                 cls : 'input-group-addon btn dropdown-toggle',
10188                 cn : [
10189                     caret,
10190                     {
10191                         tag: 'span',
10192                         cls: 'combobox-clear',
10193                         cn  : [
10194                             {
10195                                 tag : 'i',
10196                                 cls: 'icon-remove'
10197                             }
10198                         ]
10199                     }
10200                 ]
10201
10202             })
10203         }
10204         
10205         if(this.multiple){
10206             combobox.cls += ' roo-select2-container-multi';
10207         }
10208         
10209         if (align ==='left' && this.fieldLabel.length) {
10210             
10211             cfg.cls += ' roo-form-group-label-left';
10212
10213             cfg.cn = [
10214                 {
10215                     tag : 'i',
10216                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10217                     tooltip : 'This field is required'
10218                 },
10219                 {
10220                     tag: 'label',
10221                     'for' :  id,
10222                     cls : 'control-label',
10223                     html : this.fieldLabel
10224
10225                 },
10226                 {
10227                     cls : "", 
10228                     cn: [
10229                         combobox
10230                     ]
10231                 }
10232
10233             ];
10234             
10235             var labelCfg = cfg.cn[1];
10236             var contentCfg = cfg.cn[2];
10237             
10238             if(this.indicatorpos == 'right'){
10239                 cfg.cn = [
10240                     {
10241                         tag: 'label',
10242                         'for' :  id,
10243                         cls : 'control-label',
10244                         cn : [
10245                             {
10246                                 tag : 'span',
10247                                 html : this.fieldLabel
10248                             },
10249                             {
10250                                 tag : 'i',
10251                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10252                                 tooltip : 'This field is required'
10253                             }
10254                         ]
10255                     },
10256                     {
10257                         cls : "", 
10258                         cn: [
10259                             combobox
10260                         ]
10261                     }
10262
10263                 ];
10264                 
10265                 labelCfg = cfg.cn[0];
10266                 contentCfg = cfg.cn[1];
10267             }
10268             
10269             if(this.labelWidth > 12){
10270                 labelCfg.style = "width: " + this.labelWidth + 'px';
10271             }
10272             
10273             if(this.labelWidth < 13 && this.labelmd == 0){
10274                 this.labelmd = this.labelWidth;
10275             }
10276             
10277             if(this.labellg > 0){
10278                 labelCfg.cls += ' col-lg-' + this.labellg;
10279                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10280             }
10281             
10282             if(this.labelmd > 0){
10283                 labelCfg.cls += ' col-md-' + this.labelmd;
10284                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10285             }
10286             
10287             if(this.labelsm > 0){
10288                 labelCfg.cls += ' col-sm-' + this.labelsm;
10289                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10290             }
10291             
10292             if(this.labelxs > 0){
10293                 labelCfg.cls += ' col-xs-' + this.labelxs;
10294                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10295             }
10296             
10297         } else if ( this.fieldLabel.length) {
10298 //                Roo.log(" label");
10299             cfg.cn = [
10300                 {
10301                    tag : 'i',
10302                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10303                    tooltip : 'This field is required'
10304                },
10305                {
10306                    tag: 'label',
10307                    //cls : 'input-group-addon',
10308                    html : this.fieldLabel
10309
10310                },
10311
10312                combobox
10313
10314             ];
10315             
10316             if(this.indicatorpos == 'right'){
10317                 
10318                 cfg.cn = [
10319                     {
10320                        tag: 'label',
10321                        cn : [
10322                            {
10323                                tag : 'span',
10324                                html : this.fieldLabel
10325                            },
10326                            {
10327                               tag : 'i',
10328                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10329                               tooltip : 'This field is required'
10330                            }
10331                        ]
10332
10333                     },
10334                     combobox
10335
10336                 ];
10337
10338             }
10339
10340         } else {
10341             
10342 //                Roo.log(" no label && no align");
10343                 cfg = combobox
10344                      
10345                 
10346         }
10347         
10348         var settings=this;
10349         ['xs','sm','md','lg'].map(function(size){
10350             if (settings[size]) {
10351                 cfg.cls += ' col-' + size + '-' + settings[size];
10352             }
10353         });
10354         
10355         return cfg;
10356         
10357     },
10358     
10359     
10360     
10361     // private
10362     onResize : function(w, h){
10363 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10364 //        if(typeof w == 'number'){
10365 //            var x = w - this.trigger.getWidth();
10366 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10367 //            this.trigger.setStyle('left', x+'px');
10368 //        }
10369     },
10370
10371     // private
10372     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10373
10374     // private
10375     getResizeEl : function(){
10376         return this.inputEl();
10377     },
10378
10379     // private
10380     getPositionEl : function(){
10381         return this.inputEl();
10382     },
10383
10384     // private
10385     alignErrorIcon : function(){
10386         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10387     },
10388
10389     // private
10390     initEvents : function(){
10391         
10392         this.createList();
10393         
10394         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10395         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10396         if(!this.multiple && this.showToggleBtn){
10397             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10398             if(this.hideTrigger){
10399                 this.trigger.setDisplayed(false);
10400             }
10401             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10402         }
10403         
10404         if(this.multiple){
10405             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10406         }
10407         
10408         if(this.removable && !this.editable && !this.tickable){
10409             var close = this.closeTriggerEl();
10410             
10411             if(close){
10412                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10413                 close.on('click', this.removeBtnClick, this, close);
10414             }
10415         }
10416         
10417         //this.trigger.addClassOnOver('x-form-trigger-over');
10418         //this.trigger.addClassOnClick('x-form-trigger-click');
10419         
10420         //if(!this.width){
10421         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10422         //}
10423     },
10424     
10425     closeTriggerEl : function()
10426     {
10427         var close = this.el.select('.roo-combo-removable-btn', true).first();
10428         return close ? close : false;
10429     },
10430     
10431     removeBtnClick : function(e, h, el)
10432     {
10433         e.preventDefault();
10434         
10435         if(this.fireEvent("remove", this) !== false){
10436             this.reset();
10437             this.fireEvent("afterremove", this)
10438         }
10439     },
10440     
10441     createList : function()
10442     {
10443         this.list = Roo.get(document.body).createChild({
10444             tag: 'ul',
10445             cls: 'typeahead typeahead-long dropdown-menu',
10446             style: 'display:none'
10447         });
10448         
10449         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10450         
10451     },
10452
10453     // private
10454     initTrigger : function(){
10455        
10456     },
10457
10458     // private
10459     onDestroy : function(){
10460         if(this.trigger){
10461             this.trigger.removeAllListeners();
10462           //  this.trigger.remove();
10463         }
10464         //if(this.wrap){
10465         //    this.wrap.remove();
10466         //}
10467         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10468     },
10469
10470     // private
10471     onFocus : function(){
10472         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10473         /*
10474         if(!this.mimicing){
10475             this.wrap.addClass('x-trigger-wrap-focus');
10476             this.mimicing = true;
10477             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10478             if(this.monitorTab){
10479                 this.el.on("keydown", this.checkTab, this);
10480             }
10481         }
10482         */
10483     },
10484
10485     // private
10486     checkTab : function(e){
10487         if(e.getKey() == e.TAB){
10488             this.triggerBlur();
10489         }
10490     },
10491
10492     // private
10493     onBlur : function(){
10494         // do nothing
10495     },
10496
10497     // private
10498     mimicBlur : function(e, t){
10499         /*
10500         if(!this.wrap.contains(t) && this.validateBlur()){
10501             this.triggerBlur();
10502         }
10503         */
10504     },
10505
10506     // private
10507     triggerBlur : function(){
10508         this.mimicing = false;
10509         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10510         if(this.monitorTab){
10511             this.el.un("keydown", this.checkTab, this);
10512         }
10513         //this.wrap.removeClass('x-trigger-wrap-focus');
10514         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10515     },
10516
10517     // private
10518     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10519     validateBlur : function(e, t){
10520         return true;
10521     },
10522
10523     // private
10524     onDisable : function(){
10525         this.inputEl().dom.disabled = true;
10526         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10527         //if(this.wrap){
10528         //    this.wrap.addClass('x-item-disabled');
10529         //}
10530     },
10531
10532     // private
10533     onEnable : function(){
10534         this.inputEl().dom.disabled = false;
10535         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10536         //if(this.wrap){
10537         //    this.el.removeClass('x-item-disabled');
10538         //}
10539     },
10540
10541     // private
10542     onShow : function(){
10543         var ae = this.getActionEl();
10544         
10545         if(ae){
10546             ae.dom.style.display = '';
10547             ae.dom.style.visibility = 'visible';
10548         }
10549     },
10550
10551     // private
10552     
10553     onHide : function(){
10554         var ae = this.getActionEl();
10555         ae.dom.style.display = 'none';
10556     },
10557
10558     /**
10559      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10560      * by an implementing function.
10561      * @method
10562      * @param {EventObject} e
10563      */
10564     onTriggerClick : Roo.emptyFn
10565 });
10566  /*
10567  * Based on:
10568  * Ext JS Library 1.1.1
10569  * Copyright(c) 2006-2007, Ext JS, LLC.
10570  *
10571  * Originally Released Under LGPL - original licence link has changed is not relivant.
10572  *
10573  * Fork - LGPL
10574  * <script type="text/javascript">
10575  */
10576
10577
10578 /**
10579  * @class Roo.data.SortTypes
10580  * @singleton
10581  * Defines the default sorting (casting?) comparison functions used when sorting data.
10582  */
10583 Roo.data.SortTypes = {
10584     /**
10585      * Default sort that does nothing
10586      * @param {Mixed} s The value being converted
10587      * @return {Mixed} The comparison value
10588      */
10589     none : function(s){
10590         return s;
10591     },
10592     
10593     /**
10594      * The regular expression used to strip tags
10595      * @type {RegExp}
10596      * @property
10597      */
10598     stripTagsRE : /<\/?[^>]+>/gi,
10599     
10600     /**
10601      * Strips all HTML tags to sort on text only
10602      * @param {Mixed} s The value being converted
10603      * @return {String} The comparison value
10604      */
10605     asText : function(s){
10606         return String(s).replace(this.stripTagsRE, "");
10607     },
10608     
10609     /**
10610      * Strips all HTML tags to sort on text only - Case insensitive
10611      * @param {Mixed} s The value being converted
10612      * @return {String} The comparison value
10613      */
10614     asUCText : function(s){
10615         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10616     },
10617     
10618     /**
10619      * Case insensitive string
10620      * @param {Mixed} s The value being converted
10621      * @return {String} The comparison value
10622      */
10623     asUCString : function(s) {
10624         return String(s).toUpperCase();
10625     },
10626     
10627     /**
10628      * Date sorting
10629      * @param {Mixed} s The value being converted
10630      * @return {Number} The comparison value
10631      */
10632     asDate : function(s) {
10633         if(!s){
10634             return 0;
10635         }
10636         if(s instanceof Date){
10637             return s.getTime();
10638         }
10639         return Date.parse(String(s));
10640     },
10641     
10642     /**
10643      * Float sorting
10644      * @param {Mixed} s The value being converted
10645      * @return {Float} The comparison value
10646      */
10647     asFloat : function(s) {
10648         var val = parseFloat(String(s).replace(/,/g, ""));
10649         if(isNaN(val)) {
10650             val = 0;
10651         }
10652         return val;
10653     },
10654     
10655     /**
10656      * Integer sorting
10657      * @param {Mixed} s The value being converted
10658      * @return {Number} The comparison value
10659      */
10660     asInt : function(s) {
10661         var val = parseInt(String(s).replace(/,/g, ""));
10662         if(isNaN(val)) {
10663             val = 0;
10664         }
10665         return val;
10666     }
10667 };/*
10668  * Based on:
10669  * Ext JS Library 1.1.1
10670  * Copyright(c) 2006-2007, Ext JS, LLC.
10671  *
10672  * Originally Released Under LGPL - original licence link has changed is not relivant.
10673  *
10674  * Fork - LGPL
10675  * <script type="text/javascript">
10676  */
10677
10678 /**
10679 * @class Roo.data.Record
10680  * Instances of this class encapsulate both record <em>definition</em> information, and record
10681  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10682  * to access Records cached in an {@link Roo.data.Store} object.<br>
10683  * <p>
10684  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10685  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10686  * objects.<br>
10687  * <p>
10688  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10689  * @constructor
10690  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10691  * {@link #create}. The parameters are the same.
10692  * @param {Array} data An associative Array of data values keyed by the field name.
10693  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10694  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10695  * not specified an integer id is generated.
10696  */
10697 Roo.data.Record = function(data, id){
10698     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10699     this.data = data;
10700 };
10701
10702 /**
10703  * Generate a constructor for a specific record layout.
10704  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10705  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10706  * Each field definition object may contain the following properties: <ul>
10707  * <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,
10708  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10709  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10710  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10711  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10712  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10713  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10714  * this may be omitted.</p></li>
10715  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10716  * <ul><li>auto (Default, implies no conversion)</li>
10717  * <li>string</li>
10718  * <li>int</li>
10719  * <li>float</li>
10720  * <li>boolean</li>
10721  * <li>date</li></ul></p></li>
10722  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10723  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10724  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10725  * by the Reader into an object that will be stored in the Record. It is passed the
10726  * following parameters:<ul>
10727  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10728  * </ul></p></li>
10729  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10730  * </ul>
10731  * <br>usage:<br><pre><code>
10732 var TopicRecord = Roo.data.Record.create(
10733     {name: 'title', mapping: 'topic_title'},
10734     {name: 'author', mapping: 'username'},
10735     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10736     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10737     {name: 'lastPoster', mapping: 'user2'},
10738     {name: 'excerpt', mapping: 'post_text'}
10739 );
10740
10741 var myNewRecord = new TopicRecord({
10742     title: 'Do my job please',
10743     author: 'noobie',
10744     totalPosts: 1,
10745     lastPost: new Date(),
10746     lastPoster: 'Animal',
10747     excerpt: 'No way dude!'
10748 });
10749 myStore.add(myNewRecord);
10750 </code></pre>
10751  * @method create
10752  * @static
10753  */
10754 Roo.data.Record.create = function(o){
10755     var f = function(){
10756         f.superclass.constructor.apply(this, arguments);
10757     };
10758     Roo.extend(f, Roo.data.Record);
10759     var p = f.prototype;
10760     p.fields = new Roo.util.MixedCollection(false, function(field){
10761         return field.name;
10762     });
10763     for(var i = 0, len = o.length; i < len; i++){
10764         p.fields.add(new Roo.data.Field(o[i]));
10765     }
10766     f.getField = function(name){
10767         return p.fields.get(name);  
10768     };
10769     return f;
10770 };
10771
10772 Roo.data.Record.AUTO_ID = 1000;
10773 Roo.data.Record.EDIT = 'edit';
10774 Roo.data.Record.REJECT = 'reject';
10775 Roo.data.Record.COMMIT = 'commit';
10776
10777 Roo.data.Record.prototype = {
10778     /**
10779      * Readonly flag - true if this record has been modified.
10780      * @type Boolean
10781      */
10782     dirty : false,
10783     editing : false,
10784     error: null,
10785     modified: null,
10786
10787     // private
10788     join : function(store){
10789         this.store = store;
10790     },
10791
10792     /**
10793      * Set the named field to the specified value.
10794      * @param {String} name The name of the field to set.
10795      * @param {Object} value The value to set the field to.
10796      */
10797     set : function(name, value){
10798         if(this.data[name] == value){
10799             return;
10800         }
10801         this.dirty = true;
10802         if(!this.modified){
10803             this.modified = {};
10804         }
10805         if(typeof this.modified[name] == 'undefined'){
10806             this.modified[name] = this.data[name];
10807         }
10808         this.data[name] = value;
10809         if(!this.editing && this.store){
10810             this.store.afterEdit(this);
10811         }       
10812     },
10813
10814     /**
10815      * Get the value of the named field.
10816      * @param {String} name The name of the field to get the value of.
10817      * @return {Object} The value of the field.
10818      */
10819     get : function(name){
10820         return this.data[name]; 
10821     },
10822
10823     // private
10824     beginEdit : function(){
10825         this.editing = true;
10826         this.modified = {}; 
10827     },
10828
10829     // private
10830     cancelEdit : function(){
10831         this.editing = false;
10832         delete this.modified;
10833     },
10834
10835     // private
10836     endEdit : function(){
10837         this.editing = false;
10838         if(this.dirty && this.store){
10839             this.store.afterEdit(this);
10840         }
10841     },
10842
10843     /**
10844      * Usually called by the {@link Roo.data.Store} which owns the Record.
10845      * Rejects all changes made to the Record since either creation, or the last commit operation.
10846      * Modified fields are reverted to their original values.
10847      * <p>
10848      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10849      * of reject operations.
10850      */
10851     reject : function(){
10852         var m = this.modified;
10853         for(var n in m){
10854             if(typeof m[n] != "function"){
10855                 this.data[n] = m[n];
10856             }
10857         }
10858         this.dirty = false;
10859         delete this.modified;
10860         this.editing = false;
10861         if(this.store){
10862             this.store.afterReject(this);
10863         }
10864     },
10865
10866     /**
10867      * Usually called by the {@link Roo.data.Store} which owns the Record.
10868      * Commits all changes made to the Record since either creation, or the last commit operation.
10869      * <p>
10870      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10871      * of commit operations.
10872      */
10873     commit : function(){
10874         this.dirty = false;
10875         delete this.modified;
10876         this.editing = false;
10877         if(this.store){
10878             this.store.afterCommit(this);
10879         }
10880     },
10881
10882     // private
10883     hasError : function(){
10884         return this.error != null;
10885     },
10886
10887     // private
10888     clearError : function(){
10889         this.error = null;
10890     },
10891
10892     /**
10893      * Creates a copy of this record.
10894      * @param {String} id (optional) A new record id if you don't want to use this record's id
10895      * @return {Record}
10896      */
10897     copy : function(newId) {
10898         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10899     }
10900 };/*
10901  * Based on:
10902  * Ext JS Library 1.1.1
10903  * Copyright(c) 2006-2007, Ext JS, LLC.
10904  *
10905  * Originally Released Under LGPL - original licence link has changed is not relivant.
10906  *
10907  * Fork - LGPL
10908  * <script type="text/javascript">
10909  */
10910
10911
10912
10913 /**
10914  * @class Roo.data.Store
10915  * @extends Roo.util.Observable
10916  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10917  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10918  * <p>
10919  * 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
10920  * has no knowledge of the format of the data returned by the Proxy.<br>
10921  * <p>
10922  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10923  * instances from the data object. These records are cached and made available through accessor functions.
10924  * @constructor
10925  * Creates a new Store.
10926  * @param {Object} config A config object containing the objects needed for the Store to access data,
10927  * and read the data into Records.
10928  */
10929 Roo.data.Store = function(config){
10930     this.data = new Roo.util.MixedCollection(false);
10931     this.data.getKey = function(o){
10932         return o.id;
10933     };
10934     this.baseParams = {};
10935     // private
10936     this.paramNames = {
10937         "start" : "start",
10938         "limit" : "limit",
10939         "sort" : "sort",
10940         "dir" : "dir",
10941         "multisort" : "_multisort"
10942     };
10943
10944     if(config && config.data){
10945         this.inlineData = config.data;
10946         delete config.data;
10947     }
10948
10949     Roo.apply(this, config);
10950     
10951     if(this.reader){ // reader passed
10952         this.reader = Roo.factory(this.reader, Roo.data);
10953         this.reader.xmodule = this.xmodule || false;
10954         if(!this.recordType){
10955             this.recordType = this.reader.recordType;
10956         }
10957         if(this.reader.onMetaChange){
10958             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10959         }
10960     }
10961
10962     if(this.recordType){
10963         this.fields = this.recordType.prototype.fields;
10964     }
10965     this.modified = [];
10966
10967     this.addEvents({
10968         /**
10969          * @event datachanged
10970          * Fires when the data cache has changed, and a widget which is using this Store
10971          * as a Record cache should refresh its view.
10972          * @param {Store} this
10973          */
10974         datachanged : true,
10975         /**
10976          * @event metachange
10977          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10978          * @param {Store} this
10979          * @param {Object} meta The JSON metadata
10980          */
10981         metachange : true,
10982         /**
10983          * @event add
10984          * Fires when Records have been added to the Store
10985          * @param {Store} this
10986          * @param {Roo.data.Record[]} records The array of Records added
10987          * @param {Number} index The index at which the record(s) were added
10988          */
10989         add : true,
10990         /**
10991          * @event remove
10992          * Fires when a Record has been removed from the Store
10993          * @param {Store} this
10994          * @param {Roo.data.Record} record The Record that was removed
10995          * @param {Number} index The index at which the record was removed
10996          */
10997         remove : true,
10998         /**
10999          * @event update
11000          * Fires when a Record has been updated
11001          * @param {Store} this
11002          * @param {Roo.data.Record} record The Record that was updated
11003          * @param {String} operation The update operation being performed.  Value may be one of:
11004          * <pre><code>
11005  Roo.data.Record.EDIT
11006  Roo.data.Record.REJECT
11007  Roo.data.Record.COMMIT
11008          * </code></pre>
11009          */
11010         update : true,
11011         /**
11012          * @event clear
11013          * Fires when the data cache has been cleared.
11014          * @param {Store} this
11015          */
11016         clear : true,
11017         /**
11018          * @event beforeload
11019          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11020          * the load action will be canceled.
11021          * @param {Store} this
11022          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11023          */
11024         beforeload : true,
11025         /**
11026          * @event beforeloadadd
11027          * Fires after a new set of Records has been loaded.
11028          * @param {Store} this
11029          * @param {Roo.data.Record[]} records The Records that were loaded
11030          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11031          */
11032         beforeloadadd : true,
11033         /**
11034          * @event load
11035          * Fires after a new set of Records has been loaded, before they are added to the store.
11036          * @param {Store} this
11037          * @param {Roo.data.Record[]} records The Records that were loaded
11038          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11039          * @params {Object} return from reader
11040          */
11041         load : true,
11042         /**
11043          * @event loadexception
11044          * Fires if an exception occurs in the Proxy during loading.
11045          * Called with the signature of the Proxy's "loadexception" event.
11046          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11047          * 
11048          * @param {Proxy} 
11049          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11050          * @param {Object} load options 
11051          * @param {Object} jsonData from your request (normally this contains the Exception)
11052          */
11053         loadexception : true
11054     });
11055     
11056     if(this.proxy){
11057         this.proxy = Roo.factory(this.proxy, Roo.data);
11058         this.proxy.xmodule = this.xmodule || false;
11059         this.relayEvents(this.proxy,  ["loadexception"]);
11060     }
11061     this.sortToggle = {};
11062     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11063
11064     Roo.data.Store.superclass.constructor.call(this);
11065
11066     if(this.inlineData){
11067         this.loadData(this.inlineData);
11068         delete this.inlineData;
11069     }
11070 };
11071
11072 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11073      /**
11074     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11075     * without a remote query - used by combo/forms at present.
11076     */
11077     
11078     /**
11079     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11080     */
11081     /**
11082     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11083     */
11084     /**
11085     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11086     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11087     */
11088     /**
11089     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11090     * on any HTTP request
11091     */
11092     /**
11093     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11094     */
11095     /**
11096     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11097     */
11098     multiSort: false,
11099     /**
11100     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11101     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11102     */
11103     remoteSort : false,
11104
11105     /**
11106     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11107      * loaded or when a record is removed. (defaults to false).
11108     */
11109     pruneModifiedRecords : false,
11110
11111     // private
11112     lastOptions : null,
11113
11114     /**
11115      * Add Records to the Store and fires the add event.
11116      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11117      */
11118     add : function(records){
11119         records = [].concat(records);
11120         for(var i = 0, len = records.length; i < len; i++){
11121             records[i].join(this);
11122         }
11123         var index = this.data.length;
11124         this.data.addAll(records);
11125         this.fireEvent("add", this, records, index);
11126     },
11127
11128     /**
11129      * Remove a Record from the Store and fires the remove event.
11130      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11131      */
11132     remove : function(record){
11133         var index = this.data.indexOf(record);
11134         this.data.removeAt(index);
11135  
11136         if(this.pruneModifiedRecords){
11137             this.modified.remove(record);
11138         }
11139         this.fireEvent("remove", this, record, index);
11140     },
11141
11142     /**
11143      * Remove all Records from the Store and fires the clear event.
11144      */
11145     removeAll : function(){
11146         this.data.clear();
11147         if(this.pruneModifiedRecords){
11148             this.modified = [];
11149         }
11150         this.fireEvent("clear", this);
11151     },
11152
11153     /**
11154      * Inserts Records to the Store at the given index and fires the add event.
11155      * @param {Number} index The start index at which to insert the passed Records.
11156      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11157      */
11158     insert : function(index, records){
11159         records = [].concat(records);
11160         for(var i = 0, len = records.length; i < len; i++){
11161             this.data.insert(index, records[i]);
11162             records[i].join(this);
11163         }
11164         this.fireEvent("add", this, records, index);
11165     },
11166
11167     /**
11168      * Get the index within the cache of the passed Record.
11169      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11170      * @return {Number} The index of the passed Record. Returns -1 if not found.
11171      */
11172     indexOf : function(record){
11173         return this.data.indexOf(record);
11174     },
11175
11176     /**
11177      * Get the index within the cache of the Record with the passed id.
11178      * @param {String} id The id of the Record to find.
11179      * @return {Number} The index of the Record. Returns -1 if not found.
11180      */
11181     indexOfId : function(id){
11182         return this.data.indexOfKey(id);
11183     },
11184
11185     /**
11186      * Get the Record with the specified id.
11187      * @param {String} id The id of the Record to find.
11188      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11189      */
11190     getById : function(id){
11191         return this.data.key(id);
11192     },
11193
11194     /**
11195      * Get the Record at the specified index.
11196      * @param {Number} index The index of the Record to find.
11197      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11198      */
11199     getAt : function(index){
11200         return this.data.itemAt(index);
11201     },
11202
11203     /**
11204      * Returns a range of Records between specified indices.
11205      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11206      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11207      * @return {Roo.data.Record[]} An array of Records
11208      */
11209     getRange : function(start, end){
11210         return this.data.getRange(start, end);
11211     },
11212
11213     // private
11214     storeOptions : function(o){
11215         o = Roo.apply({}, o);
11216         delete o.callback;
11217         delete o.scope;
11218         this.lastOptions = o;
11219     },
11220
11221     /**
11222      * Loads the Record cache from the configured Proxy using the configured Reader.
11223      * <p>
11224      * If using remote paging, then the first load call must specify the <em>start</em>
11225      * and <em>limit</em> properties in the options.params property to establish the initial
11226      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11227      * <p>
11228      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11229      * and this call will return before the new data has been loaded. Perform any post-processing
11230      * in a callback function, or in a "load" event handler.</strong>
11231      * <p>
11232      * @param {Object} options An object containing properties which control loading options:<ul>
11233      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11234      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11235      * passed the following arguments:<ul>
11236      * <li>r : Roo.data.Record[]</li>
11237      * <li>options: Options object from the load call</li>
11238      * <li>success: Boolean success indicator</li></ul></li>
11239      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11240      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11241      * </ul>
11242      */
11243     load : function(options){
11244         options = options || {};
11245         if(this.fireEvent("beforeload", this, options) !== false){
11246             this.storeOptions(options);
11247             var p = Roo.apply(options.params || {}, this.baseParams);
11248             // if meta was not loaded from remote source.. try requesting it.
11249             if (!this.reader.metaFromRemote) {
11250                 p._requestMeta = 1;
11251             }
11252             if(this.sortInfo && this.remoteSort){
11253                 var pn = this.paramNames;
11254                 p[pn["sort"]] = this.sortInfo.field;
11255                 p[pn["dir"]] = this.sortInfo.direction;
11256             }
11257             if (this.multiSort) {
11258                 var pn = this.paramNames;
11259                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11260             }
11261             
11262             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11263         }
11264     },
11265
11266     /**
11267      * Reloads the Record cache from the configured Proxy using the configured Reader and
11268      * the options from the last load operation performed.
11269      * @param {Object} options (optional) An object containing properties which may override the options
11270      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11271      * the most recently used options are reused).
11272      */
11273     reload : function(options){
11274         this.load(Roo.applyIf(options||{}, this.lastOptions));
11275     },
11276
11277     // private
11278     // Called as a callback by the Reader during a load operation.
11279     loadRecords : function(o, options, success){
11280         if(!o || success === false){
11281             if(success !== false){
11282                 this.fireEvent("load", this, [], options, o);
11283             }
11284             if(options.callback){
11285                 options.callback.call(options.scope || this, [], options, false);
11286             }
11287             return;
11288         }
11289         // if data returned failure - throw an exception.
11290         if (o.success === false) {
11291             // show a message if no listener is registered.
11292             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11293                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11294             }
11295             // loadmask wil be hooked into this..
11296             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11297             return;
11298         }
11299         var r = o.records, t = o.totalRecords || r.length;
11300         
11301         this.fireEvent("beforeloadadd", this, r, options, o);
11302         
11303         if(!options || options.add !== true){
11304             if(this.pruneModifiedRecords){
11305                 this.modified = [];
11306             }
11307             for(var i = 0, len = r.length; i < len; i++){
11308                 r[i].join(this);
11309             }
11310             if(this.snapshot){
11311                 this.data = this.snapshot;
11312                 delete this.snapshot;
11313             }
11314             this.data.clear();
11315             this.data.addAll(r);
11316             this.totalLength = t;
11317             this.applySort();
11318             this.fireEvent("datachanged", this);
11319         }else{
11320             this.totalLength = Math.max(t, this.data.length+r.length);
11321             this.add(r);
11322         }
11323         
11324         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11325                 
11326             var e = new Roo.data.Record({});
11327
11328             e.set(this.parent.displayField, this.parent.emptyTitle);
11329             e.set(this.parent.valueField, '');
11330
11331             this.insert(0, e);
11332         }
11333             
11334         this.fireEvent("load", this, r, options, o);
11335         if(options.callback){
11336             options.callback.call(options.scope || this, r, options, true);
11337         }
11338     },
11339
11340
11341     /**
11342      * Loads data from a passed data block. A Reader which understands the format of the data
11343      * must have been configured in the constructor.
11344      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11345      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11346      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11347      */
11348     loadData : function(o, append){
11349         var r = this.reader.readRecords(o);
11350         this.loadRecords(r, {add: append}, true);
11351     },
11352
11353     /**
11354      * Gets the number of cached records.
11355      * <p>
11356      * <em>If using paging, this may not be the total size of the dataset. If the data object
11357      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11358      * the data set size</em>
11359      */
11360     getCount : function(){
11361         return this.data.length || 0;
11362     },
11363
11364     /**
11365      * Gets the total number of records in the dataset as returned by the server.
11366      * <p>
11367      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11368      * the dataset size</em>
11369      */
11370     getTotalCount : function(){
11371         return this.totalLength || 0;
11372     },
11373
11374     /**
11375      * Returns the sort state of the Store as an object with two properties:
11376      * <pre><code>
11377  field {String} The name of the field by which the Records are sorted
11378  direction {String} The sort order, "ASC" or "DESC"
11379      * </code></pre>
11380      */
11381     getSortState : function(){
11382         return this.sortInfo;
11383     },
11384
11385     // private
11386     applySort : function(){
11387         if(this.sortInfo && !this.remoteSort){
11388             var s = this.sortInfo, f = s.field;
11389             var st = this.fields.get(f).sortType;
11390             var fn = function(r1, r2){
11391                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11392                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11393             };
11394             this.data.sort(s.direction, fn);
11395             if(this.snapshot && this.snapshot != this.data){
11396                 this.snapshot.sort(s.direction, fn);
11397             }
11398         }
11399     },
11400
11401     /**
11402      * Sets the default sort column and order to be used by the next load operation.
11403      * @param {String} fieldName The name of the field to sort by.
11404      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11405      */
11406     setDefaultSort : function(field, dir){
11407         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11408     },
11409
11410     /**
11411      * Sort the Records.
11412      * If remote sorting is used, the sort is performed on the server, and the cache is
11413      * reloaded. If local sorting is used, the cache is sorted internally.
11414      * @param {String} fieldName The name of the field to sort by.
11415      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11416      */
11417     sort : function(fieldName, dir){
11418         var f = this.fields.get(fieldName);
11419         if(!dir){
11420             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11421             
11422             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11423                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11424             }else{
11425                 dir = f.sortDir;
11426             }
11427         }
11428         this.sortToggle[f.name] = dir;
11429         this.sortInfo = {field: f.name, direction: dir};
11430         if(!this.remoteSort){
11431             this.applySort();
11432             this.fireEvent("datachanged", this);
11433         }else{
11434             this.load(this.lastOptions);
11435         }
11436     },
11437
11438     /**
11439      * Calls the specified function for each of the Records in the cache.
11440      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11441      * Returning <em>false</em> aborts and exits the iteration.
11442      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11443      */
11444     each : function(fn, scope){
11445         this.data.each(fn, scope);
11446     },
11447
11448     /**
11449      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11450      * (e.g., during paging).
11451      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11452      */
11453     getModifiedRecords : function(){
11454         return this.modified;
11455     },
11456
11457     // private
11458     createFilterFn : function(property, value, anyMatch){
11459         if(!value.exec){ // not a regex
11460             value = String(value);
11461             if(value.length == 0){
11462                 return false;
11463             }
11464             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11465         }
11466         return function(r){
11467             return value.test(r.data[property]);
11468         };
11469     },
11470
11471     /**
11472      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11473      * @param {String} property A field on your records
11474      * @param {Number} start The record index to start at (defaults to 0)
11475      * @param {Number} end The last record index to include (defaults to length - 1)
11476      * @return {Number} The sum
11477      */
11478     sum : function(property, start, end){
11479         var rs = this.data.items, v = 0;
11480         start = start || 0;
11481         end = (end || end === 0) ? end : rs.length-1;
11482
11483         for(var i = start; i <= end; i++){
11484             v += (rs[i].data[property] || 0);
11485         }
11486         return v;
11487     },
11488
11489     /**
11490      * Filter the records by a specified property.
11491      * @param {String} field A field on your records
11492      * @param {String/RegExp} value Either a string that the field
11493      * should start with or a RegExp to test against the field
11494      * @param {Boolean} anyMatch True to match any part not just the beginning
11495      */
11496     filter : function(property, value, anyMatch){
11497         var fn = this.createFilterFn(property, value, anyMatch);
11498         return fn ? this.filterBy(fn) : this.clearFilter();
11499     },
11500
11501     /**
11502      * Filter by a function. The specified function will be called with each
11503      * record in this data source. If the function returns true the record is included,
11504      * otherwise it is filtered.
11505      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11506      * @param {Object} scope (optional) The scope of the function (defaults to this)
11507      */
11508     filterBy : function(fn, scope){
11509         this.snapshot = this.snapshot || this.data;
11510         this.data = this.queryBy(fn, scope||this);
11511         this.fireEvent("datachanged", this);
11512     },
11513
11514     /**
11515      * Query the records by a specified property.
11516      * @param {String} field A field on your records
11517      * @param {String/RegExp} value Either a string that the field
11518      * should start with or a RegExp to test against the field
11519      * @param {Boolean} anyMatch True to match any part not just the beginning
11520      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11521      */
11522     query : function(property, value, anyMatch){
11523         var fn = this.createFilterFn(property, value, anyMatch);
11524         return fn ? this.queryBy(fn) : this.data.clone();
11525     },
11526
11527     /**
11528      * Query by a function. The specified function will be called with each
11529      * record in this data source. If the function returns true the record is included
11530      * in the results.
11531      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11532      * @param {Object} scope (optional) The scope of the function (defaults to this)
11533       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11534      **/
11535     queryBy : function(fn, scope){
11536         var data = this.snapshot || this.data;
11537         return data.filterBy(fn, scope||this);
11538     },
11539
11540     /**
11541      * Collects unique values for a particular dataIndex from this store.
11542      * @param {String} dataIndex The property to collect
11543      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11544      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11545      * @return {Array} An array of the unique values
11546      **/
11547     collect : function(dataIndex, allowNull, bypassFilter){
11548         var d = (bypassFilter === true && this.snapshot) ?
11549                 this.snapshot.items : this.data.items;
11550         var v, sv, r = [], l = {};
11551         for(var i = 0, len = d.length; i < len; i++){
11552             v = d[i].data[dataIndex];
11553             sv = String(v);
11554             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11555                 l[sv] = true;
11556                 r[r.length] = v;
11557             }
11558         }
11559         return r;
11560     },
11561
11562     /**
11563      * Revert to a view of the Record cache with no filtering applied.
11564      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11565      */
11566     clearFilter : function(suppressEvent){
11567         if(this.snapshot && this.snapshot != this.data){
11568             this.data = this.snapshot;
11569             delete this.snapshot;
11570             if(suppressEvent !== true){
11571                 this.fireEvent("datachanged", this);
11572             }
11573         }
11574     },
11575
11576     // private
11577     afterEdit : function(record){
11578         if(this.modified.indexOf(record) == -1){
11579             this.modified.push(record);
11580         }
11581         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11582     },
11583     
11584     // private
11585     afterReject : function(record){
11586         this.modified.remove(record);
11587         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11588     },
11589
11590     // private
11591     afterCommit : function(record){
11592         this.modified.remove(record);
11593         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11594     },
11595
11596     /**
11597      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11598      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11599      */
11600     commitChanges : function(){
11601         var m = this.modified.slice(0);
11602         this.modified = [];
11603         for(var i = 0, len = m.length; i < len; i++){
11604             m[i].commit();
11605         }
11606     },
11607
11608     /**
11609      * Cancel outstanding changes on all changed records.
11610      */
11611     rejectChanges : function(){
11612         var m = this.modified.slice(0);
11613         this.modified = [];
11614         for(var i = 0, len = m.length; i < len; i++){
11615             m[i].reject();
11616         }
11617     },
11618
11619     onMetaChange : function(meta, rtype, o){
11620         this.recordType = rtype;
11621         this.fields = rtype.prototype.fields;
11622         delete this.snapshot;
11623         this.sortInfo = meta.sortInfo || this.sortInfo;
11624         this.modified = [];
11625         this.fireEvent('metachange', this, this.reader.meta);
11626     },
11627     
11628     moveIndex : function(data, type)
11629     {
11630         var index = this.indexOf(data);
11631         
11632         var newIndex = index + type;
11633         
11634         this.remove(data);
11635         
11636         this.insert(newIndex, data);
11637         
11638     }
11639 });/*
11640  * Based on:
11641  * Ext JS Library 1.1.1
11642  * Copyright(c) 2006-2007, Ext JS, LLC.
11643  *
11644  * Originally Released Under LGPL - original licence link has changed is not relivant.
11645  *
11646  * Fork - LGPL
11647  * <script type="text/javascript">
11648  */
11649
11650 /**
11651  * @class Roo.data.SimpleStore
11652  * @extends Roo.data.Store
11653  * Small helper class to make creating Stores from Array data easier.
11654  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11655  * @cfg {Array} fields An array of field definition objects, or field name strings.
11656  * @cfg {Array} data The multi-dimensional array of data
11657  * @constructor
11658  * @param {Object} config
11659  */
11660 Roo.data.SimpleStore = function(config){
11661     Roo.data.SimpleStore.superclass.constructor.call(this, {
11662         isLocal : true,
11663         reader: new Roo.data.ArrayReader({
11664                 id: config.id
11665             },
11666             Roo.data.Record.create(config.fields)
11667         ),
11668         proxy : new Roo.data.MemoryProxy(config.data)
11669     });
11670     this.load();
11671 };
11672 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11673  * Based on:
11674  * Ext JS Library 1.1.1
11675  * Copyright(c) 2006-2007, Ext JS, LLC.
11676  *
11677  * Originally Released Under LGPL - original licence link has changed is not relivant.
11678  *
11679  * Fork - LGPL
11680  * <script type="text/javascript">
11681  */
11682
11683 /**
11684 /**
11685  * @extends Roo.data.Store
11686  * @class Roo.data.JsonStore
11687  * Small helper class to make creating Stores for JSON data easier. <br/>
11688 <pre><code>
11689 var store = new Roo.data.JsonStore({
11690     url: 'get-images.php',
11691     root: 'images',
11692     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11693 });
11694 </code></pre>
11695  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11696  * JsonReader and HttpProxy (unless inline data is provided).</b>
11697  * @cfg {Array} fields An array of field definition objects, or field name strings.
11698  * @constructor
11699  * @param {Object} config
11700  */
11701 Roo.data.JsonStore = function(c){
11702     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11703         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11704         reader: new Roo.data.JsonReader(c, c.fields)
11705     }));
11706 };
11707 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11708  * Based on:
11709  * Ext JS Library 1.1.1
11710  * Copyright(c) 2006-2007, Ext JS, LLC.
11711  *
11712  * Originally Released Under LGPL - original licence link has changed is not relivant.
11713  *
11714  * Fork - LGPL
11715  * <script type="text/javascript">
11716  */
11717
11718  
11719 Roo.data.Field = function(config){
11720     if(typeof config == "string"){
11721         config = {name: config};
11722     }
11723     Roo.apply(this, config);
11724     
11725     if(!this.type){
11726         this.type = "auto";
11727     }
11728     
11729     var st = Roo.data.SortTypes;
11730     // named sortTypes are supported, here we look them up
11731     if(typeof this.sortType == "string"){
11732         this.sortType = st[this.sortType];
11733     }
11734     
11735     // set default sortType for strings and dates
11736     if(!this.sortType){
11737         switch(this.type){
11738             case "string":
11739                 this.sortType = st.asUCString;
11740                 break;
11741             case "date":
11742                 this.sortType = st.asDate;
11743                 break;
11744             default:
11745                 this.sortType = st.none;
11746         }
11747     }
11748
11749     // define once
11750     var stripRe = /[\$,%]/g;
11751
11752     // prebuilt conversion function for this field, instead of
11753     // switching every time we're reading a value
11754     if(!this.convert){
11755         var cv, dateFormat = this.dateFormat;
11756         switch(this.type){
11757             case "":
11758             case "auto":
11759             case undefined:
11760                 cv = function(v){ return v; };
11761                 break;
11762             case "string":
11763                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11764                 break;
11765             case "int":
11766                 cv = function(v){
11767                     return v !== undefined && v !== null && v !== '' ?
11768                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11769                     };
11770                 break;
11771             case "float":
11772                 cv = function(v){
11773                     return v !== undefined && v !== null && v !== '' ?
11774                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11775                     };
11776                 break;
11777             case "bool":
11778             case "boolean":
11779                 cv = function(v){ return v === true || v === "true" || v == 1; };
11780                 break;
11781             case "date":
11782                 cv = function(v){
11783                     if(!v){
11784                         return '';
11785                     }
11786                     if(v instanceof Date){
11787                         return v;
11788                     }
11789                     if(dateFormat){
11790                         if(dateFormat == "timestamp"){
11791                             return new Date(v*1000);
11792                         }
11793                         return Date.parseDate(v, dateFormat);
11794                     }
11795                     var parsed = Date.parse(v);
11796                     return parsed ? new Date(parsed) : null;
11797                 };
11798              break;
11799             
11800         }
11801         this.convert = cv;
11802     }
11803 };
11804
11805 Roo.data.Field.prototype = {
11806     dateFormat: null,
11807     defaultValue: "",
11808     mapping: null,
11809     sortType : null,
11810     sortDir : "ASC"
11811 };/*
11812  * Based on:
11813  * Ext JS Library 1.1.1
11814  * Copyright(c) 2006-2007, Ext JS, LLC.
11815  *
11816  * Originally Released Under LGPL - original licence link has changed is not relivant.
11817  *
11818  * Fork - LGPL
11819  * <script type="text/javascript">
11820  */
11821  
11822 // Base class for reading structured data from a data source.  This class is intended to be
11823 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11824
11825 /**
11826  * @class Roo.data.DataReader
11827  * Base class for reading structured data from a data source.  This class is intended to be
11828  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11829  */
11830
11831 Roo.data.DataReader = function(meta, recordType){
11832     
11833     this.meta = meta;
11834     
11835     this.recordType = recordType instanceof Array ? 
11836         Roo.data.Record.create(recordType) : recordType;
11837 };
11838
11839 Roo.data.DataReader.prototype = {
11840      /**
11841      * Create an empty record
11842      * @param {Object} data (optional) - overlay some values
11843      * @return {Roo.data.Record} record created.
11844      */
11845     newRow :  function(d) {
11846         var da =  {};
11847         this.recordType.prototype.fields.each(function(c) {
11848             switch( c.type) {
11849                 case 'int' : da[c.name] = 0; break;
11850                 case 'date' : da[c.name] = new Date(); break;
11851                 case 'float' : da[c.name] = 0.0; break;
11852                 case 'boolean' : da[c.name] = false; break;
11853                 default : da[c.name] = ""; break;
11854             }
11855             
11856         });
11857         return new this.recordType(Roo.apply(da, d));
11858     }
11859     
11860 };/*
11861  * Based on:
11862  * Ext JS Library 1.1.1
11863  * Copyright(c) 2006-2007, Ext JS, LLC.
11864  *
11865  * Originally Released Under LGPL - original licence link has changed is not relivant.
11866  *
11867  * Fork - LGPL
11868  * <script type="text/javascript">
11869  */
11870
11871 /**
11872  * @class Roo.data.DataProxy
11873  * @extends Roo.data.Observable
11874  * This class is an abstract base class for implementations which provide retrieval of
11875  * unformatted data objects.<br>
11876  * <p>
11877  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11878  * (of the appropriate type which knows how to parse the data object) to provide a block of
11879  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11880  * <p>
11881  * Custom implementations must implement the load method as described in
11882  * {@link Roo.data.HttpProxy#load}.
11883  */
11884 Roo.data.DataProxy = function(){
11885     this.addEvents({
11886         /**
11887          * @event beforeload
11888          * Fires before a network request is made to retrieve a data object.
11889          * @param {Object} This DataProxy object.
11890          * @param {Object} params The params parameter to the load function.
11891          */
11892         beforeload : true,
11893         /**
11894          * @event load
11895          * Fires before the load method's callback is called.
11896          * @param {Object} This DataProxy object.
11897          * @param {Object} o The data object.
11898          * @param {Object} arg The callback argument object passed to the load function.
11899          */
11900         load : true,
11901         /**
11902          * @event loadexception
11903          * Fires if an Exception occurs during data retrieval.
11904          * @param {Object} This DataProxy object.
11905          * @param {Object} o The data object.
11906          * @param {Object} arg The callback argument object passed to the load function.
11907          * @param {Object} e The Exception.
11908          */
11909         loadexception : true
11910     });
11911     Roo.data.DataProxy.superclass.constructor.call(this);
11912 };
11913
11914 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11915
11916     /**
11917      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11918      */
11919 /*
11920  * Based on:
11921  * Ext JS Library 1.1.1
11922  * Copyright(c) 2006-2007, Ext JS, LLC.
11923  *
11924  * Originally Released Under LGPL - original licence link has changed is not relivant.
11925  *
11926  * Fork - LGPL
11927  * <script type="text/javascript">
11928  */
11929 /**
11930  * @class Roo.data.MemoryProxy
11931  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11932  * to the Reader when its load method is called.
11933  * @constructor
11934  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11935  */
11936 Roo.data.MemoryProxy = function(data){
11937     if (data.data) {
11938         data = data.data;
11939     }
11940     Roo.data.MemoryProxy.superclass.constructor.call(this);
11941     this.data = data;
11942 };
11943
11944 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11945     
11946     /**
11947      * Load data from the requested source (in this case an in-memory
11948      * data object passed to the constructor), read the data object into
11949      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11950      * process that block using the passed callback.
11951      * @param {Object} params This parameter is not used by the MemoryProxy class.
11952      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11953      * object into a block of Roo.data.Records.
11954      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11955      * The function must be passed <ul>
11956      * <li>The Record block object</li>
11957      * <li>The "arg" argument from the load function</li>
11958      * <li>A boolean success indicator</li>
11959      * </ul>
11960      * @param {Object} scope The scope in which to call the callback
11961      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11962      */
11963     load : function(params, reader, callback, scope, arg){
11964         params = params || {};
11965         var result;
11966         try {
11967             result = reader.readRecords(this.data);
11968         }catch(e){
11969             this.fireEvent("loadexception", this, arg, null, e);
11970             callback.call(scope, null, arg, false);
11971             return;
11972         }
11973         callback.call(scope, result, arg, true);
11974     },
11975     
11976     // private
11977     update : function(params, records){
11978         
11979     }
11980 });/*
11981  * Based on:
11982  * Ext JS Library 1.1.1
11983  * Copyright(c) 2006-2007, Ext JS, LLC.
11984  *
11985  * Originally Released Under LGPL - original licence link has changed is not relivant.
11986  *
11987  * Fork - LGPL
11988  * <script type="text/javascript">
11989  */
11990 /**
11991  * @class Roo.data.HttpProxy
11992  * @extends Roo.data.DataProxy
11993  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11994  * configured to reference a certain URL.<br><br>
11995  * <p>
11996  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11997  * from which the running page was served.<br><br>
11998  * <p>
11999  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12000  * <p>
12001  * Be aware that to enable the browser to parse an XML document, the server must set
12002  * the Content-Type header in the HTTP response to "text/xml".
12003  * @constructor
12004  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12005  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12006  * will be used to make the request.
12007  */
12008 Roo.data.HttpProxy = function(conn){
12009     Roo.data.HttpProxy.superclass.constructor.call(this);
12010     // is conn a conn config or a real conn?
12011     this.conn = conn;
12012     this.useAjax = !conn || !conn.events;
12013   
12014 };
12015
12016 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12017     // thse are take from connection...
12018     
12019     /**
12020      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12021      */
12022     /**
12023      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12024      * extra parameters to each request made by this object. (defaults to undefined)
12025      */
12026     /**
12027      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12028      *  to each request made by this object. (defaults to undefined)
12029      */
12030     /**
12031      * @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)
12032      */
12033     /**
12034      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12035      */
12036      /**
12037      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12038      * @type Boolean
12039      */
12040   
12041
12042     /**
12043      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12044      * @type Boolean
12045      */
12046     /**
12047      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12048      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12049      * a finer-grained basis than the DataProxy events.
12050      */
12051     getConnection : function(){
12052         return this.useAjax ? Roo.Ajax : this.conn;
12053     },
12054
12055     /**
12056      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12057      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12058      * process that block using the passed callback.
12059      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12060      * for the request to the remote server.
12061      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12062      * object into a block of Roo.data.Records.
12063      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12064      * The function must be passed <ul>
12065      * <li>The Record block object</li>
12066      * <li>The "arg" argument from the load function</li>
12067      * <li>A boolean success indicator</li>
12068      * </ul>
12069      * @param {Object} scope The scope in which to call the callback
12070      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12071      */
12072     load : function(params, reader, callback, scope, arg){
12073         if(this.fireEvent("beforeload", this, params) !== false){
12074             var  o = {
12075                 params : params || {},
12076                 request: {
12077                     callback : callback,
12078                     scope : scope,
12079                     arg : arg
12080                 },
12081                 reader: reader,
12082                 callback : this.loadResponse,
12083                 scope: this
12084             };
12085             if(this.useAjax){
12086                 Roo.applyIf(o, this.conn);
12087                 if(this.activeRequest){
12088                     Roo.Ajax.abort(this.activeRequest);
12089                 }
12090                 this.activeRequest = Roo.Ajax.request(o);
12091             }else{
12092                 this.conn.request(o);
12093             }
12094         }else{
12095             callback.call(scope||this, null, arg, false);
12096         }
12097     },
12098
12099     // private
12100     loadResponse : function(o, success, response){
12101         delete this.activeRequest;
12102         if(!success){
12103             this.fireEvent("loadexception", this, o, response);
12104             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12105             return;
12106         }
12107         var result;
12108         try {
12109             result = o.reader.read(response);
12110         }catch(e){
12111             this.fireEvent("loadexception", this, o, response, e);
12112             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12113             return;
12114         }
12115         
12116         this.fireEvent("load", this, o, o.request.arg);
12117         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12118     },
12119
12120     // private
12121     update : function(dataSet){
12122
12123     },
12124
12125     // private
12126     updateResponse : function(dataSet){
12127
12128     }
12129 });/*
12130  * Based on:
12131  * Ext JS Library 1.1.1
12132  * Copyright(c) 2006-2007, Ext JS, LLC.
12133  *
12134  * Originally Released Under LGPL - original licence link has changed is not relivant.
12135  *
12136  * Fork - LGPL
12137  * <script type="text/javascript">
12138  */
12139
12140 /**
12141  * @class Roo.data.ScriptTagProxy
12142  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12143  * other than the originating domain of the running page.<br><br>
12144  * <p>
12145  * <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
12146  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12147  * <p>
12148  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12149  * source code that is used as the source inside a &lt;script> tag.<br><br>
12150  * <p>
12151  * In order for the browser to process the returned data, the server must wrap the data object
12152  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12153  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12154  * depending on whether the callback name was passed:
12155  * <p>
12156  * <pre><code>
12157 boolean scriptTag = false;
12158 String cb = request.getParameter("callback");
12159 if (cb != null) {
12160     scriptTag = true;
12161     response.setContentType("text/javascript");
12162 } else {
12163     response.setContentType("application/x-json");
12164 }
12165 Writer out = response.getWriter();
12166 if (scriptTag) {
12167     out.write(cb + "(");
12168 }
12169 out.print(dataBlock.toJsonString());
12170 if (scriptTag) {
12171     out.write(");");
12172 }
12173 </pre></code>
12174  *
12175  * @constructor
12176  * @param {Object} config A configuration object.
12177  */
12178 Roo.data.ScriptTagProxy = function(config){
12179     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12180     Roo.apply(this, config);
12181     this.head = document.getElementsByTagName("head")[0];
12182 };
12183
12184 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12185
12186 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12187     /**
12188      * @cfg {String} url The URL from which to request the data object.
12189      */
12190     /**
12191      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12192      */
12193     timeout : 30000,
12194     /**
12195      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12196      * the server the name of the callback function set up by the load call to process the returned data object.
12197      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12198      * javascript output which calls this named function passing the data object as its only parameter.
12199      */
12200     callbackParam : "callback",
12201     /**
12202      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12203      * name to the request.
12204      */
12205     nocache : true,
12206
12207     /**
12208      * Load data from the configured URL, read the data object into
12209      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12210      * process that block using the passed callback.
12211      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12212      * for the request to the remote server.
12213      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12214      * object into a block of Roo.data.Records.
12215      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12216      * The function must be passed <ul>
12217      * <li>The Record block object</li>
12218      * <li>The "arg" argument from the load function</li>
12219      * <li>A boolean success indicator</li>
12220      * </ul>
12221      * @param {Object} scope The scope in which to call the callback
12222      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12223      */
12224     load : function(params, reader, callback, scope, arg){
12225         if(this.fireEvent("beforeload", this, params) !== false){
12226
12227             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12228
12229             var url = this.url;
12230             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12231             if(this.nocache){
12232                 url += "&_dc=" + (new Date().getTime());
12233             }
12234             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12235             var trans = {
12236                 id : transId,
12237                 cb : "stcCallback"+transId,
12238                 scriptId : "stcScript"+transId,
12239                 params : params,
12240                 arg : arg,
12241                 url : url,
12242                 callback : callback,
12243                 scope : scope,
12244                 reader : reader
12245             };
12246             var conn = this;
12247
12248             window[trans.cb] = function(o){
12249                 conn.handleResponse(o, trans);
12250             };
12251
12252             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12253
12254             if(this.autoAbort !== false){
12255                 this.abort();
12256             }
12257
12258             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12259
12260             var script = document.createElement("script");
12261             script.setAttribute("src", url);
12262             script.setAttribute("type", "text/javascript");
12263             script.setAttribute("id", trans.scriptId);
12264             this.head.appendChild(script);
12265
12266             this.trans = trans;
12267         }else{
12268             callback.call(scope||this, null, arg, false);
12269         }
12270     },
12271
12272     // private
12273     isLoading : function(){
12274         return this.trans ? true : false;
12275     },
12276
12277     /**
12278      * Abort the current server request.
12279      */
12280     abort : function(){
12281         if(this.isLoading()){
12282             this.destroyTrans(this.trans);
12283         }
12284     },
12285
12286     // private
12287     destroyTrans : function(trans, isLoaded){
12288         this.head.removeChild(document.getElementById(trans.scriptId));
12289         clearTimeout(trans.timeoutId);
12290         if(isLoaded){
12291             window[trans.cb] = undefined;
12292             try{
12293                 delete window[trans.cb];
12294             }catch(e){}
12295         }else{
12296             // if hasn't been loaded, wait for load to remove it to prevent script error
12297             window[trans.cb] = function(){
12298                 window[trans.cb] = undefined;
12299                 try{
12300                     delete window[trans.cb];
12301                 }catch(e){}
12302             };
12303         }
12304     },
12305
12306     // private
12307     handleResponse : function(o, trans){
12308         this.trans = false;
12309         this.destroyTrans(trans, true);
12310         var result;
12311         try {
12312             result = trans.reader.readRecords(o);
12313         }catch(e){
12314             this.fireEvent("loadexception", this, o, trans.arg, e);
12315             trans.callback.call(trans.scope||window, null, trans.arg, false);
12316             return;
12317         }
12318         this.fireEvent("load", this, o, trans.arg);
12319         trans.callback.call(trans.scope||window, result, trans.arg, true);
12320     },
12321
12322     // private
12323     handleFailure : function(trans){
12324         this.trans = false;
12325         this.destroyTrans(trans, false);
12326         this.fireEvent("loadexception", this, null, trans.arg);
12327         trans.callback.call(trans.scope||window, null, trans.arg, false);
12328     }
12329 });/*
12330  * Based on:
12331  * Ext JS Library 1.1.1
12332  * Copyright(c) 2006-2007, Ext JS, LLC.
12333  *
12334  * Originally Released Under LGPL - original licence link has changed is not relivant.
12335  *
12336  * Fork - LGPL
12337  * <script type="text/javascript">
12338  */
12339
12340 /**
12341  * @class Roo.data.JsonReader
12342  * @extends Roo.data.DataReader
12343  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12344  * based on mappings in a provided Roo.data.Record constructor.
12345  * 
12346  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12347  * in the reply previously. 
12348  * 
12349  * <p>
12350  * Example code:
12351  * <pre><code>
12352 var RecordDef = Roo.data.Record.create([
12353     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12354     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12355 ]);
12356 var myReader = new Roo.data.JsonReader({
12357     totalProperty: "results",    // The property which contains the total dataset size (optional)
12358     root: "rows",                // The property which contains an Array of row objects
12359     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12360 }, RecordDef);
12361 </code></pre>
12362  * <p>
12363  * This would consume a JSON file like this:
12364  * <pre><code>
12365 { 'results': 2, 'rows': [
12366     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12367     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12368 }
12369 </code></pre>
12370  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12371  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12372  * paged from the remote server.
12373  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12374  * @cfg {String} root name of the property which contains the Array of row objects.
12375  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12376  * @cfg {Array} fields Array of field definition objects
12377  * @constructor
12378  * Create a new JsonReader
12379  * @param {Object} meta Metadata configuration options
12380  * @param {Object} recordType Either an Array of field definition objects,
12381  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12382  */
12383 Roo.data.JsonReader = function(meta, recordType){
12384     
12385     meta = meta || {};
12386     // set some defaults:
12387     Roo.applyIf(meta, {
12388         totalProperty: 'total',
12389         successProperty : 'success',
12390         root : 'data',
12391         id : 'id'
12392     });
12393     
12394     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12395 };
12396 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12397     
12398     /**
12399      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12400      * Used by Store query builder to append _requestMeta to params.
12401      * 
12402      */
12403     metaFromRemote : false,
12404     /**
12405      * This method is only used by a DataProxy which has retrieved data from a remote server.
12406      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12407      * @return {Object} data A data block which is used by an Roo.data.Store object as
12408      * a cache of Roo.data.Records.
12409      */
12410     read : function(response){
12411         var json = response.responseText;
12412        
12413         var o = /* eval:var:o */ eval("("+json+")");
12414         if(!o) {
12415             throw {message: "JsonReader.read: Json object not found"};
12416         }
12417         
12418         if(o.metaData){
12419             
12420             delete this.ef;
12421             this.metaFromRemote = true;
12422             this.meta = o.metaData;
12423             this.recordType = Roo.data.Record.create(o.metaData.fields);
12424             this.onMetaChange(this.meta, this.recordType, o);
12425         }
12426         return this.readRecords(o);
12427     },
12428
12429     // private function a store will implement
12430     onMetaChange : function(meta, recordType, o){
12431
12432     },
12433
12434     /**
12435          * @ignore
12436          */
12437     simpleAccess: function(obj, subsc) {
12438         return obj[subsc];
12439     },
12440
12441         /**
12442          * @ignore
12443          */
12444     getJsonAccessor: function(){
12445         var re = /[\[\.]/;
12446         return function(expr) {
12447             try {
12448                 return(re.test(expr))
12449                     ? new Function("obj", "return obj." + expr)
12450                     : function(obj){
12451                         return obj[expr];
12452                     };
12453             } catch(e){}
12454             return Roo.emptyFn;
12455         };
12456     }(),
12457
12458     /**
12459      * Create a data block containing Roo.data.Records from an XML document.
12460      * @param {Object} o An object which contains an Array of row objects in the property specified
12461      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12462      * which contains the total size of the dataset.
12463      * @return {Object} data A data block which is used by an Roo.data.Store object as
12464      * a cache of Roo.data.Records.
12465      */
12466     readRecords : function(o){
12467         /**
12468          * After any data loads, the raw JSON data is available for further custom processing.
12469          * @type Object
12470          */
12471         this.o = o;
12472         var s = this.meta, Record = this.recordType,
12473             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12474
12475 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12476         if (!this.ef) {
12477             if(s.totalProperty) {
12478                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12479                 }
12480                 if(s.successProperty) {
12481                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12482                 }
12483                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12484                 if (s.id) {
12485                         var g = this.getJsonAccessor(s.id);
12486                         this.getId = function(rec) {
12487                                 var r = g(rec);  
12488                                 return (r === undefined || r === "") ? null : r;
12489                         };
12490                 } else {
12491                         this.getId = function(){return null;};
12492                 }
12493             this.ef = [];
12494             for(var jj = 0; jj < fl; jj++){
12495                 f = fi[jj];
12496                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12497                 this.ef[jj] = this.getJsonAccessor(map);
12498             }
12499         }
12500
12501         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12502         if(s.totalProperty){
12503             var vt = parseInt(this.getTotal(o), 10);
12504             if(!isNaN(vt)){
12505                 totalRecords = vt;
12506             }
12507         }
12508         if(s.successProperty){
12509             var vs = this.getSuccess(o);
12510             if(vs === false || vs === 'false'){
12511                 success = false;
12512             }
12513         }
12514         var records = [];
12515         for(var i = 0; i < c; i++){
12516                 var n = root[i];
12517             var values = {};
12518             var id = this.getId(n);
12519             for(var j = 0; j < fl; j++){
12520                 f = fi[j];
12521             var v = this.ef[j](n);
12522             if (!f.convert) {
12523                 Roo.log('missing convert for ' + f.name);
12524                 Roo.log(f);
12525                 continue;
12526             }
12527             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12528             }
12529             var record = new Record(values, id);
12530             record.json = n;
12531             records[i] = record;
12532         }
12533         return {
12534             raw : o,
12535             success : success,
12536             records : records,
12537             totalRecords : totalRecords
12538         };
12539     }
12540 });/*
12541  * Based on:
12542  * Ext JS Library 1.1.1
12543  * Copyright(c) 2006-2007, Ext JS, LLC.
12544  *
12545  * Originally Released Under LGPL - original licence link has changed is not relivant.
12546  *
12547  * Fork - LGPL
12548  * <script type="text/javascript">
12549  */
12550
12551 /**
12552  * @class Roo.data.ArrayReader
12553  * @extends Roo.data.DataReader
12554  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12555  * Each element of that Array represents a row of data fields. The
12556  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12557  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12558  * <p>
12559  * Example code:.
12560  * <pre><code>
12561 var RecordDef = Roo.data.Record.create([
12562     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12563     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12564 ]);
12565 var myReader = new Roo.data.ArrayReader({
12566     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12567 }, RecordDef);
12568 </code></pre>
12569  * <p>
12570  * This would consume an Array like this:
12571  * <pre><code>
12572 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12573   </code></pre>
12574  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12575  * @constructor
12576  * Create a new JsonReader
12577  * @param {Object} meta Metadata configuration options.
12578  * @param {Object} recordType Either an Array of field definition objects
12579  * as specified to {@link Roo.data.Record#create},
12580  * or an {@link Roo.data.Record} object
12581  * created using {@link Roo.data.Record#create}.
12582  */
12583 Roo.data.ArrayReader = function(meta, recordType){
12584     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12585 };
12586
12587 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12588     /**
12589      * Create a data block containing Roo.data.Records from an XML document.
12590      * @param {Object} o An Array of row objects which represents the dataset.
12591      * @return {Object} data A data block which is used by an Roo.data.Store object as
12592      * a cache of Roo.data.Records.
12593      */
12594     readRecords : function(o){
12595         var sid = this.meta ? this.meta.id : null;
12596         var recordType = this.recordType, fields = recordType.prototype.fields;
12597         var records = [];
12598         var root = o;
12599             for(var i = 0; i < root.length; i++){
12600                     var n = root[i];
12601                 var values = {};
12602                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12603                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12604                 var f = fields.items[j];
12605                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12606                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12607                 v = f.convert(v);
12608                 values[f.name] = v;
12609             }
12610                 var record = new recordType(values, id);
12611                 record.json = n;
12612                 records[records.length] = record;
12613             }
12614             return {
12615                 records : records,
12616                 totalRecords : records.length
12617             };
12618     }
12619 });/*
12620  * - LGPL
12621  * * 
12622  */
12623
12624 /**
12625  * @class Roo.bootstrap.ComboBox
12626  * @extends Roo.bootstrap.TriggerField
12627  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12628  * @cfg {Boolean} append (true|false) default false
12629  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12630  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12631  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12632  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12633  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12634  * @cfg {Boolean} animate default true
12635  * @cfg {Boolean} emptyResultText only for touch device
12636  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12637  * @cfg {String} emptyTitle default ''
12638  * @constructor
12639  * Create a new ComboBox.
12640  * @param {Object} config Configuration options
12641  */
12642 Roo.bootstrap.ComboBox = function(config){
12643     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12644     this.addEvents({
12645         /**
12646          * @event expand
12647          * Fires when the dropdown list is expanded
12648         * @param {Roo.bootstrap.ComboBox} combo This combo box
12649         */
12650         'expand' : true,
12651         /**
12652          * @event collapse
12653          * Fires when the dropdown list is collapsed
12654         * @param {Roo.bootstrap.ComboBox} combo This combo box
12655         */
12656         'collapse' : true,
12657         /**
12658          * @event beforeselect
12659          * Fires before a list item is selected. Return false to cancel the selection.
12660         * @param {Roo.bootstrap.ComboBox} combo This combo box
12661         * @param {Roo.data.Record} record The data record returned from the underlying store
12662         * @param {Number} index The index of the selected item in the dropdown list
12663         */
12664         'beforeselect' : true,
12665         /**
12666          * @event select
12667          * Fires when a list item is selected
12668         * @param {Roo.bootstrap.ComboBox} combo This combo box
12669         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12670         * @param {Number} index The index of the selected item in the dropdown list
12671         */
12672         'select' : true,
12673         /**
12674          * @event beforequery
12675          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12676          * The event object passed has these properties:
12677         * @param {Roo.bootstrap.ComboBox} combo This combo box
12678         * @param {String} query The query
12679         * @param {Boolean} forceAll true to force "all" query
12680         * @param {Boolean} cancel true to cancel the query
12681         * @param {Object} e The query event object
12682         */
12683         'beforequery': true,
12684          /**
12685          * @event add
12686          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12687         * @param {Roo.bootstrap.ComboBox} combo This combo box
12688         */
12689         'add' : true,
12690         /**
12691          * @event edit
12692          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12693         * @param {Roo.bootstrap.ComboBox} combo This combo box
12694         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12695         */
12696         'edit' : true,
12697         /**
12698          * @event remove
12699          * Fires when the remove value from the combobox array
12700         * @param {Roo.bootstrap.ComboBox} combo This combo box
12701         */
12702         'remove' : true,
12703         /**
12704          * @event afterremove
12705          * Fires when the remove value from the combobox array
12706         * @param {Roo.bootstrap.ComboBox} combo This combo box
12707         */
12708         'afterremove' : true,
12709         /**
12710          * @event specialfilter
12711          * Fires when specialfilter
12712             * @param {Roo.bootstrap.ComboBox} combo This combo box
12713             */
12714         'specialfilter' : true,
12715         /**
12716          * @event tick
12717          * Fires when tick the element
12718             * @param {Roo.bootstrap.ComboBox} combo This combo box
12719             */
12720         'tick' : true,
12721         /**
12722          * @event touchviewdisplay
12723          * Fires when touch view require special display (default is using displayField)
12724             * @param {Roo.bootstrap.ComboBox} combo This combo box
12725             * @param {Object} cfg set html .
12726             */
12727         'touchviewdisplay' : true
12728         
12729     });
12730     
12731     this.item = [];
12732     this.tickItems = [];
12733     
12734     this.selectedIndex = -1;
12735     if(this.mode == 'local'){
12736         if(config.queryDelay === undefined){
12737             this.queryDelay = 10;
12738         }
12739         if(config.minChars === undefined){
12740             this.minChars = 0;
12741         }
12742     }
12743 };
12744
12745 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12746      
12747     /**
12748      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12749      * rendering into an Roo.Editor, defaults to false)
12750      */
12751     /**
12752      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12753      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12754      */
12755     /**
12756      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12757      */
12758     /**
12759      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12760      * the dropdown list (defaults to undefined, with no header element)
12761      */
12762
12763      /**
12764      * @cfg {String/Roo.Template} tpl The template to use to render the output
12765      */
12766      
12767      /**
12768      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12769      */
12770     listWidth: undefined,
12771     /**
12772      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12773      * mode = 'remote' or 'text' if mode = 'local')
12774      */
12775     displayField: undefined,
12776     
12777     /**
12778      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12779      * mode = 'remote' or 'value' if mode = 'local'). 
12780      * Note: use of a valueField requires the user make a selection
12781      * in order for a value to be mapped.
12782      */
12783     valueField: undefined,
12784     /**
12785      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12786      */
12787     modalTitle : '',
12788     
12789     /**
12790      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12791      * field's data value (defaults to the underlying DOM element's name)
12792      */
12793     hiddenName: undefined,
12794     /**
12795      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12796      */
12797     listClass: '',
12798     /**
12799      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12800      */
12801     selectedClass: 'active',
12802     
12803     /**
12804      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12805      */
12806     shadow:'sides',
12807     /**
12808      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12809      * anchor positions (defaults to 'tl-bl')
12810      */
12811     listAlign: 'tl-bl?',
12812     /**
12813      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12814      */
12815     maxHeight: 300,
12816     /**
12817      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12818      * query specified by the allQuery config option (defaults to 'query')
12819      */
12820     triggerAction: 'query',
12821     /**
12822      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12823      * (defaults to 4, does not apply if editable = false)
12824      */
12825     minChars : 4,
12826     /**
12827      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12828      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12829      */
12830     typeAhead: false,
12831     /**
12832      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12833      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12834      */
12835     queryDelay: 500,
12836     /**
12837      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12838      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12839      */
12840     pageSize: 0,
12841     /**
12842      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12843      * when editable = true (defaults to false)
12844      */
12845     selectOnFocus:false,
12846     /**
12847      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12848      */
12849     queryParam: 'query',
12850     /**
12851      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12852      * when mode = 'remote' (defaults to 'Loading...')
12853      */
12854     loadingText: 'Loading...',
12855     /**
12856      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12857      */
12858     resizable: false,
12859     /**
12860      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12861      */
12862     handleHeight : 8,
12863     /**
12864      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12865      * traditional select (defaults to true)
12866      */
12867     editable: true,
12868     /**
12869      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12870      */
12871     allQuery: '',
12872     /**
12873      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12874      */
12875     mode: 'remote',
12876     /**
12877      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12878      * listWidth has a higher value)
12879      */
12880     minListWidth : 70,
12881     /**
12882      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12883      * allow the user to set arbitrary text into the field (defaults to false)
12884      */
12885     forceSelection:false,
12886     /**
12887      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12888      * if typeAhead = true (defaults to 250)
12889      */
12890     typeAheadDelay : 250,
12891     /**
12892      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12893      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12894      */
12895     valueNotFoundText : undefined,
12896     /**
12897      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12898      */
12899     blockFocus : false,
12900     
12901     /**
12902      * @cfg {Boolean} disableClear Disable showing of clear button.
12903      */
12904     disableClear : false,
12905     /**
12906      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12907      */
12908     alwaysQuery : false,
12909     
12910     /**
12911      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12912      */
12913     multiple : false,
12914     
12915     /**
12916      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12917      */
12918     invalidClass : "has-warning",
12919     
12920     /**
12921      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12922      */
12923     validClass : "has-success",
12924     
12925     /**
12926      * @cfg {Boolean} specialFilter (true|false) special filter default false
12927      */
12928     specialFilter : false,
12929     
12930     /**
12931      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12932      */
12933     mobileTouchView : true,
12934     
12935     /**
12936      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12937      */
12938     useNativeIOS : false,
12939     
12940     ios_options : false,
12941     
12942     //private
12943     addicon : false,
12944     editicon: false,
12945     
12946     page: 0,
12947     hasQuery: false,
12948     append: false,
12949     loadNext: false,
12950     autoFocus : true,
12951     tickable : false,
12952     btnPosition : 'right',
12953     triggerList : true,
12954     showToggleBtn : true,
12955     animate : true,
12956     emptyResultText: 'Empty',
12957     triggerText : 'Select',
12958     emptyTitle : '',
12959     
12960     // element that contains real text value.. (when hidden is used..)
12961     
12962     getAutoCreate : function()
12963     {   
12964         var cfg = false;
12965         //render
12966         /*
12967          * Render classic select for iso
12968          */
12969         
12970         if(Roo.isIOS && this.useNativeIOS){
12971             cfg = this.getAutoCreateNativeIOS();
12972             return cfg;
12973         }
12974         
12975         /*
12976          * Touch Devices
12977          */
12978         
12979         if(Roo.isTouch && this.mobileTouchView){
12980             cfg = this.getAutoCreateTouchView();
12981             return cfg;;
12982         }
12983         
12984         /*
12985          *  Normal ComboBox
12986          */
12987         if(!this.tickable){
12988             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12989             return cfg;
12990         }
12991         
12992         /*
12993          *  ComboBox with tickable selections
12994          */
12995              
12996         var align = this.labelAlign || this.parentLabelAlign();
12997         
12998         cfg = {
12999             cls : 'form-group roo-combobox-tickable' //input-group
13000         };
13001         
13002         var btn_text_select = '';
13003         var btn_text_done = '';
13004         var btn_text_cancel = '';
13005         
13006         if (this.btn_text_show) {
13007             btn_text_select = 'Select';
13008             btn_text_done = 'Done';
13009             btn_text_cancel = 'Cancel'; 
13010         }
13011         
13012         var buttons = {
13013             tag : 'div',
13014             cls : 'tickable-buttons',
13015             cn : [
13016                 {
13017                     tag : 'button',
13018                     type : 'button',
13019                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13020                     //html : this.triggerText
13021                     html: btn_text_select
13022                 },
13023                 {
13024                     tag : 'button',
13025                     type : 'button',
13026                     name : 'ok',
13027                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13028                     //html : 'Done'
13029                     html: btn_text_done
13030                 },
13031                 {
13032                     tag : 'button',
13033                     type : 'button',
13034                     name : 'cancel',
13035                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13036                     //html : 'Cancel'
13037                     html: btn_text_cancel
13038                 }
13039             ]
13040         };
13041         
13042         if(this.editable){
13043             buttons.cn.unshift({
13044                 tag: 'input',
13045                 cls: 'roo-select2-search-field-input'
13046             });
13047         }
13048         
13049         var _this = this;
13050         
13051         Roo.each(buttons.cn, function(c){
13052             if (_this.size) {
13053                 c.cls += ' btn-' + _this.size;
13054             }
13055
13056             if (_this.disabled) {
13057                 c.disabled = true;
13058             }
13059         });
13060         
13061         var box = {
13062             tag: 'div',
13063             cn: [
13064                 {
13065                     tag: 'input',
13066                     type : 'hidden',
13067                     cls: 'form-hidden-field'
13068                 },
13069                 {
13070                     tag: 'ul',
13071                     cls: 'roo-select2-choices',
13072                     cn:[
13073                         {
13074                             tag: 'li',
13075                             cls: 'roo-select2-search-field',
13076                             cn: [
13077                                 buttons
13078                             ]
13079                         }
13080                     ]
13081                 }
13082             ]
13083         };
13084         
13085         var combobox = {
13086             cls: 'roo-select2-container input-group roo-select2-container-multi',
13087             cn: [
13088                 box
13089 //                {
13090 //                    tag: 'ul',
13091 //                    cls: 'typeahead typeahead-long dropdown-menu',
13092 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13093 //                }
13094             ]
13095         };
13096         
13097         if(this.hasFeedback && !this.allowBlank){
13098             
13099             var feedback = {
13100                 tag: 'span',
13101                 cls: 'glyphicon form-control-feedback'
13102             };
13103
13104             combobox.cn.push(feedback);
13105         }
13106         
13107         
13108         if (align ==='left' && this.fieldLabel.length) {
13109             
13110             cfg.cls += ' roo-form-group-label-left';
13111             
13112             cfg.cn = [
13113                 {
13114                     tag : 'i',
13115                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13116                     tooltip : 'This field is required'
13117                 },
13118                 {
13119                     tag: 'label',
13120                     'for' :  id,
13121                     cls : 'control-label',
13122                     html : this.fieldLabel
13123
13124                 },
13125                 {
13126                     cls : "", 
13127                     cn: [
13128                         combobox
13129                     ]
13130                 }
13131
13132             ];
13133             
13134             var labelCfg = cfg.cn[1];
13135             var contentCfg = cfg.cn[2];
13136             
13137
13138             if(this.indicatorpos == 'right'){
13139                 
13140                 cfg.cn = [
13141                     {
13142                         tag: 'label',
13143                         'for' :  id,
13144                         cls : 'control-label',
13145                         cn : [
13146                             {
13147                                 tag : 'span',
13148                                 html : this.fieldLabel
13149                             },
13150                             {
13151                                 tag : 'i',
13152                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13153                                 tooltip : 'This field is required'
13154                             }
13155                         ]
13156                     },
13157                     {
13158                         cls : "",
13159                         cn: [
13160                             combobox
13161                         ]
13162                     }
13163
13164                 ];
13165                 
13166                 
13167                 
13168                 labelCfg = cfg.cn[0];
13169                 contentCfg = cfg.cn[1];
13170             
13171             }
13172             
13173             if(this.labelWidth > 12){
13174                 labelCfg.style = "width: " + this.labelWidth + 'px';
13175             }
13176             
13177             if(this.labelWidth < 13 && this.labelmd == 0){
13178                 this.labelmd = this.labelWidth;
13179             }
13180             
13181             if(this.labellg > 0){
13182                 labelCfg.cls += ' col-lg-' + this.labellg;
13183                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13184             }
13185             
13186             if(this.labelmd > 0){
13187                 labelCfg.cls += ' col-md-' + this.labelmd;
13188                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13189             }
13190             
13191             if(this.labelsm > 0){
13192                 labelCfg.cls += ' col-sm-' + this.labelsm;
13193                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13194             }
13195             
13196             if(this.labelxs > 0){
13197                 labelCfg.cls += ' col-xs-' + this.labelxs;
13198                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13199             }
13200                 
13201                 
13202         } else if ( this.fieldLabel.length) {
13203 //                Roo.log(" label");
13204                  cfg.cn = [
13205                     {
13206                         tag : 'i',
13207                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13208                         tooltip : 'This field is required'
13209                     },
13210                     {
13211                         tag: 'label',
13212                         //cls : 'input-group-addon',
13213                         html : this.fieldLabel
13214                     },
13215                     combobox
13216                 ];
13217                 
13218                 if(this.indicatorpos == 'right'){
13219                     cfg.cn = [
13220                         {
13221                             tag: 'label',
13222                             //cls : 'input-group-addon',
13223                             html : this.fieldLabel
13224                         },
13225                         {
13226                             tag : 'i',
13227                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13228                             tooltip : 'This field is required'
13229                         },
13230                         combobox
13231                     ];
13232                     
13233                 }
13234
13235         } else {
13236             
13237 //                Roo.log(" no label && no align");
13238                 cfg = combobox
13239                      
13240                 
13241         }
13242          
13243         var settings=this;
13244         ['xs','sm','md','lg'].map(function(size){
13245             if (settings[size]) {
13246                 cfg.cls += ' col-' + size + '-' + settings[size];
13247             }
13248         });
13249         
13250         return cfg;
13251         
13252     },
13253     
13254     _initEventsCalled : false,
13255     
13256     // private
13257     initEvents: function()
13258     {   
13259         if (this._initEventsCalled) { // as we call render... prevent looping...
13260             return;
13261         }
13262         this._initEventsCalled = true;
13263         
13264         if (!this.store) {
13265             throw "can not find store for combo";
13266         }
13267         
13268         this.indicator = this.indicatorEl();
13269         
13270         this.store = Roo.factory(this.store, Roo.data);
13271         this.store.parent = this;
13272         
13273         // if we are building from html. then this element is so complex, that we can not really
13274         // use the rendered HTML.
13275         // so we have to trash and replace the previous code.
13276         if (Roo.XComponent.build_from_html) {
13277             // remove this element....
13278             var e = this.el.dom, k=0;
13279             while (e ) { e = e.previousSibling;  ++k;}
13280
13281             this.el.remove();
13282             
13283             this.el=false;
13284             this.rendered = false;
13285             
13286             this.render(this.parent().getChildContainer(true), k);
13287         }
13288         
13289         if(Roo.isIOS && this.useNativeIOS){
13290             this.initIOSView();
13291             return;
13292         }
13293         
13294         /*
13295          * Touch Devices
13296          */
13297         
13298         if(Roo.isTouch && this.mobileTouchView){
13299             this.initTouchView();
13300             return;
13301         }
13302         
13303         if(this.tickable){
13304             this.initTickableEvents();
13305             return;
13306         }
13307         
13308         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13309         
13310         if(this.hiddenName){
13311             
13312             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13313             
13314             this.hiddenField.dom.value =
13315                 this.hiddenValue !== undefined ? this.hiddenValue :
13316                 this.value !== undefined ? this.value : '';
13317
13318             // prevent input submission
13319             this.el.dom.removeAttribute('name');
13320             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13321              
13322              
13323         }
13324         //if(Roo.isGecko){
13325         //    this.el.dom.setAttribute('autocomplete', 'off');
13326         //}
13327         
13328         var cls = 'x-combo-list';
13329         
13330         //this.list = new Roo.Layer({
13331         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13332         //});
13333         
13334         var _this = this;
13335         
13336         (function(){
13337             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13338             _this.list.setWidth(lw);
13339         }).defer(100);
13340         
13341         this.list.on('mouseover', this.onViewOver, this);
13342         this.list.on('mousemove', this.onViewMove, this);
13343         this.list.on('scroll', this.onViewScroll, this);
13344         
13345         /*
13346         this.list.swallowEvent('mousewheel');
13347         this.assetHeight = 0;
13348
13349         if(this.title){
13350             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13351             this.assetHeight += this.header.getHeight();
13352         }
13353
13354         this.innerList = this.list.createChild({cls:cls+'-inner'});
13355         this.innerList.on('mouseover', this.onViewOver, this);
13356         this.innerList.on('mousemove', this.onViewMove, this);
13357         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13358         
13359         if(this.allowBlank && !this.pageSize && !this.disableClear){
13360             this.footer = this.list.createChild({cls:cls+'-ft'});
13361             this.pageTb = new Roo.Toolbar(this.footer);
13362            
13363         }
13364         if(this.pageSize){
13365             this.footer = this.list.createChild({cls:cls+'-ft'});
13366             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13367                     {pageSize: this.pageSize});
13368             
13369         }
13370         
13371         if (this.pageTb && this.allowBlank && !this.disableClear) {
13372             var _this = this;
13373             this.pageTb.add(new Roo.Toolbar.Fill(), {
13374                 cls: 'x-btn-icon x-btn-clear',
13375                 text: '&#160;',
13376                 handler: function()
13377                 {
13378                     _this.collapse();
13379                     _this.clearValue();
13380                     _this.onSelect(false, -1);
13381                 }
13382             });
13383         }
13384         if (this.footer) {
13385             this.assetHeight += this.footer.getHeight();
13386         }
13387         */
13388             
13389         if(!this.tpl){
13390             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13391         }
13392
13393         this.view = new Roo.View(this.list, this.tpl, {
13394             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13395         });
13396         //this.view.wrapEl.setDisplayed(false);
13397         this.view.on('click', this.onViewClick, this);
13398         
13399         
13400         this.store.on('beforeload', this.onBeforeLoad, this);
13401         this.store.on('load', this.onLoad, this);
13402         this.store.on('loadexception', this.onLoadException, this);
13403         /*
13404         if(this.resizable){
13405             this.resizer = new Roo.Resizable(this.list,  {
13406                pinned:true, handles:'se'
13407             });
13408             this.resizer.on('resize', function(r, w, h){
13409                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13410                 this.listWidth = w;
13411                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13412                 this.restrictHeight();
13413             }, this);
13414             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13415         }
13416         */
13417         if(!this.editable){
13418             this.editable = true;
13419             this.setEditable(false);
13420         }
13421         
13422         /*
13423         
13424         if (typeof(this.events.add.listeners) != 'undefined') {
13425             
13426             this.addicon = this.wrap.createChild(
13427                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13428        
13429             this.addicon.on('click', function(e) {
13430                 this.fireEvent('add', this);
13431             }, this);
13432         }
13433         if (typeof(this.events.edit.listeners) != 'undefined') {
13434             
13435             this.editicon = this.wrap.createChild(
13436                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13437             if (this.addicon) {
13438                 this.editicon.setStyle('margin-left', '40px');
13439             }
13440             this.editicon.on('click', function(e) {
13441                 
13442                 // we fire even  if inothing is selected..
13443                 this.fireEvent('edit', this, this.lastData );
13444                 
13445             }, this);
13446         }
13447         */
13448         
13449         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13450             "up" : function(e){
13451                 this.inKeyMode = true;
13452                 this.selectPrev();
13453             },
13454
13455             "down" : function(e){
13456                 if(!this.isExpanded()){
13457                     this.onTriggerClick();
13458                 }else{
13459                     this.inKeyMode = true;
13460                     this.selectNext();
13461                 }
13462             },
13463
13464             "enter" : function(e){
13465 //                this.onViewClick();
13466                 //return true;
13467                 this.collapse();
13468                 
13469                 if(this.fireEvent("specialkey", this, e)){
13470                     this.onViewClick(false);
13471                 }
13472                 
13473                 return true;
13474             },
13475
13476             "esc" : function(e){
13477                 this.collapse();
13478             },
13479
13480             "tab" : function(e){
13481                 this.collapse();
13482                 
13483                 if(this.fireEvent("specialkey", this, e)){
13484                     this.onViewClick(false);
13485                 }
13486                 
13487                 return true;
13488             },
13489
13490             scope : this,
13491
13492             doRelay : function(foo, bar, hname){
13493                 if(hname == 'down' || this.scope.isExpanded()){
13494                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13495                 }
13496                 return true;
13497             },
13498
13499             forceKeyDown: true
13500         });
13501         
13502         
13503         this.queryDelay = Math.max(this.queryDelay || 10,
13504                 this.mode == 'local' ? 10 : 250);
13505         
13506         
13507         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13508         
13509         if(this.typeAhead){
13510             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13511         }
13512         if(this.editable !== false){
13513             this.inputEl().on("keyup", this.onKeyUp, this);
13514         }
13515         if(this.forceSelection){
13516             this.inputEl().on('blur', this.doForce, this);
13517         }
13518         
13519         if(this.multiple){
13520             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13521             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13522         }
13523     },
13524     
13525     initTickableEvents: function()
13526     {   
13527         this.createList();
13528         
13529         if(this.hiddenName){
13530             
13531             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13532             
13533             this.hiddenField.dom.value =
13534                 this.hiddenValue !== undefined ? this.hiddenValue :
13535                 this.value !== undefined ? this.value : '';
13536
13537             // prevent input submission
13538             this.el.dom.removeAttribute('name');
13539             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13540              
13541              
13542         }
13543         
13544 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13545         
13546         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13547         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13548         if(this.triggerList){
13549             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13550         }
13551          
13552         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13553         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13554         
13555         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13556         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13557         
13558         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13559         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13560         
13561         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13562         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13563         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13564         
13565         this.okBtn.hide();
13566         this.cancelBtn.hide();
13567         
13568         var _this = this;
13569         
13570         (function(){
13571             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13572             _this.list.setWidth(lw);
13573         }).defer(100);
13574         
13575         this.list.on('mouseover', this.onViewOver, this);
13576         this.list.on('mousemove', this.onViewMove, this);
13577         
13578         this.list.on('scroll', this.onViewScroll, this);
13579         
13580         if(!this.tpl){
13581             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>';
13582         }
13583
13584         this.view = new Roo.View(this.list, this.tpl, {
13585             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13586         });
13587         
13588         //this.view.wrapEl.setDisplayed(false);
13589         this.view.on('click', this.onViewClick, this);
13590         
13591         
13592         
13593         this.store.on('beforeload', this.onBeforeLoad, this);
13594         this.store.on('load', this.onLoad, this);
13595         this.store.on('loadexception', this.onLoadException, this);
13596         
13597         if(this.editable){
13598             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13599                 "up" : function(e){
13600                     this.inKeyMode = true;
13601                     this.selectPrev();
13602                 },
13603
13604                 "down" : function(e){
13605                     this.inKeyMode = true;
13606                     this.selectNext();
13607                 },
13608
13609                 "enter" : function(e){
13610                     if(this.fireEvent("specialkey", this, e)){
13611                         this.onViewClick(false);
13612                     }
13613                     
13614                     return true;
13615                 },
13616
13617                 "esc" : function(e){
13618                     this.onTickableFooterButtonClick(e, false, false);
13619                 },
13620
13621                 "tab" : function(e){
13622                     this.fireEvent("specialkey", this, e);
13623                     
13624                     this.onTickableFooterButtonClick(e, false, false);
13625                     
13626                     return true;
13627                 },
13628
13629                 scope : this,
13630
13631                 doRelay : function(e, fn, key){
13632                     if(this.scope.isExpanded()){
13633                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13634                     }
13635                     return true;
13636                 },
13637
13638                 forceKeyDown: true
13639             });
13640         }
13641         
13642         this.queryDelay = Math.max(this.queryDelay || 10,
13643                 this.mode == 'local' ? 10 : 250);
13644         
13645         
13646         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13647         
13648         if(this.typeAhead){
13649             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13650         }
13651         
13652         if(this.editable !== false){
13653             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13654         }
13655         
13656         this.indicator = this.indicatorEl();
13657         
13658         if(this.indicator){
13659             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13660             this.indicator.hide();
13661         }
13662         
13663     },
13664
13665     onDestroy : function(){
13666         if(this.view){
13667             this.view.setStore(null);
13668             this.view.el.removeAllListeners();
13669             this.view.el.remove();
13670             this.view.purgeListeners();
13671         }
13672         if(this.list){
13673             this.list.dom.innerHTML  = '';
13674         }
13675         
13676         if(this.store){
13677             this.store.un('beforeload', this.onBeforeLoad, this);
13678             this.store.un('load', this.onLoad, this);
13679             this.store.un('loadexception', this.onLoadException, this);
13680         }
13681         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13682     },
13683
13684     // private
13685     fireKey : function(e){
13686         if(e.isNavKeyPress() && !this.list.isVisible()){
13687             this.fireEvent("specialkey", this, e);
13688         }
13689     },
13690
13691     // private
13692     onResize: function(w, h){
13693 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13694 //        
13695 //        if(typeof w != 'number'){
13696 //            // we do not handle it!?!?
13697 //            return;
13698 //        }
13699 //        var tw = this.trigger.getWidth();
13700 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13701 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13702 //        var x = w - tw;
13703 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13704 //            
13705 //        //this.trigger.setStyle('left', x+'px');
13706 //        
13707 //        if(this.list && this.listWidth === undefined){
13708 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13709 //            this.list.setWidth(lw);
13710 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13711 //        }
13712         
13713     
13714         
13715     },
13716
13717     /**
13718      * Allow or prevent the user from directly editing the field text.  If false is passed,
13719      * the user will only be able to select from the items defined in the dropdown list.  This method
13720      * is the runtime equivalent of setting the 'editable' config option at config time.
13721      * @param {Boolean} value True to allow the user to directly edit the field text
13722      */
13723     setEditable : function(value){
13724         if(value == this.editable){
13725             return;
13726         }
13727         this.editable = value;
13728         if(!value){
13729             this.inputEl().dom.setAttribute('readOnly', true);
13730             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13731             this.inputEl().addClass('x-combo-noedit');
13732         }else{
13733             this.inputEl().dom.setAttribute('readOnly', false);
13734             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13735             this.inputEl().removeClass('x-combo-noedit');
13736         }
13737     },
13738
13739     // private
13740     
13741     onBeforeLoad : function(combo,opts){
13742         if(!this.hasFocus){
13743             return;
13744         }
13745          if (!opts.add) {
13746             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13747          }
13748         this.restrictHeight();
13749         this.selectedIndex = -1;
13750     },
13751
13752     // private
13753     onLoad : function(){
13754         
13755         this.hasQuery = false;
13756         
13757         if(!this.hasFocus){
13758             return;
13759         }
13760         
13761         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13762             this.loading.hide();
13763         }
13764         
13765         if(this.store.getCount() > 0){
13766             
13767             this.expand();
13768             this.restrictHeight();
13769             if(this.lastQuery == this.allQuery){
13770                 if(this.editable && !this.tickable){
13771                     this.inputEl().dom.select();
13772                 }
13773                 
13774                 if(
13775                     !this.selectByValue(this.value, true) &&
13776                     this.autoFocus && 
13777                     (
13778                         !this.store.lastOptions ||
13779                         typeof(this.store.lastOptions.add) == 'undefined' || 
13780                         this.store.lastOptions.add != true
13781                     )
13782                 ){
13783                     this.select(0, true);
13784                 }
13785             }else{
13786                 if(this.autoFocus){
13787                     this.selectNext();
13788                 }
13789                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13790                     this.taTask.delay(this.typeAheadDelay);
13791                 }
13792             }
13793         }else{
13794             this.onEmptyResults();
13795         }
13796         
13797         //this.el.focus();
13798     },
13799     // private
13800     onLoadException : function()
13801     {
13802         this.hasQuery = false;
13803         
13804         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13805             this.loading.hide();
13806         }
13807         
13808         if(this.tickable && this.editable){
13809             return;
13810         }
13811         
13812         this.collapse();
13813         // only causes errors at present
13814         //Roo.log(this.store.reader.jsonData);
13815         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13816             // fixme
13817             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13818         //}
13819         
13820         
13821     },
13822     // private
13823     onTypeAhead : function(){
13824         if(this.store.getCount() > 0){
13825             var r = this.store.getAt(0);
13826             var newValue = r.data[this.displayField];
13827             var len = newValue.length;
13828             var selStart = this.getRawValue().length;
13829             
13830             if(selStart != len){
13831                 this.setRawValue(newValue);
13832                 this.selectText(selStart, newValue.length);
13833             }
13834         }
13835     },
13836
13837     // private
13838     onSelect : function(record, index){
13839         
13840         if(this.fireEvent('beforeselect', this, record, index) !== false){
13841         
13842             this.setFromData(index > -1 ? record.data : false);
13843             
13844             this.collapse();
13845             this.fireEvent('select', this, record, index);
13846         }
13847     },
13848
13849     /**
13850      * Returns the currently selected field value or empty string if no value is set.
13851      * @return {String} value The selected value
13852      */
13853     getValue : function()
13854     {
13855         if(Roo.isIOS && this.useNativeIOS){
13856             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13857         }
13858         
13859         if(this.multiple){
13860             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13861         }
13862         
13863         if(this.valueField){
13864             return typeof this.value != 'undefined' ? this.value : '';
13865         }else{
13866             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13867         }
13868     },
13869     
13870     getRawValue : function()
13871     {
13872         if(Roo.isIOS && this.useNativeIOS){
13873             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13874         }
13875         
13876         var v = this.inputEl().getValue();
13877         
13878         return v;
13879     },
13880
13881     /**
13882      * Clears any text/value currently set in the field
13883      */
13884     clearValue : function(){
13885         
13886         if(this.hiddenField){
13887             this.hiddenField.dom.value = '';
13888         }
13889         this.value = '';
13890         this.setRawValue('');
13891         this.lastSelectionText = '';
13892         this.lastData = false;
13893         
13894         var close = this.closeTriggerEl();
13895         
13896         if(close){
13897             close.hide();
13898         }
13899         
13900         this.validate();
13901         
13902     },
13903
13904     /**
13905      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13906      * will be displayed in the field.  If the value does not match the data value of an existing item,
13907      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13908      * Otherwise the field will be blank (although the value will still be set).
13909      * @param {String} value The value to match
13910      */
13911     setValue : function(v)
13912     {
13913         if(Roo.isIOS && this.useNativeIOS){
13914             this.setIOSValue(v);
13915             return;
13916         }
13917         
13918         if(this.multiple){
13919             this.syncValue();
13920             return;
13921         }
13922         
13923         var text = v;
13924         if(this.valueField){
13925             var r = this.findRecord(this.valueField, v);
13926             if(r){
13927                 text = r.data[this.displayField];
13928             }else if(this.valueNotFoundText !== undefined){
13929                 text = this.valueNotFoundText;
13930             }
13931         }
13932         this.lastSelectionText = text;
13933         if(this.hiddenField){
13934             this.hiddenField.dom.value = v;
13935         }
13936         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13937         this.value = v;
13938         
13939         var close = this.closeTriggerEl();
13940         
13941         if(close){
13942             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13943         }
13944         
13945         this.validate();
13946     },
13947     /**
13948      * @property {Object} the last set data for the element
13949      */
13950     
13951     lastData : false,
13952     /**
13953      * Sets the value of the field based on a object which is related to the record format for the store.
13954      * @param {Object} value the value to set as. or false on reset?
13955      */
13956     setFromData : function(o){
13957         
13958         if(this.multiple){
13959             this.addItem(o);
13960             return;
13961         }
13962             
13963         var dv = ''; // display value
13964         var vv = ''; // value value..
13965         this.lastData = o;
13966         if (this.displayField) {
13967             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13968         } else {
13969             // this is an error condition!!!
13970             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13971         }
13972         
13973         if(this.valueField){
13974             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13975         }
13976         
13977         var close = this.closeTriggerEl();
13978         
13979         if(close){
13980             if(dv.length || vv * 1 > 0){
13981                 close.show() ;
13982                 this.blockFocus=true;
13983             } else {
13984                 close.hide();
13985             }             
13986         }
13987         
13988         if(this.hiddenField){
13989             this.hiddenField.dom.value = vv;
13990             
13991             this.lastSelectionText = dv;
13992             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13993             this.value = vv;
13994             return;
13995         }
13996         // no hidden field.. - we store the value in 'value', but still display
13997         // display field!!!!
13998         this.lastSelectionText = dv;
13999         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14000         this.value = vv;
14001         
14002         
14003         
14004     },
14005     // private
14006     reset : function(){
14007         // overridden so that last data is reset..
14008         
14009         if(this.multiple){
14010             this.clearItem();
14011             return;
14012         }
14013         
14014         this.setValue(this.originalValue);
14015         //this.clearInvalid();
14016         this.lastData = false;
14017         if (this.view) {
14018             this.view.clearSelections();
14019         }
14020         
14021         this.validate();
14022     },
14023     // private
14024     findRecord : function(prop, value){
14025         var record;
14026         if(this.store.getCount() > 0){
14027             this.store.each(function(r){
14028                 if(r.data[prop] == value){
14029                     record = r;
14030                     return false;
14031                 }
14032                 return true;
14033             });
14034         }
14035         return record;
14036     },
14037     
14038     getName: function()
14039     {
14040         // returns hidden if it's set..
14041         if (!this.rendered) {return ''};
14042         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14043         
14044     },
14045     // private
14046     onViewMove : function(e, t){
14047         this.inKeyMode = false;
14048     },
14049
14050     // private
14051     onViewOver : function(e, t){
14052         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14053             return;
14054         }
14055         var item = this.view.findItemFromChild(t);
14056         
14057         if(item){
14058             var index = this.view.indexOf(item);
14059             this.select(index, false);
14060         }
14061     },
14062
14063     // private
14064     onViewClick : function(view, doFocus, el, e)
14065     {
14066         var index = this.view.getSelectedIndexes()[0];
14067         
14068         var r = this.store.getAt(index);
14069         
14070         if(this.tickable){
14071             
14072             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14073                 return;
14074             }
14075             
14076             var rm = false;
14077             var _this = this;
14078             
14079             Roo.each(this.tickItems, function(v,k){
14080                 
14081                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14082                     Roo.log(v);
14083                     _this.tickItems.splice(k, 1);
14084                     
14085                     if(typeof(e) == 'undefined' && view == false){
14086                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14087                     }
14088                     
14089                     rm = true;
14090                     return;
14091                 }
14092             });
14093             
14094             if(rm){
14095                 return;
14096             }
14097             
14098             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14099                 this.tickItems.push(r.data);
14100             }
14101             
14102             if(typeof(e) == 'undefined' && view == false){
14103                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14104             }
14105                     
14106             return;
14107         }
14108         
14109         if(r){
14110             this.onSelect(r, index);
14111         }
14112         if(doFocus !== false && !this.blockFocus){
14113             this.inputEl().focus();
14114         }
14115     },
14116
14117     // private
14118     restrictHeight : function(){
14119         //this.innerList.dom.style.height = '';
14120         //var inner = this.innerList.dom;
14121         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14122         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14123         //this.list.beginUpdate();
14124         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14125         this.list.alignTo(this.inputEl(), this.listAlign);
14126         this.list.alignTo(this.inputEl(), this.listAlign);
14127         //this.list.endUpdate();
14128     },
14129
14130     // private
14131     onEmptyResults : function(){
14132         
14133         if(this.tickable && this.editable){
14134             this.hasFocus = false;
14135             this.restrictHeight();
14136             return;
14137         }
14138         
14139         this.collapse();
14140     },
14141
14142     /**
14143      * Returns true if the dropdown list is expanded, else false.
14144      */
14145     isExpanded : function(){
14146         return this.list.isVisible();
14147     },
14148
14149     /**
14150      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14151      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14152      * @param {String} value The data value of the item to select
14153      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14154      * selected item if it is not currently in view (defaults to true)
14155      * @return {Boolean} True if the value matched an item in the list, else false
14156      */
14157     selectByValue : function(v, scrollIntoView){
14158         if(v !== undefined && v !== null){
14159             var r = this.findRecord(this.valueField || this.displayField, v);
14160             if(r){
14161                 this.select(this.store.indexOf(r), scrollIntoView);
14162                 return true;
14163             }
14164         }
14165         return false;
14166     },
14167
14168     /**
14169      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14170      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14171      * @param {Number} index The zero-based index of the list item to select
14172      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14173      * selected item if it is not currently in view (defaults to true)
14174      */
14175     select : function(index, scrollIntoView){
14176         this.selectedIndex = index;
14177         this.view.select(index);
14178         if(scrollIntoView !== false){
14179             var el = this.view.getNode(index);
14180             /*
14181              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14182              */
14183             if(el){
14184                 this.list.scrollChildIntoView(el, false);
14185             }
14186         }
14187     },
14188
14189     // private
14190     selectNext : function(){
14191         var ct = this.store.getCount();
14192         if(ct > 0){
14193             if(this.selectedIndex == -1){
14194                 this.select(0);
14195             }else if(this.selectedIndex < ct-1){
14196                 this.select(this.selectedIndex+1);
14197             }
14198         }
14199     },
14200
14201     // private
14202     selectPrev : function(){
14203         var ct = this.store.getCount();
14204         if(ct > 0){
14205             if(this.selectedIndex == -1){
14206                 this.select(0);
14207             }else if(this.selectedIndex != 0){
14208                 this.select(this.selectedIndex-1);
14209             }
14210         }
14211     },
14212
14213     // private
14214     onKeyUp : function(e){
14215         if(this.editable !== false && !e.isSpecialKey()){
14216             this.lastKey = e.getKey();
14217             this.dqTask.delay(this.queryDelay);
14218         }
14219     },
14220
14221     // private
14222     validateBlur : function(){
14223         return !this.list || !this.list.isVisible();   
14224     },
14225
14226     // private
14227     initQuery : function(){
14228         
14229         var v = this.getRawValue();
14230         
14231         if(this.tickable && this.editable){
14232             v = this.tickableInputEl().getValue();
14233         }
14234         
14235         this.doQuery(v);
14236     },
14237
14238     // private
14239     doForce : function(){
14240         if(this.inputEl().dom.value.length > 0){
14241             this.inputEl().dom.value =
14242                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14243              
14244         }
14245     },
14246
14247     /**
14248      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14249      * query allowing the query action to be canceled if needed.
14250      * @param {String} query The SQL query to execute
14251      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14252      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14253      * saved in the current store (defaults to false)
14254      */
14255     doQuery : function(q, forceAll){
14256         
14257         if(q === undefined || q === null){
14258             q = '';
14259         }
14260         var qe = {
14261             query: q,
14262             forceAll: forceAll,
14263             combo: this,
14264             cancel:false
14265         };
14266         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14267             return false;
14268         }
14269         q = qe.query;
14270         
14271         forceAll = qe.forceAll;
14272         if(forceAll === true || (q.length >= this.minChars)){
14273             
14274             this.hasQuery = true;
14275             
14276             if(this.lastQuery != q || this.alwaysQuery){
14277                 this.lastQuery = q;
14278                 if(this.mode == 'local'){
14279                     this.selectedIndex = -1;
14280                     if(forceAll){
14281                         this.store.clearFilter();
14282                     }else{
14283                         
14284                         if(this.specialFilter){
14285                             this.fireEvent('specialfilter', this);
14286                             this.onLoad();
14287                             return;
14288                         }
14289                         
14290                         this.store.filter(this.displayField, q);
14291                     }
14292                     
14293                     this.store.fireEvent("datachanged", this.store);
14294                     
14295                     this.onLoad();
14296                     
14297                     
14298                 }else{
14299                     
14300                     this.store.baseParams[this.queryParam] = q;
14301                     
14302                     var options = {params : this.getParams(q)};
14303                     
14304                     if(this.loadNext){
14305                         options.add = true;
14306                         options.params.start = this.page * this.pageSize;
14307                     }
14308                     
14309                     this.store.load(options);
14310                     
14311                     /*
14312                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14313                      *  we should expand the list on onLoad
14314                      *  so command out it
14315                      */
14316 //                    this.expand();
14317                 }
14318             }else{
14319                 this.selectedIndex = -1;
14320                 this.onLoad();   
14321             }
14322         }
14323         
14324         this.loadNext = false;
14325     },
14326     
14327     // private
14328     getParams : function(q){
14329         var p = {};
14330         //p[this.queryParam] = q;
14331         
14332         if(this.pageSize){
14333             p.start = 0;
14334             p.limit = this.pageSize;
14335         }
14336         return p;
14337     },
14338
14339     /**
14340      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14341      */
14342     collapse : function(){
14343         if(!this.isExpanded()){
14344             return;
14345         }
14346         
14347         this.list.hide();
14348         
14349         this.hasFocus = false;
14350         
14351         if(this.tickable){
14352             this.okBtn.hide();
14353             this.cancelBtn.hide();
14354             this.trigger.show();
14355             
14356             if(this.editable){
14357                 this.tickableInputEl().dom.value = '';
14358                 this.tickableInputEl().blur();
14359             }
14360             
14361         }
14362         
14363         Roo.get(document).un('mousedown', this.collapseIf, this);
14364         Roo.get(document).un('mousewheel', this.collapseIf, this);
14365         if (!this.editable) {
14366             Roo.get(document).un('keydown', this.listKeyPress, this);
14367         }
14368         this.fireEvent('collapse', this);
14369         
14370         this.validate();
14371     },
14372
14373     // private
14374     collapseIf : function(e){
14375         var in_combo  = e.within(this.el);
14376         var in_list =  e.within(this.list);
14377         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14378         
14379         if (in_combo || in_list || is_list) {
14380             //e.stopPropagation();
14381             return;
14382         }
14383         
14384         if(this.tickable){
14385             this.onTickableFooterButtonClick(e, false, false);
14386         }
14387
14388         this.collapse();
14389         
14390     },
14391
14392     /**
14393      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14394      */
14395     expand : function(){
14396        
14397         if(this.isExpanded() || !this.hasFocus){
14398             return;
14399         }
14400         
14401         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14402         this.list.setWidth(lw);
14403         
14404         Roo.log('expand');
14405         
14406         this.list.show();
14407         
14408         this.restrictHeight();
14409         
14410         if(this.tickable){
14411             
14412             this.tickItems = Roo.apply([], this.item);
14413             
14414             this.okBtn.show();
14415             this.cancelBtn.show();
14416             this.trigger.hide();
14417             
14418             if(this.editable){
14419                 this.tickableInputEl().focus();
14420             }
14421             
14422         }
14423         
14424         Roo.get(document).on('mousedown', this.collapseIf, this);
14425         Roo.get(document).on('mousewheel', this.collapseIf, this);
14426         if (!this.editable) {
14427             Roo.get(document).on('keydown', this.listKeyPress, this);
14428         }
14429         
14430         this.fireEvent('expand', this);
14431     },
14432
14433     // private
14434     // Implements the default empty TriggerField.onTriggerClick function
14435     onTriggerClick : function(e)
14436     {
14437         Roo.log('trigger click');
14438         
14439         if(this.disabled || !this.triggerList){
14440             return;
14441         }
14442         
14443         this.page = 0;
14444         this.loadNext = false;
14445         
14446         if(this.isExpanded()){
14447             this.collapse();
14448             if (!this.blockFocus) {
14449                 this.inputEl().focus();
14450             }
14451             
14452         }else {
14453             this.hasFocus = true;
14454             if(this.triggerAction == 'all') {
14455                 this.doQuery(this.allQuery, true);
14456             } else {
14457                 this.doQuery(this.getRawValue());
14458             }
14459             if (!this.blockFocus) {
14460                 this.inputEl().focus();
14461             }
14462         }
14463     },
14464     
14465     onTickableTriggerClick : function(e)
14466     {
14467         if(this.disabled){
14468             return;
14469         }
14470         
14471         this.page = 0;
14472         this.loadNext = false;
14473         this.hasFocus = true;
14474         
14475         if(this.triggerAction == 'all') {
14476             this.doQuery(this.allQuery, true);
14477         } else {
14478             this.doQuery(this.getRawValue());
14479         }
14480     },
14481     
14482     onSearchFieldClick : function(e)
14483     {
14484         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14485             this.onTickableFooterButtonClick(e, false, false);
14486             return;
14487         }
14488         
14489         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14490             return;
14491         }
14492         
14493         this.page = 0;
14494         this.loadNext = false;
14495         this.hasFocus = true;
14496         
14497         if(this.triggerAction == 'all') {
14498             this.doQuery(this.allQuery, true);
14499         } else {
14500             this.doQuery(this.getRawValue());
14501         }
14502     },
14503     
14504     listKeyPress : function(e)
14505     {
14506         //Roo.log('listkeypress');
14507         // scroll to first matching element based on key pres..
14508         if (e.isSpecialKey()) {
14509             return false;
14510         }
14511         var k = String.fromCharCode(e.getKey()).toUpperCase();
14512         //Roo.log(k);
14513         var match  = false;
14514         var csel = this.view.getSelectedNodes();
14515         var cselitem = false;
14516         if (csel.length) {
14517             var ix = this.view.indexOf(csel[0]);
14518             cselitem  = this.store.getAt(ix);
14519             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14520                 cselitem = false;
14521             }
14522             
14523         }
14524         
14525         this.store.each(function(v) { 
14526             if (cselitem) {
14527                 // start at existing selection.
14528                 if (cselitem.id == v.id) {
14529                     cselitem = false;
14530                 }
14531                 return true;
14532             }
14533                 
14534             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14535                 match = this.store.indexOf(v);
14536                 return false;
14537             }
14538             return true;
14539         }, this);
14540         
14541         if (match === false) {
14542             return true; // no more action?
14543         }
14544         // scroll to?
14545         this.view.select(match);
14546         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14547         sn.scrollIntoView(sn.dom.parentNode, false);
14548     },
14549     
14550     onViewScroll : function(e, t){
14551         
14552         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){
14553             return;
14554         }
14555         
14556         this.hasQuery = true;
14557         
14558         this.loading = this.list.select('.loading', true).first();
14559         
14560         if(this.loading === null){
14561             this.list.createChild({
14562                 tag: 'div',
14563                 cls: 'loading roo-select2-more-results roo-select2-active',
14564                 html: 'Loading more results...'
14565             });
14566             
14567             this.loading = this.list.select('.loading', true).first();
14568             
14569             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14570             
14571             this.loading.hide();
14572         }
14573         
14574         this.loading.show();
14575         
14576         var _combo = this;
14577         
14578         this.page++;
14579         this.loadNext = true;
14580         
14581         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14582         
14583         return;
14584     },
14585     
14586     addItem : function(o)
14587     {   
14588         var dv = ''; // display value
14589         
14590         if (this.displayField) {
14591             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14592         } else {
14593             // this is an error condition!!!
14594             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14595         }
14596         
14597         if(!dv.length){
14598             return;
14599         }
14600         
14601         var choice = this.choices.createChild({
14602             tag: 'li',
14603             cls: 'roo-select2-search-choice',
14604             cn: [
14605                 {
14606                     tag: 'div',
14607                     html: dv
14608                 },
14609                 {
14610                     tag: 'a',
14611                     href: '#',
14612                     cls: 'roo-select2-search-choice-close fa fa-times',
14613                     tabindex: '-1'
14614                 }
14615             ]
14616             
14617         }, this.searchField);
14618         
14619         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14620         
14621         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14622         
14623         this.item.push(o);
14624         
14625         this.lastData = o;
14626         
14627         this.syncValue();
14628         
14629         this.inputEl().dom.value = '';
14630         
14631         this.validate();
14632     },
14633     
14634     onRemoveItem : function(e, _self, o)
14635     {
14636         e.preventDefault();
14637         
14638         this.lastItem = Roo.apply([], this.item);
14639         
14640         var index = this.item.indexOf(o.data) * 1;
14641         
14642         if( index < 0){
14643             Roo.log('not this item?!');
14644             return;
14645         }
14646         
14647         this.item.splice(index, 1);
14648         o.item.remove();
14649         
14650         this.syncValue();
14651         
14652         this.fireEvent('remove', this, e);
14653         
14654         this.validate();
14655         
14656     },
14657     
14658     syncValue : function()
14659     {
14660         if(!this.item.length){
14661             this.clearValue();
14662             return;
14663         }
14664             
14665         var value = [];
14666         var _this = this;
14667         Roo.each(this.item, function(i){
14668             if(_this.valueField){
14669                 value.push(i[_this.valueField]);
14670                 return;
14671             }
14672
14673             value.push(i);
14674         });
14675
14676         this.value = value.join(',');
14677
14678         if(this.hiddenField){
14679             this.hiddenField.dom.value = this.value;
14680         }
14681         
14682         this.store.fireEvent("datachanged", this.store);
14683         
14684         this.validate();
14685     },
14686     
14687     clearItem : function()
14688     {
14689         if(!this.multiple){
14690             return;
14691         }
14692         
14693         this.item = [];
14694         
14695         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14696            c.remove();
14697         });
14698         
14699         this.syncValue();
14700         
14701         this.validate();
14702         
14703         if(this.tickable && !Roo.isTouch){
14704             this.view.refresh();
14705         }
14706     },
14707     
14708     inputEl: function ()
14709     {
14710         if(Roo.isIOS && this.useNativeIOS){
14711             return this.el.select('select.roo-ios-select', true).first();
14712         }
14713         
14714         if(Roo.isTouch && this.mobileTouchView){
14715             return this.el.select('input.form-control',true).first();
14716         }
14717         
14718         if(this.tickable){
14719             return this.searchField;
14720         }
14721         
14722         return this.el.select('input.form-control',true).first();
14723     },
14724     
14725     onTickableFooterButtonClick : function(e, btn, el)
14726     {
14727         e.preventDefault();
14728         
14729         this.lastItem = Roo.apply([], this.item);
14730         
14731         if(btn && btn.name == 'cancel'){
14732             this.tickItems = Roo.apply([], this.item);
14733             this.collapse();
14734             return;
14735         }
14736         
14737         this.clearItem();
14738         
14739         var _this = this;
14740         
14741         Roo.each(this.tickItems, function(o){
14742             _this.addItem(o);
14743         });
14744         
14745         this.collapse();
14746         
14747     },
14748     
14749     validate : function()
14750     {
14751         if(this.getVisibilityEl().hasClass('hidden')){
14752             return true;
14753         }
14754         
14755         var v = this.getRawValue();
14756         
14757         if(this.multiple){
14758             v = this.getValue();
14759         }
14760         
14761         if(this.disabled || this.allowBlank || v.length){
14762             this.markValid();
14763             return true;
14764         }
14765         
14766         this.markInvalid();
14767         return false;
14768     },
14769     
14770     tickableInputEl : function()
14771     {
14772         if(!this.tickable || !this.editable){
14773             return this.inputEl();
14774         }
14775         
14776         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14777     },
14778     
14779     
14780     getAutoCreateTouchView : function()
14781     {
14782         var id = Roo.id();
14783         
14784         var cfg = {
14785             cls: 'form-group' //input-group
14786         };
14787         
14788         var input =  {
14789             tag: 'input',
14790             id : id,
14791             type : this.inputType,
14792             cls : 'form-control x-combo-noedit',
14793             autocomplete: 'new-password',
14794             placeholder : this.placeholder || '',
14795             readonly : true
14796         };
14797         
14798         if (this.name) {
14799             input.name = this.name;
14800         }
14801         
14802         if (this.size) {
14803             input.cls += ' input-' + this.size;
14804         }
14805         
14806         if (this.disabled) {
14807             input.disabled = true;
14808         }
14809         
14810         var inputblock = {
14811             cls : '',
14812             cn : [
14813                 input
14814             ]
14815         };
14816         
14817         if(this.before){
14818             inputblock.cls += ' input-group';
14819             
14820             inputblock.cn.unshift({
14821                 tag :'span',
14822                 cls : 'input-group-addon',
14823                 html : this.before
14824             });
14825         }
14826         
14827         if(this.removable && !this.multiple){
14828             inputblock.cls += ' roo-removable';
14829             
14830             inputblock.cn.push({
14831                 tag: 'button',
14832                 html : 'x',
14833                 cls : 'roo-combo-removable-btn close'
14834             });
14835         }
14836
14837         if(this.hasFeedback && !this.allowBlank){
14838             
14839             inputblock.cls += ' has-feedback';
14840             
14841             inputblock.cn.push({
14842                 tag: 'span',
14843                 cls: 'glyphicon form-control-feedback'
14844             });
14845             
14846         }
14847         
14848         if (this.after) {
14849             
14850             inputblock.cls += (this.before) ? '' : ' input-group';
14851             
14852             inputblock.cn.push({
14853                 tag :'span',
14854                 cls : 'input-group-addon',
14855                 html : this.after
14856             });
14857         }
14858
14859         var box = {
14860             tag: 'div',
14861             cn: [
14862                 {
14863                     tag: 'input',
14864                     type : 'hidden',
14865                     cls: 'form-hidden-field'
14866                 },
14867                 inputblock
14868             ]
14869             
14870         };
14871         
14872         if(this.multiple){
14873             box = {
14874                 tag: 'div',
14875                 cn: [
14876                     {
14877                         tag: 'input',
14878                         type : 'hidden',
14879                         cls: 'form-hidden-field'
14880                     },
14881                     {
14882                         tag: 'ul',
14883                         cls: 'roo-select2-choices',
14884                         cn:[
14885                             {
14886                                 tag: 'li',
14887                                 cls: 'roo-select2-search-field',
14888                                 cn: [
14889
14890                                     inputblock
14891                                 ]
14892                             }
14893                         ]
14894                     }
14895                 ]
14896             }
14897         };
14898         
14899         var combobox = {
14900             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14901             cn: [
14902                 box
14903             ]
14904         };
14905         
14906         if(!this.multiple && this.showToggleBtn){
14907             
14908             var caret = {
14909                         tag: 'span',
14910                         cls: 'caret'
14911             };
14912             
14913             if (this.caret != false) {
14914                 caret = {
14915                      tag: 'i',
14916                      cls: 'fa fa-' + this.caret
14917                 };
14918                 
14919             }
14920             
14921             combobox.cn.push({
14922                 tag :'span',
14923                 cls : 'input-group-addon btn dropdown-toggle',
14924                 cn : [
14925                     caret,
14926                     {
14927                         tag: 'span',
14928                         cls: 'combobox-clear',
14929                         cn  : [
14930                             {
14931                                 tag : 'i',
14932                                 cls: 'icon-remove'
14933                             }
14934                         ]
14935                     }
14936                 ]
14937
14938             })
14939         }
14940         
14941         if(this.multiple){
14942             combobox.cls += ' roo-select2-container-multi';
14943         }
14944         
14945         var align = this.labelAlign || this.parentLabelAlign();
14946         
14947         if (align ==='left' && this.fieldLabel.length) {
14948
14949             cfg.cn = [
14950                 {
14951                    tag : 'i',
14952                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14953                    tooltip : 'This field is required'
14954                 },
14955                 {
14956                     tag: 'label',
14957                     cls : 'control-label',
14958                     html : this.fieldLabel
14959
14960                 },
14961                 {
14962                     cls : '', 
14963                     cn: [
14964                         combobox
14965                     ]
14966                 }
14967             ];
14968             
14969             var labelCfg = cfg.cn[1];
14970             var contentCfg = cfg.cn[2];
14971             
14972
14973             if(this.indicatorpos == 'right'){
14974                 cfg.cn = [
14975                     {
14976                         tag: 'label',
14977                         'for' :  id,
14978                         cls : 'control-label',
14979                         cn : [
14980                             {
14981                                 tag : 'span',
14982                                 html : this.fieldLabel
14983                             },
14984                             {
14985                                 tag : 'i',
14986                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14987                                 tooltip : 'This field is required'
14988                             }
14989                         ]
14990                     },
14991                     {
14992                         cls : "",
14993                         cn: [
14994                             combobox
14995                         ]
14996                     }
14997
14998                 ];
14999                 
15000                 labelCfg = cfg.cn[0];
15001                 contentCfg = cfg.cn[1];
15002             }
15003             
15004            
15005             
15006             if(this.labelWidth > 12){
15007                 labelCfg.style = "width: " + this.labelWidth + 'px';
15008             }
15009             
15010             if(this.labelWidth < 13 && this.labelmd == 0){
15011                 this.labelmd = this.labelWidth;
15012             }
15013             
15014             if(this.labellg > 0){
15015                 labelCfg.cls += ' col-lg-' + this.labellg;
15016                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15017             }
15018             
15019             if(this.labelmd > 0){
15020                 labelCfg.cls += ' col-md-' + this.labelmd;
15021                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15022             }
15023             
15024             if(this.labelsm > 0){
15025                 labelCfg.cls += ' col-sm-' + this.labelsm;
15026                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15027             }
15028             
15029             if(this.labelxs > 0){
15030                 labelCfg.cls += ' col-xs-' + this.labelxs;
15031                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15032             }
15033                 
15034                 
15035         } else if ( this.fieldLabel.length) {
15036             cfg.cn = [
15037                 {
15038                    tag : 'i',
15039                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15040                    tooltip : 'This field is required'
15041                 },
15042                 {
15043                     tag: 'label',
15044                     cls : 'control-label',
15045                     html : this.fieldLabel
15046
15047                 },
15048                 {
15049                     cls : '', 
15050                     cn: [
15051                         combobox
15052                     ]
15053                 }
15054             ];
15055             
15056             if(this.indicatorpos == 'right'){
15057                 cfg.cn = [
15058                     {
15059                         tag: 'label',
15060                         cls : 'control-label',
15061                         html : this.fieldLabel,
15062                         cn : [
15063                             {
15064                                tag : 'i',
15065                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15066                                tooltip : 'This field is required'
15067                             }
15068                         ]
15069                     },
15070                     {
15071                         cls : '', 
15072                         cn: [
15073                             combobox
15074                         ]
15075                     }
15076                 ];
15077             }
15078         } else {
15079             cfg.cn = combobox;    
15080         }
15081         
15082         
15083         var settings = this;
15084         
15085         ['xs','sm','md','lg'].map(function(size){
15086             if (settings[size]) {
15087                 cfg.cls += ' col-' + size + '-' + settings[size];
15088             }
15089         });
15090         
15091         return cfg;
15092     },
15093     
15094     initTouchView : function()
15095     {
15096         this.renderTouchView();
15097         
15098         this.touchViewEl.on('scroll', function(){
15099             this.el.dom.scrollTop = 0;
15100         }, this);
15101         
15102         this.originalValue = this.getValue();
15103         
15104         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15105         
15106         this.inputEl().on("click", this.showTouchView, this);
15107         if (this.triggerEl) {
15108             this.triggerEl.on("click", this.showTouchView, this);
15109         }
15110         
15111         
15112         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15113         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15114         
15115         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15116         
15117         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15118         this.store.on('load', this.onTouchViewLoad, this);
15119         this.store.on('loadexception', this.onTouchViewLoadException, this);
15120         
15121         if(this.hiddenName){
15122             
15123             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15124             
15125             this.hiddenField.dom.value =
15126                 this.hiddenValue !== undefined ? this.hiddenValue :
15127                 this.value !== undefined ? this.value : '';
15128         
15129             this.el.dom.removeAttribute('name');
15130             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15131         }
15132         
15133         if(this.multiple){
15134             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15135             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15136         }
15137         
15138         if(this.removable && !this.multiple){
15139             var close = this.closeTriggerEl();
15140             if(close){
15141                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15142                 close.on('click', this.removeBtnClick, this, close);
15143             }
15144         }
15145         /*
15146          * fix the bug in Safari iOS8
15147          */
15148         this.inputEl().on("focus", function(e){
15149             document.activeElement.blur();
15150         }, this);
15151         
15152         return;
15153         
15154         
15155     },
15156     
15157     renderTouchView : function()
15158     {
15159         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15160         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15161         
15162         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15163         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15164         
15165         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15166         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15167         this.touchViewBodyEl.setStyle('overflow', 'auto');
15168         
15169         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15170         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15171         
15172         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15173         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15174         
15175     },
15176     
15177     showTouchView : function()
15178     {
15179         if(this.disabled){
15180             return;
15181         }
15182         
15183         this.touchViewHeaderEl.hide();
15184
15185         if(this.modalTitle.length){
15186             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15187             this.touchViewHeaderEl.show();
15188         }
15189
15190         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15191         this.touchViewEl.show();
15192
15193         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15194         
15195         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15196         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15197
15198         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15199
15200         if(this.modalTitle.length){
15201             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15202         }
15203         
15204         this.touchViewBodyEl.setHeight(bodyHeight);
15205
15206         if(this.animate){
15207             var _this = this;
15208             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15209         }else{
15210             this.touchViewEl.addClass('in');
15211         }
15212
15213         this.doTouchViewQuery();
15214         
15215     },
15216     
15217     hideTouchView : function()
15218     {
15219         this.touchViewEl.removeClass('in');
15220
15221         if(this.animate){
15222             var _this = this;
15223             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15224         }else{
15225             this.touchViewEl.setStyle('display', 'none');
15226         }
15227         
15228     },
15229     
15230     setTouchViewValue : function()
15231     {
15232         if(this.multiple){
15233             this.clearItem();
15234         
15235             var _this = this;
15236
15237             Roo.each(this.tickItems, function(o){
15238                 this.addItem(o);
15239             }, this);
15240         }
15241         
15242         this.hideTouchView();
15243     },
15244     
15245     doTouchViewQuery : function()
15246     {
15247         var qe = {
15248             query: '',
15249             forceAll: true,
15250             combo: this,
15251             cancel:false
15252         };
15253         
15254         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15255             return false;
15256         }
15257         
15258         if(!this.alwaysQuery || this.mode == 'local'){
15259             this.onTouchViewLoad();
15260             return;
15261         }
15262         
15263         this.store.load();
15264     },
15265     
15266     onTouchViewBeforeLoad : function(combo,opts)
15267     {
15268         return;
15269     },
15270
15271     // private
15272     onTouchViewLoad : function()
15273     {
15274         if(this.store.getCount() < 1){
15275             this.onTouchViewEmptyResults();
15276             return;
15277         }
15278         
15279         this.clearTouchView();
15280         
15281         var rawValue = this.getRawValue();
15282         
15283         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15284         
15285         this.tickItems = [];
15286         
15287         this.store.data.each(function(d, rowIndex){
15288             var row = this.touchViewListGroup.createChild(template);
15289             
15290             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15291                 row.addClass(d.data.cls);
15292             }
15293             
15294             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15295                 var cfg = {
15296                     data : d.data,
15297                     html : d.data[this.displayField]
15298                 };
15299                 
15300                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15301                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15302                 }
15303             }
15304             row.removeClass('selected');
15305             if(!this.multiple && this.valueField &&
15306                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15307             {
15308                 // radio buttons..
15309                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15310                 row.addClass('selected');
15311             }
15312             
15313             if(this.multiple && this.valueField &&
15314                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15315             {
15316                 
15317                 // checkboxes...
15318                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15319                 this.tickItems.push(d.data);
15320             }
15321             
15322             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15323             
15324         }, this);
15325         
15326         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15327         
15328         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15329
15330         if(this.modalTitle.length){
15331             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15332         }
15333
15334         var listHeight = this.touchViewListGroup.getHeight();
15335         
15336         var _this = this;
15337         
15338         if(firstChecked && listHeight > bodyHeight){
15339             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15340         }
15341         
15342     },
15343     
15344     onTouchViewLoadException : function()
15345     {
15346         this.hideTouchView();
15347     },
15348     
15349     onTouchViewEmptyResults : function()
15350     {
15351         this.clearTouchView();
15352         
15353         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15354         
15355         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15356         
15357     },
15358     
15359     clearTouchView : function()
15360     {
15361         this.touchViewListGroup.dom.innerHTML = '';
15362     },
15363     
15364     onTouchViewClick : function(e, el, o)
15365     {
15366         e.preventDefault();
15367         
15368         var row = o.row;
15369         var rowIndex = o.rowIndex;
15370         
15371         var r = this.store.getAt(rowIndex);
15372         
15373         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15374             
15375             if(!this.multiple){
15376                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15377                     c.dom.removeAttribute('checked');
15378                 }, this);
15379
15380                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15381
15382                 this.setFromData(r.data);
15383
15384                 var close = this.closeTriggerEl();
15385
15386                 if(close){
15387                     close.show();
15388                 }
15389
15390                 this.hideTouchView();
15391
15392                 this.fireEvent('select', this, r, rowIndex);
15393
15394                 return;
15395             }
15396
15397             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15398                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15399                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15400                 return;
15401             }
15402
15403             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15404             this.addItem(r.data);
15405             this.tickItems.push(r.data);
15406         }
15407     },
15408     
15409     getAutoCreateNativeIOS : function()
15410     {
15411         var cfg = {
15412             cls: 'form-group' //input-group,
15413         };
15414         
15415         var combobox =  {
15416             tag: 'select',
15417             cls : 'roo-ios-select'
15418         };
15419         
15420         if (this.name) {
15421             combobox.name = this.name;
15422         }
15423         
15424         if (this.disabled) {
15425             combobox.disabled = true;
15426         }
15427         
15428         var settings = this;
15429         
15430         ['xs','sm','md','lg'].map(function(size){
15431             if (settings[size]) {
15432                 cfg.cls += ' col-' + size + '-' + settings[size];
15433             }
15434         });
15435         
15436         cfg.cn = combobox;
15437         
15438         return cfg;
15439         
15440     },
15441     
15442     initIOSView : function()
15443     {
15444         this.store.on('load', this.onIOSViewLoad, this);
15445         
15446         return;
15447     },
15448     
15449     onIOSViewLoad : function()
15450     {
15451         if(this.store.getCount() < 1){
15452             return;
15453         }
15454         
15455         this.clearIOSView();
15456         
15457         if(this.allowBlank) {
15458             
15459             var default_text = '-- SELECT --';
15460             
15461             if(this.placeholder.length){
15462                 default_text = this.placeholder;
15463             }
15464             
15465             if(this.emptyTitle.length){
15466                 default_text += ' - ' + this.emptyTitle + ' -';
15467             }
15468             
15469             var opt = this.inputEl().createChild({
15470                 tag: 'option',
15471                 value : 0,
15472                 html : default_text
15473             });
15474             
15475             var o = {};
15476             o[this.valueField] = 0;
15477             o[this.displayField] = default_text;
15478             
15479             this.ios_options.push({
15480                 data : o,
15481                 el : opt
15482             });
15483             
15484         }
15485         
15486         this.store.data.each(function(d, rowIndex){
15487             
15488             var html = '';
15489             
15490             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15491                 html = d.data[this.displayField];
15492             }
15493             
15494             var value = '';
15495             
15496             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15497                 value = d.data[this.valueField];
15498             }
15499             
15500             var option = {
15501                 tag: 'option',
15502                 value : value,
15503                 html : html
15504             };
15505             
15506             if(this.value == d.data[this.valueField]){
15507                 option['selected'] = true;
15508             }
15509             
15510             var opt = this.inputEl().createChild(option);
15511             
15512             this.ios_options.push({
15513                 data : d.data,
15514                 el : opt
15515             });
15516             
15517         }, this);
15518         
15519         this.inputEl().on('change', function(){
15520            this.fireEvent('select', this);
15521         }, this);
15522         
15523     },
15524     
15525     clearIOSView: function()
15526     {
15527         this.inputEl().dom.innerHTML = '';
15528         
15529         this.ios_options = [];
15530     },
15531     
15532     setIOSValue: function(v)
15533     {
15534         this.value = v;
15535         
15536         if(!this.ios_options){
15537             return;
15538         }
15539         
15540         Roo.each(this.ios_options, function(opts){
15541            
15542            opts.el.dom.removeAttribute('selected');
15543            
15544            if(opts.data[this.valueField] != v){
15545                return;
15546            }
15547            
15548            opts.el.dom.setAttribute('selected', true);
15549            
15550         }, this);
15551     }
15552
15553     /** 
15554     * @cfg {Boolean} grow 
15555     * @hide 
15556     */
15557     /** 
15558     * @cfg {Number} growMin 
15559     * @hide 
15560     */
15561     /** 
15562     * @cfg {Number} growMax 
15563     * @hide 
15564     */
15565     /**
15566      * @hide
15567      * @method autoSize
15568      */
15569 });
15570
15571 Roo.apply(Roo.bootstrap.ComboBox,  {
15572     
15573     header : {
15574         tag: 'div',
15575         cls: 'modal-header',
15576         cn: [
15577             {
15578                 tag: 'h4',
15579                 cls: 'modal-title'
15580             }
15581         ]
15582     },
15583     
15584     body : {
15585         tag: 'div',
15586         cls: 'modal-body',
15587         cn: [
15588             {
15589                 tag: 'ul',
15590                 cls: 'list-group'
15591             }
15592         ]
15593     },
15594     
15595     listItemRadio : {
15596         tag: 'li',
15597         cls: 'list-group-item',
15598         cn: [
15599             {
15600                 tag: 'span',
15601                 cls: 'roo-combobox-list-group-item-value'
15602             },
15603             {
15604                 tag: 'div',
15605                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15606                 cn: [
15607                     {
15608                         tag: 'input',
15609                         type: 'radio'
15610                     },
15611                     {
15612                         tag: 'label'
15613                     }
15614                 ]
15615             }
15616         ]
15617     },
15618     
15619     listItemCheckbox : {
15620         tag: 'li',
15621         cls: 'list-group-item',
15622         cn: [
15623             {
15624                 tag: 'span',
15625                 cls: 'roo-combobox-list-group-item-value'
15626             },
15627             {
15628                 tag: 'div',
15629                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15630                 cn: [
15631                     {
15632                         tag: 'input',
15633                         type: 'checkbox'
15634                     },
15635                     {
15636                         tag: 'label'
15637                     }
15638                 ]
15639             }
15640         ]
15641     },
15642     
15643     emptyResult : {
15644         tag: 'div',
15645         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15646     },
15647     
15648     footer : {
15649         tag: 'div',
15650         cls: 'modal-footer',
15651         cn: [
15652             {
15653                 tag: 'div',
15654                 cls: 'row',
15655                 cn: [
15656                     {
15657                         tag: 'div',
15658                         cls: 'col-xs-6 text-left',
15659                         cn: {
15660                             tag: 'button',
15661                             cls: 'btn btn-danger roo-touch-view-cancel',
15662                             html: 'Cancel'
15663                         }
15664                     },
15665                     {
15666                         tag: 'div',
15667                         cls: 'col-xs-6 text-right',
15668                         cn: {
15669                             tag: 'button',
15670                             cls: 'btn btn-success roo-touch-view-ok',
15671                             html: 'OK'
15672                         }
15673                     }
15674                 ]
15675             }
15676         ]
15677         
15678     }
15679 });
15680
15681 Roo.apply(Roo.bootstrap.ComboBox,  {
15682     
15683     touchViewTemplate : {
15684         tag: 'div',
15685         cls: 'modal fade roo-combobox-touch-view',
15686         cn: [
15687             {
15688                 tag: 'div',
15689                 cls: 'modal-dialog',
15690                 style : 'position:fixed', // we have to fix position....
15691                 cn: [
15692                     {
15693                         tag: 'div',
15694                         cls: 'modal-content',
15695                         cn: [
15696                             Roo.bootstrap.ComboBox.header,
15697                             Roo.bootstrap.ComboBox.body,
15698                             Roo.bootstrap.ComboBox.footer
15699                         ]
15700                     }
15701                 ]
15702             }
15703         ]
15704     }
15705 });/*
15706  * Based on:
15707  * Ext JS Library 1.1.1
15708  * Copyright(c) 2006-2007, Ext JS, LLC.
15709  *
15710  * Originally Released Under LGPL - original licence link has changed is not relivant.
15711  *
15712  * Fork - LGPL
15713  * <script type="text/javascript">
15714  */
15715
15716 /**
15717  * @class Roo.View
15718  * @extends Roo.util.Observable
15719  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15720  * This class also supports single and multi selection modes. <br>
15721  * Create a data model bound view:
15722  <pre><code>
15723  var store = new Roo.data.Store(...);
15724
15725  var view = new Roo.View({
15726     el : "my-element",
15727     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15728  
15729     singleSelect: true,
15730     selectedClass: "ydataview-selected",
15731     store: store
15732  });
15733
15734  // listen for node click?
15735  view.on("click", function(vw, index, node, e){
15736  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15737  });
15738
15739  // load XML data
15740  dataModel.load("foobar.xml");
15741  </code></pre>
15742  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15743  * <br><br>
15744  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15745  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15746  * 
15747  * Note: old style constructor is still suported (container, template, config)
15748  * 
15749  * @constructor
15750  * Create a new View
15751  * @param {Object} config The config object
15752  * 
15753  */
15754 Roo.View = function(config, depreciated_tpl, depreciated_config){
15755     
15756     this.parent = false;
15757     
15758     if (typeof(depreciated_tpl) == 'undefined') {
15759         // new way.. - universal constructor.
15760         Roo.apply(this, config);
15761         this.el  = Roo.get(this.el);
15762     } else {
15763         // old format..
15764         this.el  = Roo.get(config);
15765         this.tpl = depreciated_tpl;
15766         Roo.apply(this, depreciated_config);
15767     }
15768     this.wrapEl  = this.el.wrap().wrap();
15769     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15770     
15771     
15772     if(typeof(this.tpl) == "string"){
15773         this.tpl = new Roo.Template(this.tpl);
15774     } else {
15775         // support xtype ctors..
15776         this.tpl = new Roo.factory(this.tpl, Roo);
15777     }
15778     
15779     
15780     this.tpl.compile();
15781     
15782     /** @private */
15783     this.addEvents({
15784         /**
15785          * @event beforeclick
15786          * Fires before a click is processed. Returns false to cancel the default action.
15787          * @param {Roo.View} this
15788          * @param {Number} index The index of the target node
15789          * @param {HTMLElement} node The target node
15790          * @param {Roo.EventObject} e The raw event object
15791          */
15792             "beforeclick" : true,
15793         /**
15794          * @event click
15795          * Fires when a template node is clicked.
15796          * @param {Roo.View} this
15797          * @param {Number} index The index of the target node
15798          * @param {HTMLElement} node The target node
15799          * @param {Roo.EventObject} e The raw event object
15800          */
15801             "click" : true,
15802         /**
15803          * @event dblclick
15804          * Fires when a template node is double clicked.
15805          * @param {Roo.View} this
15806          * @param {Number} index The index of the target node
15807          * @param {HTMLElement} node The target node
15808          * @param {Roo.EventObject} e The raw event object
15809          */
15810             "dblclick" : true,
15811         /**
15812          * @event contextmenu
15813          * Fires when a template node is right clicked.
15814          * @param {Roo.View} this
15815          * @param {Number} index The index of the target node
15816          * @param {HTMLElement} node The target node
15817          * @param {Roo.EventObject} e The raw event object
15818          */
15819             "contextmenu" : true,
15820         /**
15821          * @event selectionchange
15822          * Fires when the selected nodes change.
15823          * @param {Roo.View} this
15824          * @param {Array} selections Array of the selected nodes
15825          */
15826             "selectionchange" : true,
15827     
15828         /**
15829          * @event beforeselect
15830          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15831          * @param {Roo.View} this
15832          * @param {HTMLElement} node The node to be selected
15833          * @param {Array} selections Array of currently selected nodes
15834          */
15835             "beforeselect" : true,
15836         /**
15837          * @event preparedata
15838          * Fires on every row to render, to allow you to change the data.
15839          * @param {Roo.View} this
15840          * @param {Object} data to be rendered (change this)
15841          */
15842           "preparedata" : true
15843           
15844           
15845         });
15846
15847
15848
15849     this.el.on({
15850         "click": this.onClick,
15851         "dblclick": this.onDblClick,
15852         "contextmenu": this.onContextMenu,
15853         scope:this
15854     });
15855
15856     this.selections = [];
15857     this.nodes = [];
15858     this.cmp = new Roo.CompositeElementLite([]);
15859     if(this.store){
15860         this.store = Roo.factory(this.store, Roo.data);
15861         this.setStore(this.store, true);
15862     }
15863     
15864     if ( this.footer && this.footer.xtype) {
15865            
15866          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15867         
15868         this.footer.dataSource = this.store;
15869         this.footer.container = fctr;
15870         this.footer = Roo.factory(this.footer, Roo);
15871         fctr.insertFirst(this.el);
15872         
15873         // this is a bit insane - as the paging toolbar seems to detach the el..
15874 //        dom.parentNode.parentNode.parentNode
15875          // they get detached?
15876     }
15877     
15878     
15879     Roo.View.superclass.constructor.call(this);
15880     
15881     
15882 };
15883
15884 Roo.extend(Roo.View, Roo.util.Observable, {
15885     
15886      /**
15887      * @cfg {Roo.data.Store} store Data store to load data from.
15888      */
15889     store : false,
15890     
15891     /**
15892      * @cfg {String|Roo.Element} el The container element.
15893      */
15894     el : '',
15895     
15896     /**
15897      * @cfg {String|Roo.Template} tpl The template used by this View 
15898      */
15899     tpl : false,
15900     /**
15901      * @cfg {String} dataName the named area of the template to use as the data area
15902      *                          Works with domtemplates roo-name="name"
15903      */
15904     dataName: false,
15905     /**
15906      * @cfg {String} selectedClass The css class to add to selected nodes
15907      */
15908     selectedClass : "x-view-selected",
15909      /**
15910      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15911      */
15912     emptyText : "",
15913     
15914     /**
15915      * @cfg {String} text to display on mask (default Loading)
15916      */
15917     mask : false,
15918     /**
15919      * @cfg {Boolean} multiSelect Allow multiple selection
15920      */
15921     multiSelect : false,
15922     /**
15923      * @cfg {Boolean} singleSelect Allow single selection
15924      */
15925     singleSelect:  false,
15926     
15927     /**
15928      * @cfg {Boolean} toggleSelect - selecting 
15929      */
15930     toggleSelect : false,
15931     
15932     /**
15933      * @cfg {Boolean} tickable - selecting 
15934      */
15935     tickable : false,
15936     
15937     /**
15938      * Returns the element this view is bound to.
15939      * @return {Roo.Element}
15940      */
15941     getEl : function(){
15942         return this.wrapEl;
15943     },
15944     
15945     
15946
15947     /**
15948      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15949      */
15950     refresh : function(){
15951         //Roo.log('refresh');
15952         var t = this.tpl;
15953         
15954         // if we are using something like 'domtemplate', then
15955         // the what gets used is:
15956         // t.applySubtemplate(NAME, data, wrapping data..)
15957         // the outer template then get' applied with
15958         //     the store 'extra data'
15959         // and the body get's added to the
15960         //      roo-name="data" node?
15961         //      <span class='roo-tpl-{name}'></span> ?????
15962         
15963         
15964         
15965         this.clearSelections();
15966         this.el.update("");
15967         var html = [];
15968         var records = this.store.getRange();
15969         if(records.length < 1) {
15970             
15971             // is this valid??  = should it render a template??
15972             
15973             this.el.update(this.emptyText);
15974             return;
15975         }
15976         var el = this.el;
15977         if (this.dataName) {
15978             this.el.update(t.apply(this.store.meta)); //????
15979             el = this.el.child('.roo-tpl-' + this.dataName);
15980         }
15981         
15982         for(var i = 0, len = records.length; i < len; i++){
15983             var data = this.prepareData(records[i].data, i, records[i]);
15984             this.fireEvent("preparedata", this, data, i, records[i]);
15985             
15986             var d = Roo.apply({}, data);
15987             
15988             if(this.tickable){
15989                 Roo.apply(d, {'roo-id' : Roo.id()});
15990                 
15991                 var _this = this;
15992             
15993                 Roo.each(this.parent.item, function(item){
15994                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15995                         return;
15996                     }
15997                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15998                 });
15999             }
16000             
16001             html[html.length] = Roo.util.Format.trim(
16002                 this.dataName ?
16003                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16004                     t.apply(d)
16005             );
16006         }
16007         
16008         
16009         
16010         el.update(html.join(""));
16011         this.nodes = el.dom.childNodes;
16012         this.updateIndexes(0);
16013     },
16014     
16015
16016     /**
16017      * Function to override to reformat the data that is sent to
16018      * the template for each node.
16019      * DEPRICATED - use the preparedata event handler.
16020      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16021      * a JSON object for an UpdateManager bound view).
16022      */
16023     prepareData : function(data, index, record)
16024     {
16025         this.fireEvent("preparedata", this, data, index, record);
16026         return data;
16027     },
16028
16029     onUpdate : function(ds, record){
16030         // Roo.log('on update');   
16031         this.clearSelections();
16032         var index = this.store.indexOf(record);
16033         var n = this.nodes[index];
16034         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16035         n.parentNode.removeChild(n);
16036         this.updateIndexes(index, index);
16037     },
16038
16039     
16040     
16041 // --------- FIXME     
16042     onAdd : function(ds, records, index)
16043     {
16044         //Roo.log(['on Add', ds, records, index] );        
16045         this.clearSelections();
16046         if(this.nodes.length == 0){
16047             this.refresh();
16048             return;
16049         }
16050         var n = this.nodes[index];
16051         for(var i = 0, len = records.length; i < len; i++){
16052             var d = this.prepareData(records[i].data, i, records[i]);
16053             if(n){
16054                 this.tpl.insertBefore(n, d);
16055             }else{
16056                 
16057                 this.tpl.append(this.el, d);
16058             }
16059         }
16060         this.updateIndexes(index);
16061     },
16062
16063     onRemove : function(ds, record, index){
16064        // Roo.log('onRemove');
16065         this.clearSelections();
16066         var el = this.dataName  ?
16067             this.el.child('.roo-tpl-' + this.dataName) :
16068             this.el; 
16069         
16070         el.dom.removeChild(this.nodes[index]);
16071         this.updateIndexes(index);
16072     },
16073
16074     /**
16075      * Refresh an individual node.
16076      * @param {Number} index
16077      */
16078     refreshNode : function(index){
16079         this.onUpdate(this.store, this.store.getAt(index));
16080     },
16081
16082     updateIndexes : function(startIndex, endIndex){
16083         var ns = this.nodes;
16084         startIndex = startIndex || 0;
16085         endIndex = endIndex || ns.length - 1;
16086         for(var i = startIndex; i <= endIndex; i++){
16087             ns[i].nodeIndex = i;
16088         }
16089     },
16090
16091     /**
16092      * Changes the data store this view uses and refresh the view.
16093      * @param {Store} store
16094      */
16095     setStore : function(store, initial){
16096         if(!initial && this.store){
16097             this.store.un("datachanged", this.refresh);
16098             this.store.un("add", this.onAdd);
16099             this.store.un("remove", this.onRemove);
16100             this.store.un("update", this.onUpdate);
16101             this.store.un("clear", this.refresh);
16102             this.store.un("beforeload", this.onBeforeLoad);
16103             this.store.un("load", this.onLoad);
16104             this.store.un("loadexception", this.onLoad);
16105         }
16106         if(store){
16107           
16108             store.on("datachanged", this.refresh, this);
16109             store.on("add", this.onAdd, this);
16110             store.on("remove", this.onRemove, this);
16111             store.on("update", this.onUpdate, this);
16112             store.on("clear", this.refresh, this);
16113             store.on("beforeload", this.onBeforeLoad, this);
16114             store.on("load", this.onLoad, this);
16115             store.on("loadexception", this.onLoad, this);
16116         }
16117         
16118         if(store){
16119             this.refresh();
16120         }
16121     },
16122     /**
16123      * onbeforeLoad - masks the loading area.
16124      *
16125      */
16126     onBeforeLoad : function(store,opts)
16127     {
16128          //Roo.log('onBeforeLoad');   
16129         if (!opts.add) {
16130             this.el.update("");
16131         }
16132         this.el.mask(this.mask ? this.mask : "Loading" ); 
16133     },
16134     onLoad : function ()
16135     {
16136         this.el.unmask();
16137     },
16138     
16139
16140     /**
16141      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16142      * @param {HTMLElement} node
16143      * @return {HTMLElement} The template node
16144      */
16145     findItemFromChild : function(node){
16146         var el = this.dataName  ?
16147             this.el.child('.roo-tpl-' + this.dataName,true) :
16148             this.el.dom; 
16149         
16150         if(!node || node.parentNode == el){
16151                     return node;
16152             }
16153             var p = node.parentNode;
16154             while(p && p != el){
16155             if(p.parentNode == el){
16156                 return p;
16157             }
16158             p = p.parentNode;
16159         }
16160             return null;
16161     },
16162
16163     /** @ignore */
16164     onClick : function(e){
16165         var item = this.findItemFromChild(e.getTarget());
16166         if(item){
16167             var index = this.indexOf(item);
16168             if(this.onItemClick(item, index, e) !== false){
16169                 this.fireEvent("click", this, index, item, e);
16170             }
16171         }else{
16172             this.clearSelections();
16173         }
16174     },
16175
16176     /** @ignore */
16177     onContextMenu : function(e){
16178         var item = this.findItemFromChild(e.getTarget());
16179         if(item){
16180             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16181         }
16182     },
16183
16184     /** @ignore */
16185     onDblClick : function(e){
16186         var item = this.findItemFromChild(e.getTarget());
16187         if(item){
16188             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16189         }
16190     },
16191
16192     onItemClick : function(item, index, e)
16193     {
16194         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16195             return false;
16196         }
16197         if (this.toggleSelect) {
16198             var m = this.isSelected(item) ? 'unselect' : 'select';
16199             //Roo.log(m);
16200             var _t = this;
16201             _t[m](item, true, false);
16202             return true;
16203         }
16204         if(this.multiSelect || this.singleSelect){
16205             if(this.multiSelect && e.shiftKey && this.lastSelection){
16206                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16207             }else{
16208                 this.select(item, this.multiSelect && e.ctrlKey);
16209                 this.lastSelection = item;
16210             }
16211             
16212             if(!this.tickable){
16213                 e.preventDefault();
16214             }
16215             
16216         }
16217         return true;
16218     },
16219
16220     /**
16221      * Get the number of selected nodes.
16222      * @return {Number}
16223      */
16224     getSelectionCount : function(){
16225         return this.selections.length;
16226     },
16227
16228     /**
16229      * Get the currently selected nodes.
16230      * @return {Array} An array of HTMLElements
16231      */
16232     getSelectedNodes : function(){
16233         return this.selections;
16234     },
16235
16236     /**
16237      * Get the indexes of the selected nodes.
16238      * @return {Array}
16239      */
16240     getSelectedIndexes : function(){
16241         var indexes = [], s = this.selections;
16242         for(var i = 0, len = s.length; i < len; i++){
16243             indexes.push(s[i].nodeIndex);
16244         }
16245         return indexes;
16246     },
16247
16248     /**
16249      * Clear all selections
16250      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16251      */
16252     clearSelections : function(suppressEvent){
16253         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16254             this.cmp.elements = this.selections;
16255             this.cmp.removeClass(this.selectedClass);
16256             this.selections = [];
16257             if(!suppressEvent){
16258                 this.fireEvent("selectionchange", this, this.selections);
16259             }
16260         }
16261     },
16262
16263     /**
16264      * Returns true if the passed node is selected
16265      * @param {HTMLElement/Number} node The node or node index
16266      * @return {Boolean}
16267      */
16268     isSelected : function(node){
16269         var s = this.selections;
16270         if(s.length < 1){
16271             return false;
16272         }
16273         node = this.getNode(node);
16274         return s.indexOf(node) !== -1;
16275     },
16276
16277     /**
16278      * Selects 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 to keep existing selections
16281      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16282      */
16283     select : function(nodeInfo, keepExisting, suppressEvent){
16284         if(nodeInfo instanceof Array){
16285             if(!keepExisting){
16286                 this.clearSelections(true);
16287             }
16288             for(var i = 0, len = nodeInfo.length; i < len; i++){
16289                 this.select(nodeInfo[i], true, true);
16290             }
16291             return;
16292         } 
16293         var node = this.getNode(nodeInfo);
16294         if(!node || this.isSelected(node)){
16295             return; // already selected.
16296         }
16297         if(!keepExisting){
16298             this.clearSelections(true);
16299         }
16300         
16301         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16302             Roo.fly(node).addClass(this.selectedClass);
16303             this.selections.push(node);
16304             if(!suppressEvent){
16305                 this.fireEvent("selectionchange", this, this.selections);
16306             }
16307         }
16308         
16309         
16310     },
16311       /**
16312      * Unselects nodes.
16313      * @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
16314      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16315      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16316      */
16317     unselect : function(nodeInfo, keepExisting, suppressEvent)
16318     {
16319         if(nodeInfo instanceof Array){
16320             Roo.each(this.selections, function(s) {
16321                 this.unselect(s, nodeInfo);
16322             }, this);
16323             return;
16324         }
16325         var node = this.getNode(nodeInfo);
16326         if(!node || !this.isSelected(node)){
16327             //Roo.log("not selected");
16328             return; // not selected.
16329         }
16330         // fireevent???
16331         var ns = [];
16332         Roo.each(this.selections, function(s) {
16333             if (s == node ) {
16334                 Roo.fly(node).removeClass(this.selectedClass);
16335
16336                 return;
16337             }
16338             ns.push(s);
16339         },this);
16340         
16341         this.selections= ns;
16342         this.fireEvent("selectionchange", this, this.selections);
16343     },
16344
16345     /**
16346      * Gets a template node.
16347      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16348      * @return {HTMLElement} The node or null if it wasn't found
16349      */
16350     getNode : function(nodeInfo){
16351         if(typeof nodeInfo == "string"){
16352             return document.getElementById(nodeInfo);
16353         }else if(typeof nodeInfo == "number"){
16354             return this.nodes[nodeInfo];
16355         }
16356         return nodeInfo;
16357     },
16358
16359     /**
16360      * Gets a range template nodes.
16361      * @param {Number} startIndex
16362      * @param {Number} endIndex
16363      * @return {Array} An array of nodes
16364      */
16365     getNodes : function(start, end){
16366         var ns = this.nodes;
16367         start = start || 0;
16368         end = typeof end == "undefined" ? ns.length - 1 : end;
16369         var nodes = [];
16370         if(start <= end){
16371             for(var i = start; i <= end; i++){
16372                 nodes.push(ns[i]);
16373             }
16374         } else{
16375             for(var i = start; i >= end; i--){
16376                 nodes.push(ns[i]);
16377             }
16378         }
16379         return nodes;
16380     },
16381
16382     /**
16383      * Finds the index of the passed node
16384      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16385      * @return {Number} The index of the node or -1
16386      */
16387     indexOf : function(node){
16388         node = this.getNode(node);
16389         if(typeof node.nodeIndex == "number"){
16390             return node.nodeIndex;
16391         }
16392         var ns = this.nodes;
16393         for(var i = 0, len = ns.length; i < len; i++){
16394             if(ns[i] == node){
16395                 return i;
16396             }
16397         }
16398         return -1;
16399     }
16400 });
16401 /*
16402  * - LGPL
16403  *
16404  * based on jquery fullcalendar
16405  * 
16406  */
16407
16408 Roo.bootstrap = Roo.bootstrap || {};
16409 /**
16410  * @class Roo.bootstrap.Calendar
16411  * @extends Roo.bootstrap.Component
16412  * Bootstrap Calendar class
16413  * @cfg {Boolean} loadMask (true|false) default false
16414  * @cfg {Object} header generate the user specific header of the calendar, default false
16415
16416  * @constructor
16417  * Create a new Container
16418  * @param {Object} config The config object
16419  */
16420
16421
16422
16423 Roo.bootstrap.Calendar = function(config){
16424     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16425      this.addEvents({
16426         /**
16427              * @event select
16428              * Fires when a date is selected
16429              * @param {DatePicker} this
16430              * @param {Date} date The selected date
16431              */
16432         'select': true,
16433         /**
16434              * @event monthchange
16435              * Fires when the displayed month changes 
16436              * @param {DatePicker} this
16437              * @param {Date} date The selected month
16438              */
16439         'monthchange': true,
16440         /**
16441              * @event evententer
16442              * Fires when mouse over an event
16443              * @param {Calendar} this
16444              * @param {event} Event
16445              */
16446         'evententer': true,
16447         /**
16448              * @event eventleave
16449              * Fires when the mouse leaves an
16450              * @param {Calendar} this
16451              * @param {event}
16452              */
16453         'eventleave': true,
16454         /**
16455              * @event eventclick
16456              * Fires when the mouse click an
16457              * @param {Calendar} this
16458              * @param {event}
16459              */
16460         'eventclick': true
16461         
16462     });
16463
16464 };
16465
16466 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16467     
16468      /**
16469      * @cfg {Number} startDay
16470      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16471      */
16472     startDay : 0,
16473     
16474     loadMask : false,
16475     
16476     header : false,
16477       
16478     getAutoCreate : function(){
16479         
16480         
16481         var fc_button = function(name, corner, style, content ) {
16482             return Roo.apply({},{
16483                 tag : 'span',
16484                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16485                          (corner.length ?
16486                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16487                             ''
16488                         ),
16489                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16490                 unselectable: 'on'
16491             });
16492         };
16493         
16494         var header = {};
16495         
16496         if(!this.header){
16497             header = {
16498                 tag : 'table',
16499                 cls : 'fc-header',
16500                 style : 'width:100%',
16501                 cn : [
16502                     {
16503                         tag: 'tr',
16504                         cn : [
16505                             {
16506                                 tag : 'td',
16507                                 cls : 'fc-header-left',
16508                                 cn : [
16509                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16510                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16511                                     { tag: 'span', cls: 'fc-header-space' },
16512                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16513
16514
16515                                 ]
16516                             },
16517
16518                             {
16519                                 tag : 'td',
16520                                 cls : 'fc-header-center',
16521                                 cn : [
16522                                     {
16523                                         tag: 'span',
16524                                         cls: 'fc-header-title',
16525                                         cn : {
16526                                             tag: 'H2',
16527                                             html : 'month / year'
16528                                         }
16529                                     }
16530
16531                                 ]
16532                             },
16533                             {
16534                                 tag : 'td',
16535                                 cls : 'fc-header-right',
16536                                 cn : [
16537                               /*      fc_button('month', 'left', '', 'month' ),
16538                                     fc_button('week', '', '', 'week' ),
16539                                     fc_button('day', 'right', '', 'day' )
16540                                 */    
16541
16542                                 ]
16543                             }
16544
16545                         ]
16546                     }
16547                 ]
16548             };
16549         }
16550         
16551         header = this.header;
16552         
16553        
16554         var cal_heads = function() {
16555             var ret = [];
16556             // fixme - handle this.
16557             
16558             for (var i =0; i < Date.dayNames.length; i++) {
16559                 var d = Date.dayNames[i];
16560                 ret.push({
16561                     tag: 'th',
16562                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16563                     html : d.substring(0,3)
16564                 });
16565                 
16566             }
16567             ret[0].cls += ' fc-first';
16568             ret[6].cls += ' fc-last';
16569             return ret;
16570         };
16571         var cal_cell = function(n) {
16572             return  {
16573                 tag: 'td',
16574                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16575                 cn : [
16576                     {
16577                         cn : [
16578                             {
16579                                 cls: 'fc-day-number',
16580                                 html: 'D'
16581                             },
16582                             {
16583                                 cls: 'fc-day-content',
16584                              
16585                                 cn : [
16586                                      {
16587                                         style: 'position: relative;' // height: 17px;
16588                                     }
16589                                 ]
16590                             }
16591                             
16592                             
16593                         ]
16594                     }
16595                 ]
16596                 
16597             }
16598         };
16599         var cal_rows = function() {
16600             
16601             var ret = [];
16602             for (var r = 0; r < 6; r++) {
16603                 var row= {
16604                     tag : 'tr',
16605                     cls : 'fc-week',
16606                     cn : []
16607                 };
16608                 
16609                 for (var i =0; i < Date.dayNames.length; i++) {
16610                     var d = Date.dayNames[i];
16611                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16612
16613                 }
16614                 row.cn[0].cls+=' fc-first';
16615                 row.cn[0].cn[0].style = 'min-height:90px';
16616                 row.cn[6].cls+=' fc-last';
16617                 ret.push(row);
16618                 
16619             }
16620             ret[0].cls += ' fc-first';
16621             ret[4].cls += ' fc-prev-last';
16622             ret[5].cls += ' fc-last';
16623             return ret;
16624             
16625         };
16626         
16627         var cal_table = {
16628             tag: 'table',
16629             cls: 'fc-border-separate',
16630             style : 'width:100%',
16631             cellspacing  : 0,
16632             cn : [
16633                 { 
16634                     tag: 'thead',
16635                     cn : [
16636                         { 
16637                             tag: 'tr',
16638                             cls : 'fc-first fc-last',
16639                             cn : cal_heads()
16640                         }
16641                     ]
16642                 },
16643                 { 
16644                     tag: 'tbody',
16645                     cn : cal_rows()
16646                 }
16647                   
16648             ]
16649         };
16650          
16651          var cfg = {
16652             cls : 'fc fc-ltr',
16653             cn : [
16654                 header,
16655                 {
16656                     cls : 'fc-content',
16657                     style : "position: relative;",
16658                     cn : [
16659                         {
16660                             cls : 'fc-view fc-view-month fc-grid',
16661                             style : 'position: relative',
16662                             unselectable : 'on',
16663                             cn : [
16664                                 {
16665                                     cls : 'fc-event-container',
16666                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16667                                 },
16668                                 cal_table
16669                             ]
16670                         }
16671                     ]
16672     
16673                 }
16674            ] 
16675             
16676         };
16677         
16678          
16679         
16680         return cfg;
16681     },
16682     
16683     
16684     initEvents : function()
16685     {
16686         if(!this.store){
16687             throw "can not find store for calendar";
16688         }
16689         
16690         var mark = {
16691             tag: "div",
16692             cls:"x-dlg-mask",
16693             style: "text-align:center",
16694             cn: [
16695                 {
16696                     tag: "div",
16697                     style: "background-color:white;width:50%;margin:250 auto",
16698                     cn: [
16699                         {
16700                             tag: "img",
16701                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16702                         },
16703                         {
16704                             tag: "span",
16705                             html: "Loading"
16706                         }
16707                         
16708                     ]
16709                 }
16710             ]
16711         };
16712         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16713         
16714         var size = this.el.select('.fc-content', true).first().getSize();
16715         this.maskEl.setSize(size.width, size.height);
16716         this.maskEl.enableDisplayMode("block");
16717         if(!this.loadMask){
16718             this.maskEl.hide();
16719         }
16720         
16721         this.store = Roo.factory(this.store, Roo.data);
16722         this.store.on('load', this.onLoad, this);
16723         this.store.on('beforeload', this.onBeforeLoad, this);
16724         
16725         this.resize();
16726         
16727         this.cells = this.el.select('.fc-day',true);
16728         //Roo.log(this.cells);
16729         this.textNodes = this.el.query('.fc-day-number');
16730         this.cells.addClassOnOver('fc-state-hover');
16731         
16732         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16733         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16734         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16735         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16736         
16737         this.on('monthchange', this.onMonthChange, this);
16738         
16739         this.update(new Date().clearTime());
16740     },
16741     
16742     resize : function() {
16743         var sz  = this.el.getSize();
16744         
16745         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16746         this.el.select('.fc-day-content div',true).setHeight(34);
16747     },
16748     
16749     
16750     // private
16751     showPrevMonth : function(e){
16752         this.update(this.activeDate.add("mo", -1));
16753     },
16754     showToday : function(e){
16755         this.update(new Date().clearTime());
16756     },
16757     // private
16758     showNextMonth : function(e){
16759         this.update(this.activeDate.add("mo", 1));
16760     },
16761
16762     // private
16763     showPrevYear : function(){
16764         this.update(this.activeDate.add("y", -1));
16765     },
16766
16767     // private
16768     showNextYear : function(){
16769         this.update(this.activeDate.add("y", 1));
16770     },
16771
16772     
16773    // private
16774     update : function(date)
16775     {
16776         var vd = this.activeDate;
16777         this.activeDate = date;
16778 //        if(vd && this.el){
16779 //            var t = date.getTime();
16780 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16781 //                Roo.log('using add remove');
16782 //                
16783 //                this.fireEvent('monthchange', this, date);
16784 //                
16785 //                this.cells.removeClass("fc-state-highlight");
16786 //                this.cells.each(function(c){
16787 //                   if(c.dateValue == t){
16788 //                       c.addClass("fc-state-highlight");
16789 //                       setTimeout(function(){
16790 //                            try{c.dom.firstChild.focus();}catch(e){}
16791 //                       }, 50);
16792 //                       return false;
16793 //                   }
16794 //                   return true;
16795 //                });
16796 //                return;
16797 //            }
16798 //        }
16799         
16800         var days = date.getDaysInMonth();
16801         
16802         var firstOfMonth = date.getFirstDateOfMonth();
16803         var startingPos = firstOfMonth.getDay()-this.startDay;
16804         
16805         if(startingPos < this.startDay){
16806             startingPos += 7;
16807         }
16808         
16809         var pm = date.add(Date.MONTH, -1);
16810         var prevStart = pm.getDaysInMonth()-startingPos;
16811 //        
16812         this.cells = this.el.select('.fc-day',true);
16813         this.textNodes = this.el.query('.fc-day-number');
16814         this.cells.addClassOnOver('fc-state-hover');
16815         
16816         var cells = this.cells.elements;
16817         var textEls = this.textNodes;
16818         
16819         Roo.each(cells, function(cell){
16820             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16821         });
16822         
16823         days += startingPos;
16824
16825         // convert everything to numbers so it's fast
16826         var day = 86400000;
16827         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16828         //Roo.log(d);
16829         //Roo.log(pm);
16830         //Roo.log(prevStart);
16831         
16832         var today = new Date().clearTime().getTime();
16833         var sel = date.clearTime().getTime();
16834         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16835         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16836         var ddMatch = this.disabledDatesRE;
16837         var ddText = this.disabledDatesText;
16838         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16839         var ddaysText = this.disabledDaysText;
16840         var format = this.format;
16841         
16842         var setCellClass = function(cal, cell){
16843             cell.row = 0;
16844             cell.events = [];
16845             cell.more = [];
16846             //Roo.log('set Cell Class');
16847             cell.title = "";
16848             var t = d.getTime();
16849             
16850             //Roo.log(d);
16851             
16852             cell.dateValue = t;
16853             if(t == today){
16854                 cell.className += " fc-today";
16855                 cell.className += " fc-state-highlight";
16856                 cell.title = cal.todayText;
16857             }
16858             if(t == sel){
16859                 // disable highlight in other month..
16860                 //cell.className += " fc-state-highlight";
16861                 
16862             }
16863             // disabling
16864             if(t < min) {
16865                 cell.className = " fc-state-disabled";
16866                 cell.title = cal.minText;
16867                 return;
16868             }
16869             if(t > max) {
16870                 cell.className = " fc-state-disabled";
16871                 cell.title = cal.maxText;
16872                 return;
16873             }
16874             if(ddays){
16875                 if(ddays.indexOf(d.getDay()) != -1){
16876                     cell.title = ddaysText;
16877                     cell.className = " fc-state-disabled";
16878                 }
16879             }
16880             if(ddMatch && format){
16881                 var fvalue = d.dateFormat(format);
16882                 if(ddMatch.test(fvalue)){
16883                     cell.title = ddText.replace("%0", fvalue);
16884                     cell.className = " fc-state-disabled";
16885                 }
16886             }
16887             
16888             if (!cell.initialClassName) {
16889                 cell.initialClassName = cell.dom.className;
16890             }
16891             
16892             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16893         };
16894
16895         var i = 0;
16896         
16897         for(; i < startingPos; i++) {
16898             textEls[i].innerHTML = (++prevStart);
16899             d.setDate(d.getDate()+1);
16900             
16901             cells[i].className = "fc-past fc-other-month";
16902             setCellClass(this, cells[i]);
16903         }
16904         
16905         var intDay = 0;
16906         
16907         for(; i < days; i++){
16908             intDay = i - startingPos + 1;
16909             textEls[i].innerHTML = (intDay);
16910             d.setDate(d.getDate()+1);
16911             
16912             cells[i].className = ''; // "x-date-active";
16913             setCellClass(this, cells[i]);
16914         }
16915         var extraDays = 0;
16916         
16917         for(; i < 42; i++) {
16918             textEls[i].innerHTML = (++extraDays);
16919             d.setDate(d.getDate()+1);
16920             
16921             cells[i].className = "fc-future fc-other-month";
16922             setCellClass(this, cells[i]);
16923         }
16924         
16925         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16926         
16927         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16928         
16929         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16930         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16931         
16932         if(totalRows != 6){
16933             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16934             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16935         }
16936         
16937         this.fireEvent('monthchange', this, date);
16938         
16939         
16940         /*
16941         if(!this.internalRender){
16942             var main = this.el.dom.firstChild;
16943             var w = main.offsetWidth;
16944             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16945             Roo.fly(main).setWidth(w);
16946             this.internalRender = true;
16947             // opera does not respect the auto grow header center column
16948             // then, after it gets a width opera refuses to recalculate
16949             // without a second pass
16950             if(Roo.isOpera && !this.secondPass){
16951                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16952                 this.secondPass = true;
16953                 this.update.defer(10, this, [date]);
16954             }
16955         }
16956         */
16957         
16958     },
16959     
16960     findCell : function(dt) {
16961         dt = dt.clearTime().getTime();
16962         var ret = false;
16963         this.cells.each(function(c){
16964             //Roo.log("check " +c.dateValue + '?=' + dt);
16965             if(c.dateValue == dt){
16966                 ret = c;
16967                 return false;
16968             }
16969             return true;
16970         });
16971         
16972         return ret;
16973     },
16974     
16975     findCells : function(ev) {
16976         var s = ev.start.clone().clearTime().getTime();
16977        // Roo.log(s);
16978         var e= ev.end.clone().clearTime().getTime();
16979        // Roo.log(e);
16980         var ret = [];
16981         this.cells.each(function(c){
16982              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16983             
16984             if(c.dateValue > e){
16985                 return ;
16986             }
16987             if(c.dateValue < s){
16988                 return ;
16989             }
16990             ret.push(c);
16991         });
16992         
16993         return ret;    
16994     },
16995     
16996 //    findBestRow: function(cells)
16997 //    {
16998 //        var ret = 0;
16999 //        
17000 //        for (var i =0 ; i < cells.length;i++) {
17001 //            ret  = Math.max(cells[i].rows || 0,ret);
17002 //        }
17003 //        return ret;
17004 //        
17005 //    },
17006     
17007     
17008     addItem : function(ev)
17009     {
17010         // look for vertical location slot in
17011         var cells = this.findCells(ev);
17012         
17013 //        ev.row = this.findBestRow(cells);
17014         
17015         // work out the location.
17016         
17017         var crow = false;
17018         var rows = [];
17019         for(var i =0; i < cells.length; i++) {
17020             
17021             cells[i].row = cells[0].row;
17022             
17023             if(i == 0){
17024                 cells[i].row = cells[i].row + 1;
17025             }
17026             
17027             if (!crow) {
17028                 crow = {
17029                     start : cells[i],
17030                     end :  cells[i]
17031                 };
17032                 continue;
17033             }
17034             if (crow.start.getY() == cells[i].getY()) {
17035                 // on same row.
17036                 crow.end = cells[i];
17037                 continue;
17038             }
17039             // different row.
17040             rows.push(crow);
17041             crow = {
17042                 start: cells[i],
17043                 end : cells[i]
17044             };
17045             
17046         }
17047         
17048         rows.push(crow);
17049         ev.els = [];
17050         ev.rows = rows;
17051         ev.cells = cells;
17052         
17053         cells[0].events.push(ev);
17054         
17055         this.calevents.push(ev);
17056     },
17057     
17058     clearEvents: function() {
17059         
17060         if(!this.calevents){
17061             return;
17062         }
17063         
17064         Roo.each(this.cells.elements, function(c){
17065             c.row = 0;
17066             c.events = [];
17067             c.more = [];
17068         });
17069         
17070         Roo.each(this.calevents, function(e) {
17071             Roo.each(e.els, function(el) {
17072                 el.un('mouseenter' ,this.onEventEnter, this);
17073                 el.un('mouseleave' ,this.onEventLeave, this);
17074                 el.remove();
17075             },this);
17076         },this);
17077         
17078         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17079             e.remove();
17080         });
17081         
17082     },
17083     
17084     renderEvents: function()
17085     {   
17086         var _this = this;
17087         
17088         this.cells.each(function(c) {
17089             
17090             if(c.row < 5){
17091                 return;
17092             }
17093             
17094             var ev = c.events;
17095             
17096             var r = 4;
17097             if(c.row != c.events.length){
17098                 r = 4 - (4 - (c.row - c.events.length));
17099             }
17100             
17101             c.events = ev.slice(0, r);
17102             c.more = ev.slice(r);
17103             
17104             if(c.more.length && c.more.length == 1){
17105                 c.events.push(c.more.pop());
17106             }
17107             
17108             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17109             
17110         });
17111             
17112         this.cells.each(function(c) {
17113             
17114             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17115             
17116             
17117             for (var e = 0; e < c.events.length; e++){
17118                 var ev = c.events[e];
17119                 var rows = ev.rows;
17120                 
17121                 for(var i = 0; i < rows.length; i++) {
17122                 
17123                     // how many rows should it span..
17124
17125                     var  cfg = {
17126                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17127                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17128
17129                         unselectable : "on",
17130                         cn : [
17131                             {
17132                                 cls: 'fc-event-inner',
17133                                 cn : [
17134     //                                {
17135     //                                  tag:'span',
17136     //                                  cls: 'fc-event-time',
17137     //                                  html : cells.length > 1 ? '' : ev.time
17138     //                                },
17139                                     {
17140                                       tag:'span',
17141                                       cls: 'fc-event-title',
17142                                       html : String.format('{0}', ev.title)
17143                                     }
17144
17145
17146                                 ]
17147                             },
17148                             {
17149                                 cls: 'ui-resizable-handle ui-resizable-e',
17150                                 html : '&nbsp;&nbsp;&nbsp'
17151                             }
17152
17153                         ]
17154                     };
17155
17156                     if (i == 0) {
17157                         cfg.cls += ' fc-event-start';
17158                     }
17159                     if ((i+1) == rows.length) {
17160                         cfg.cls += ' fc-event-end';
17161                     }
17162
17163                     var ctr = _this.el.select('.fc-event-container',true).first();
17164                     var cg = ctr.createChild(cfg);
17165
17166                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17167                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17168
17169                     var r = (c.more.length) ? 1 : 0;
17170                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17171                     cg.setWidth(ebox.right - sbox.x -2);
17172
17173                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17174                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17175                     cg.on('click', _this.onEventClick, _this, ev);
17176
17177                     ev.els.push(cg);
17178                     
17179                 }
17180                 
17181             }
17182             
17183             
17184             if(c.more.length){
17185                 var  cfg = {
17186                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17187                     style : 'position: absolute',
17188                     unselectable : "on",
17189                     cn : [
17190                         {
17191                             cls: 'fc-event-inner',
17192                             cn : [
17193                                 {
17194                                   tag:'span',
17195                                   cls: 'fc-event-title',
17196                                   html : 'More'
17197                                 }
17198
17199
17200                             ]
17201                         },
17202                         {
17203                             cls: 'ui-resizable-handle ui-resizable-e',
17204                             html : '&nbsp;&nbsp;&nbsp'
17205                         }
17206
17207                     ]
17208                 };
17209
17210                 var ctr = _this.el.select('.fc-event-container',true).first();
17211                 var cg = ctr.createChild(cfg);
17212
17213                 var sbox = c.select('.fc-day-content',true).first().getBox();
17214                 var ebox = c.select('.fc-day-content',true).first().getBox();
17215                 //Roo.log(cg);
17216                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17217                 cg.setWidth(ebox.right - sbox.x -2);
17218
17219                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17220                 
17221             }
17222             
17223         });
17224         
17225         
17226         
17227     },
17228     
17229     onEventEnter: function (e, el,event,d) {
17230         this.fireEvent('evententer', this, el, event);
17231     },
17232     
17233     onEventLeave: function (e, el,event,d) {
17234         this.fireEvent('eventleave', this, el, event);
17235     },
17236     
17237     onEventClick: function (e, el,event,d) {
17238         this.fireEvent('eventclick', this, el, event);
17239     },
17240     
17241     onMonthChange: function () {
17242         this.store.load();
17243     },
17244     
17245     onMoreEventClick: function(e, el, more)
17246     {
17247         var _this = this;
17248         
17249         this.calpopover.placement = 'right';
17250         this.calpopover.setTitle('More');
17251         
17252         this.calpopover.setContent('');
17253         
17254         var ctr = this.calpopover.el.select('.popover-content', true).first();
17255         
17256         Roo.each(more, function(m){
17257             var cfg = {
17258                 cls : 'fc-event-hori fc-event-draggable',
17259                 html : m.title
17260             };
17261             var cg = ctr.createChild(cfg);
17262             
17263             cg.on('click', _this.onEventClick, _this, m);
17264         });
17265         
17266         this.calpopover.show(el);
17267         
17268         
17269     },
17270     
17271     onLoad: function () 
17272     {   
17273         this.calevents = [];
17274         var cal = this;
17275         
17276         if(this.store.getCount() > 0){
17277             this.store.data.each(function(d){
17278                cal.addItem({
17279                     id : d.data.id,
17280                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17281                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17282                     time : d.data.start_time,
17283                     title : d.data.title,
17284                     description : d.data.description,
17285                     venue : d.data.venue
17286                 });
17287             });
17288         }
17289         
17290         this.renderEvents();
17291         
17292         if(this.calevents.length && this.loadMask){
17293             this.maskEl.hide();
17294         }
17295     },
17296     
17297     onBeforeLoad: function()
17298     {
17299         this.clearEvents();
17300         if(this.loadMask){
17301             this.maskEl.show();
17302         }
17303     }
17304 });
17305
17306  
17307  /*
17308  * - LGPL
17309  *
17310  * element
17311  * 
17312  */
17313
17314 /**
17315  * @class Roo.bootstrap.Popover
17316  * @extends Roo.bootstrap.Component
17317  * Bootstrap Popover class
17318  * @cfg {String} html contents of the popover   (or false to use children..)
17319  * @cfg {String} title of popover (or false to hide)
17320  * @cfg {String} placement how it is placed
17321  * @cfg {String} trigger click || hover (or false to trigger manually)
17322  * @cfg {String} over what (parent or false to trigger manually.)
17323  * @cfg {Number} delay - delay before showing
17324  
17325  * @constructor
17326  * Create a new Popover
17327  * @param {Object} config The config object
17328  */
17329
17330 Roo.bootstrap.Popover = function(config){
17331     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17332     
17333     this.addEvents({
17334         // raw events
17335          /**
17336          * @event show
17337          * After the popover show
17338          * 
17339          * @param {Roo.bootstrap.Popover} this
17340          */
17341         "show" : true,
17342         /**
17343          * @event hide
17344          * After the popover hide
17345          * 
17346          * @param {Roo.bootstrap.Popover} this
17347          */
17348         "hide" : true
17349     });
17350 };
17351
17352 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17353     
17354     title: 'Fill in a title',
17355     html: false,
17356     
17357     placement : 'right',
17358     trigger : 'hover', // hover
17359     
17360     delay : 0,
17361     
17362     over: 'parent',
17363     
17364     can_build_overlaid : false,
17365     
17366     getChildContainer : function()
17367     {
17368         return this.el.select('.popover-content',true).first();
17369     },
17370     
17371     getAutoCreate : function(){
17372          
17373         var cfg = {
17374            cls : 'popover roo-dynamic',
17375            style: 'display:block',
17376            cn : [
17377                 {
17378                     cls : 'arrow'
17379                 },
17380                 {
17381                     cls : 'popover-inner',
17382                     cn : [
17383                         {
17384                             tag: 'h3',
17385                             cls: 'popover-title',
17386                             html : this.title
17387                         },
17388                         {
17389                             cls : 'popover-content',
17390                             html : this.html
17391                         }
17392                     ]
17393                     
17394                 }
17395            ]
17396         };
17397         
17398         return cfg;
17399     },
17400     setTitle: function(str)
17401     {
17402         this.title = str;
17403         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17404     },
17405     setContent: function(str)
17406     {
17407         this.html = str;
17408         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17409     },
17410     // as it get's added to the bottom of the page.
17411     onRender : function(ct, position)
17412     {
17413         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17414         if(!this.el){
17415             var cfg = Roo.apply({},  this.getAutoCreate());
17416             cfg.id = Roo.id();
17417             
17418             if (this.cls) {
17419                 cfg.cls += ' ' + this.cls;
17420             }
17421             if (this.style) {
17422                 cfg.style = this.style;
17423             }
17424             //Roo.log("adding to ");
17425             this.el = Roo.get(document.body).createChild(cfg, position);
17426 //            Roo.log(this.el);
17427         }
17428         this.initEvents();
17429     },
17430     
17431     initEvents : function()
17432     {
17433         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17434         this.el.enableDisplayMode('block');
17435         this.el.hide();
17436         if (this.over === false) {
17437             return; 
17438         }
17439         if (this.triggers === false) {
17440             return;
17441         }
17442         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17443         var triggers = this.trigger ? this.trigger.split(' ') : [];
17444         Roo.each(triggers, function(trigger) {
17445         
17446             if (trigger == 'click') {
17447                 on_el.on('click', this.toggle, this);
17448             } else if (trigger != 'manual') {
17449                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17450                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17451       
17452                 on_el.on(eventIn  ,this.enter, this);
17453                 on_el.on(eventOut, this.leave, this);
17454             }
17455         }, this);
17456         
17457     },
17458     
17459     
17460     // private
17461     timeout : null,
17462     hoverState : null,
17463     
17464     toggle : function () {
17465         this.hoverState == 'in' ? this.leave() : this.enter();
17466     },
17467     
17468     enter : function () {
17469         
17470         clearTimeout(this.timeout);
17471     
17472         this.hoverState = 'in';
17473     
17474         if (!this.delay || !this.delay.show) {
17475             this.show();
17476             return;
17477         }
17478         var _t = this;
17479         this.timeout = setTimeout(function () {
17480             if (_t.hoverState == 'in') {
17481                 _t.show();
17482             }
17483         }, this.delay.show)
17484     },
17485     
17486     leave : function() {
17487         clearTimeout(this.timeout);
17488     
17489         this.hoverState = 'out';
17490     
17491         if (!this.delay || !this.delay.hide) {
17492             this.hide();
17493             return;
17494         }
17495         var _t = this;
17496         this.timeout = setTimeout(function () {
17497             if (_t.hoverState == 'out') {
17498                 _t.hide();
17499             }
17500         }, this.delay.hide)
17501     },
17502     
17503     show : function (on_el)
17504     {
17505         if (!on_el) {
17506             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17507         }
17508         
17509         // set content.
17510         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17511         if (this.html !== false) {
17512             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17513         }
17514         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17515         if (!this.title.length) {
17516             this.el.select('.popover-title',true).hide();
17517         }
17518         
17519         var placement = typeof this.placement == 'function' ?
17520             this.placement.call(this, this.el, on_el) :
17521             this.placement;
17522             
17523         var autoToken = /\s?auto?\s?/i;
17524         var autoPlace = autoToken.test(placement);
17525         if (autoPlace) {
17526             placement = placement.replace(autoToken, '') || 'top';
17527         }
17528         
17529         //this.el.detach()
17530         //this.el.setXY([0,0]);
17531         this.el.show();
17532         this.el.dom.style.display='block';
17533         this.el.addClass(placement);
17534         
17535         //this.el.appendTo(on_el);
17536         
17537         var p = this.getPosition();
17538         var box = this.el.getBox();
17539         
17540         if (autoPlace) {
17541             // fixme..
17542         }
17543         var align = Roo.bootstrap.Popover.alignment[placement];
17544         
17545 //        Roo.log(align);
17546         this.el.alignTo(on_el, align[0],align[1]);
17547         //var arrow = this.el.select('.arrow',true).first();
17548         //arrow.set(align[2], 
17549         
17550         this.el.addClass('in');
17551         
17552         
17553         if (this.el.hasClass('fade')) {
17554             // fade it?
17555         }
17556         
17557         this.hoverState = 'in';
17558         
17559         this.fireEvent('show', this);
17560         
17561     },
17562     hide : function()
17563     {
17564         this.el.setXY([0,0]);
17565         this.el.removeClass('in');
17566         this.el.hide();
17567         this.hoverState = null;
17568         
17569         this.fireEvent('hide', this);
17570     }
17571     
17572 });
17573
17574 Roo.bootstrap.Popover.alignment = {
17575     'left' : ['r-l', [-10,0], 'right'],
17576     'right' : ['l-r', [10,0], 'left'],
17577     'bottom' : ['t-b', [0,10], 'top'],
17578     'top' : [ 'b-t', [0,-10], 'bottom']
17579 };
17580
17581  /*
17582  * - LGPL
17583  *
17584  * Progress
17585  * 
17586  */
17587
17588 /**
17589  * @class Roo.bootstrap.Progress
17590  * @extends Roo.bootstrap.Component
17591  * Bootstrap Progress class
17592  * @cfg {Boolean} striped striped of the progress bar
17593  * @cfg {Boolean} active animated of the progress bar
17594  * 
17595  * 
17596  * @constructor
17597  * Create a new Progress
17598  * @param {Object} config The config object
17599  */
17600
17601 Roo.bootstrap.Progress = function(config){
17602     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17603 };
17604
17605 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17606     
17607     striped : false,
17608     active: false,
17609     
17610     getAutoCreate : function(){
17611         var cfg = {
17612             tag: 'div',
17613             cls: 'progress'
17614         };
17615         
17616         
17617         if(this.striped){
17618             cfg.cls += ' progress-striped';
17619         }
17620       
17621         if(this.active){
17622             cfg.cls += ' active';
17623         }
17624         
17625         
17626         return cfg;
17627     }
17628    
17629 });
17630
17631  
17632
17633  /*
17634  * - LGPL
17635  *
17636  * ProgressBar
17637  * 
17638  */
17639
17640 /**
17641  * @class Roo.bootstrap.ProgressBar
17642  * @extends Roo.bootstrap.Component
17643  * Bootstrap ProgressBar class
17644  * @cfg {Number} aria_valuenow aria-value now
17645  * @cfg {Number} aria_valuemin aria-value min
17646  * @cfg {Number} aria_valuemax aria-value max
17647  * @cfg {String} label label for the progress bar
17648  * @cfg {String} panel (success | info | warning | danger )
17649  * @cfg {String} role role of the progress bar
17650  * @cfg {String} sr_only text
17651  * 
17652  * 
17653  * @constructor
17654  * Create a new ProgressBar
17655  * @param {Object} config The config object
17656  */
17657
17658 Roo.bootstrap.ProgressBar = function(config){
17659     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17660 };
17661
17662 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17663     
17664     aria_valuenow : 0,
17665     aria_valuemin : 0,
17666     aria_valuemax : 100,
17667     label : false,
17668     panel : false,
17669     role : false,
17670     sr_only: false,
17671     
17672     getAutoCreate : function()
17673     {
17674         
17675         var cfg = {
17676             tag: 'div',
17677             cls: 'progress-bar',
17678             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17679         };
17680         
17681         if(this.sr_only){
17682             cfg.cn = {
17683                 tag: 'span',
17684                 cls: 'sr-only',
17685                 html: this.sr_only
17686             }
17687         }
17688         
17689         if(this.role){
17690             cfg.role = this.role;
17691         }
17692         
17693         if(this.aria_valuenow){
17694             cfg['aria-valuenow'] = this.aria_valuenow;
17695         }
17696         
17697         if(this.aria_valuemin){
17698             cfg['aria-valuemin'] = this.aria_valuemin;
17699         }
17700         
17701         if(this.aria_valuemax){
17702             cfg['aria-valuemax'] = this.aria_valuemax;
17703         }
17704         
17705         if(this.label && !this.sr_only){
17706             cfg.html = this.label;
17707         }
17708         
17709         if(this.panel){
17710             cfg.cls += ' progress-bar-' + this.panel;
17711         }
17712         
17713         return cfg;
17714     },
17715     
17716     update : function(aria_valuenow)
17717     {
17718         this.aria_valuenow = aria_valuenow;
17719         
17720         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17721     }
17722    
17723 });
17724
17725  
17726
17727  /*
17728  * - LGPL
17729  *
17730  * column
17731  * 
17732  */
17733
17734 /**
17735  * @class Roo.bootstrap.TabGroup
17736  * @extends Roo.bootstrap.Column
17737  * Bootstrap Column class
17738  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17739  * @cfg {Boolean} carousel true to make the group behave like a carousel
17740  * @cfg {Boolean} bullets show bullets for the panels
17741  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17742  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17743  * @cfg {Boolean} showarrow (true|false) show arrow default true
17744  * 
17745  * @constructor
17746  * Create a new TabGroup
17747  * @param {Object} config The config object
17748  */
17749
17750 Roo.bootstrap.TabGroup = function(config){
17751     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17752     if (!this.navId) {
17753         this.navId = Roo.id();
17754     }
17755     this.tabs = [];
17756     Roo.bootstrap.TabGroup.register(this);
17757     
17758 };
17759
17760 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17761     
17762     carousel : false,
17763     transition : false,
17764     bullets : 0,
17765     timer : 0,
17766     autoslide : false,
17767     slideFn : false,
17768     slideOnTouch : false,
17769     showarrow : true,
17770     
17771     getAutoCreate : function()
17772     {
17773         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17774         
17775         cfg.cls += ' tab-content';
17776         
17777         if (this.carousel) {
17778             cfg.cls += ' carousel slide';
17779             
17780             cfg.cn = [{
17781                cls : 'carousel-inner',
17782                cn : []
17783             }];
17784         
17785             if(this.bullets  && !Roo.isTouch){
17786                 
17787                 var bullets = {
17788                     cls : 'carousel-bullets',
17789                     cn : []
17790                 };
17791                
17792                 if(this.bullets_cls){
17793                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17794                 }
17795                 
17796                 bullets.cn.push({
17797                     cls : 'clear'
17798                 });
17799                 
17800                 cfg.cn[0].cn.push(bullets);
17801             }
17802             
17803             if(this.showarrow){
17804                 cfg.cn[0].cn.push({
17805                     tag : 'div',
17806                     class : 'carousel-arrow',
17807                     cn : [
17808                         {
17809                             tag : 'div',
17810                             class : 'carousel-prev',
17811                             cn : [
17812                                 {
17813                                     tag : 'i',
17814                                     class : 'fa fa-chevron-left'
17815                                 }
17816                             ]
17817                         },
17818                         {
17819                             tag : 'div',
17820                             class : 'carousel-next',
17821                             cn : [
17822                                 {
17823                                     tag : 'i',
17824                                     class : 'fa fa-chevron-right'
17825                                 }
17826                             ]
17827                         }
17828                     ]
17829                 });
17830             }
17831             
17832         }
17833         
17834         return cfg;
17835     },
17836     
17837     initEvents:  function()
17838     {
17839 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17840 //            this.el.on("touchstart", this.onTouchStart, this);
17841 //        }
17842         
17843         if(this.autoslide){
17844             var _this = this;
17845             
17846             this.slideFn = window.setInterval(function() {
17847                 _this.showPanelNext();
17848             }, this.timer);
17849         }
17850         
17851         if(this.showarrow){
17852             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17853             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17854         }
17855         
17856         
17857     },
17858     
17859 //    onTouchStart : function(e, el, o)
17860 //    {
17861 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17862 //            return;
17863 //        }
17864 //        
17865 //        this.showPanelNext();
17866 //    },
17867     
17868     
17869     getChildContainer : function()
17870     {
17871         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17872     },
17873     
17874     /**
17875     * register a Navigation item
17876     * @param {Roo.bootstrap.NavItem} the navitem to add
17877     */
17878     register : function(item)
17879     {
17880         this.tabs.push( item);
17881         item.navId = this.navId; // not really needed..
17882         this.addBullet();
17883     
17884     },
17885     
17886     getActivePanel : function()
17887     {
17888         var r = false;
17889         Roo.each(this.tabs, function(t) {
17890             if (t.active) {
17891                 r = t;
17892                 return false;
17893             }
17894             return null;
17895         });
17896         return r;
17897         
17898     },
17899     getPanelByName : function(n)
17900     {
17901         var r = false;
17902         Roo.each(this.tabs, function(t) {
17903             if (t.tabId == n) {
17904                 r = t;
17905                 return false;
17906             }
17907             return null;
17908         });
17909         return r;
17910     },
17911     indexOfPanel : function(p)
17912     {
17913         var r = false;
17914         Roo.each(this.tabs, function(t,i) {
17915             if (t.tabId == p.tabId) {
17916                 r = i;
17917                 return false;
17918             }
17919             return null;
17920         });
17921         return r;
17922     },
17923     /**
17924      * show a specific panel
17925      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17926      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17927      */
17928     showPanel : function (pan)
17929     {
17930         if(this.transition || typeof(pan) == 'undefined'){
17931             Roo.log("waiting for the transitionend");
17932             return;
17933         }
17934         
17935         if (typeof(pan) == 'number') {
17936             pan = this.tabs[pan];
17937         }
17938         
17939         if (typeof(pan) == 'string') {
17940             pan = this.getPanelByName(pan);
17941         }
17942         
17943         var cur = this.getActivePanel();
17944         
17945         if(!pan || !cur){
17946             Roo.log('pan or acitve pan is undefined');
17947             return false;
17948         }
17949         
17950         if (pan.tabId == this.getActivePanel().tabId) {
17951             return true;
17952         }
17953         
17954         if (false === cur.fireEvent('beforedeactivate')) {
17955             return false;
17956         }
17957         
17958         if(this.bullets > 0 && !Roo.isTouch){
17959             this.setActiveBullet(this.indexOfPanel(pan));
17960         }
17961         
17962         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17963             
17964             this.transition = true;
17965             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17966             var lr = dir == 'next' ? 'left' : 'right';
17967             pan.el.addClass(dir); // or prev
17968             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17969             cur.el.addClass(lr); // or right
17970             pan.el.addClass(lr);
17971             
17972             var _this = this;
17973             cur.el.on('transitionend', function() {
17974                 Roo.log("trans end?");
17975                 
17976                 pan.el.removeClass([lr,dir]);
17977                 pan.setActive(true);
17978                 
17979                 cur.el.removeClass([lr]);
17980                 cur.setActive(false);
17981                 
17982                 _this.transition = false;
17983                 
17984             }, this, { single:  true } );
17985             
17986             return true;
17987         }
17988         
17989         cur.setActive(false);
17990         pan.setActive(true);
17991         
17992         return true;
17993         
17994     },
17995     showPanelNext : function()
17996     {
17997         var i = this.indexOfPanel(this.getActivePanel());
17998         
17999         if (i >= this.tabs.length - 1 && !this.autoslide) {
18000             return;
18001         }
18002         
18003         if (i >= this.tabs.length - 1 && this.autoslide) {
18004             i = -1;
18005         }
18006         
18007         this.showPanel(this.tabs[i+1]);
18008     },
18009     
18010     showPanelPrev : function()
18011     {
18012         var i = this.indexOfPanel(this.getActivePanel());
18013         
18014         if (i  < 1 && !this.autoslide) {
18015             return;
18016         }
18017         
18018         if (i < 1 && this.autoslide) {
18019             i = this.tabs.length;
18020         }
18021         
18022         this.showPanel(this.tabs[i-1]);
18023     },
18024     
18025     
18026     addBullet: function()
18027     {
18028         if(!this.bullets || Roo.isTouch){
18029             return;
18030         }
18031         var ctr = this.el.select('.carousel-bullets',true).first();
18032         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18033         var bullet = ctr.createChild({
18034             cls : 'bullet bullet-' + i
18035         },ctr.dom.lastChild);
18036         
18037         
18038         var _this = this;
18039         
18040         bullet.on('click', (function(e, el, o, ii, t){
18041
18042             e.preventDefault();
18043
18044             this.showPanel(ii);
18045
18046             if(this.autoslide && this.slideFn){
18047                 clearInterval(this.slideFn);
18048                 this.slideFn = window.setInterval(function() {
18049                     _this.showPanelNext();
18050                 }, this.timer);
18051             }
18052
18053         }).createDelegate(this, [i, bullet], true));
18054                 
18055         
18056     },
18057      
18058     setActiveBullet : function(i)
18059     {
18060         if(Roo.isTouch){
18061             return;
18062         }
18063         
18064         Roo.each(this.el.select('.bullet', true).elements, function(el){
18065             el.removeClass('selected');
18066         });
18067
18068         var bullet = this.el.select('.bullet-' + i, true).first();
18069         
18070         if(!bullet){
18071             return;
18072         }
18073         
18074         bullet.addClass('selected');
18075     }
18076     
18077     
18078   
18079 });
18080
18081  
18082
18083  
18084  
18085 Roo.apply(Roo.bootstrap.TabGroup, {
18086     
18087     groups: {},
18088      /**
18089     * register a Navigation Group
18090     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18091     */
18092     register : function(navgrp)
18093     {
18094         this.groups[navgrp.navId] = navgrp;
18095         
18096     },
18097     /**
18098     * fetch a Navigation Group based on the navigation ID
18099     * if one does not exist , it will get created.
18100     * @param {string} the navgroup to add
18101     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18102     */
18103     get: function(navId) {
18104         if (typeof(this.groups[navId]) == 'undefined') {
18105             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18106         }
18107         return this.groups[navId] ;
18108     }
18109     
18110     
18111     
18112 });
18113
18114  /*
18115  * - LGPL
18116  *
18117  * TabPanel
18118  * 
18119  */
18120
18121 /**
18122  * @class Roo.bootstrap.TabPanel
18123  * @extends Roo.bootstrap.Component
18124  * Bootstrap TabPanel class
18125  * @cfg {Boolean} active panel active
18126  * @cfg {String} html panel content
18127  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18128  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18129  * @cfg {String} href click to link..
18130  * 
18131  * 
18132  * @constructor
18133  * Create a new TabPanel
18134  * @param {Object} config The config object
18135  */
18136
18137 Roo.bootstrap.TabPanel = function(config){
18138     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18139     this.addEvents({
18140         /**
18141              * @event changed
18142              * Fires when the active status changes
18143              * @param {Roo.bootstrap.TabPanel} this
18144              * @param {Boolean} state the new state
18145             
18146          */
18147         'changed': true,
18148         /**
18149              * @event beforedeactivate
18150              * Fires before a tab is de-activated - can be used to do validation on a form.
18151              * @param {Roo.bootstrap.TabPanel} this
18152              * @return {Boolean} false if there is an error
18153             
18154          */
18155         'beforedeactivate': true
18156      });
18157     
18158     this.tabId = this.tabId || Roo.id();
18159   
18160 };
18161
18162 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18163     
18164     active: false,
18165     html: false,
18166     tabId: false,
18167     navId : false,
18168     href : '',
18169     
18170     getAutoCreate : function(){
18171         var cfg = {
18172             tag: 'div',
18173             // item is needed for carousel - not sure if it has any effect otherwise
18174             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18175             html: this.html || ''
18176         };
18177         
18178         if(this.active){
18179             cfg.cls += ' active';
18180         }
18181         
18182         if(this.tabId){
18183             cfg.tabId = this.tabId;
18184         }
18185         
18186         
18187         return cfg;
18188     },
18189     
18190     initEvents:  function()
18191     {
18192         var p = this.parent();
18193         
18194         this.navId = this.navId || p.navId;
18195         
18196         if (typeof(this.navId) != 'undefined') {
18197             // not really needed.. but just in case.. parent should be a NavGroup.
18198             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18199             
18200             tg.register(this);
18201             
18202             var i = tg.tabs.length - 1;
18203             
18204             if(this.active && tg.bullets > 0 && i < tg.bullets){
18205                 tg.setActiveBullet(i);
18206             }
18207         }
18208         
18209         this.el.on('click', this.onClick, this);
18210         
18211         if(Roo.isTouch){
18212             this.el.on("touchstart", this.onTouchStart, this);
18213             this.el.on("touchmove", this.onTouchMove, this);
18214             this.el.on("touchend", this.onTouchEnd, this);
18215         }
18216         
18217     },
18218     
18219     onRender : function(ct, position)
18220     {
18221         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18222     },
18223     
18224     setActive : function(state)
18225     {
18226         Roo.log("panel - set active " + this.tabId + "=" + state);
18227         
18228         this.active = state;
18229         if (!state) {
18230             this.el.removeClass('active');
18231             
18232         } else  if (!this.el.hasClass('active')) {
18233             this.el.addClass('active');
18234         }
18235         
18236         this.fireEvent('changed', this, state);
18237     },
18238     
18239     onClick : function(e)
18240     {
18241         e.preventDefault();
18242         
18243         if(!this.href.length){
18244             return;
18245         }
18246         
18247         window.location.href = this.href;
18248     },
18249     
18250     startX : 0,
18251     startY : 0,
18252     endX : 0,
18253     endY : 0,
18254     swiping : false,
18255     
18256     onTouchStart : function(e)
18257     {
18258         this.swiping = false;
18259         
18260         this.startX = e.browserEvent.touches[0].clientX;
18261         this.startY = e.browserEvent.touches[0].clientY;
18262     },
18263     
18264     onTouchMove : function(e)
18265     {
18266         this.swiping = true;
18267         
18268         this.endX = e.browserEvent.touches[0].clientX;
18269         this.endY = e.browserEvent.touches[0].clientY;
18270     },
18271     
18272     onTouchEnd : function(e)
18273     {
18274         if(!this.swiping){
18275             this.onClick(e);
18276             return;
18277         }
18278         
18279         var tabGroup = this.parent();
18280         
18281         if(this.endX > this.startX){ // swiping right
18282             tabGroup.showPanelPrev();
18283             return;
18284         }
18285         
18286         if(this.startX > this.endX){ // swiping left
18287             tabGroup.showPanelNext();
18288             return;
18289         }
18290     }
18291     
18292     
18293 });
18294  
18295
18296  
18297
18298  /*
18299  * - LGPL
18300  *
18301  * DateField
18302  * 
18303  */
18304
18305 /**
18306  * @class Roo.bootstrap.DateField
18307  * @extends Roo.bootstrap.Input
18308  * Bootstrap DateField class
18309  * @cfg {Number} weekStart default 0
18310  * @cfg {String} viewMode default empty, (months|years)
18311  * @cfg {String} minViewMode default empty, (months|years)
18312  * @cfg {Number} startDate default -Infinity
18313  * @cfg {Number} endDate default Infinity
18314  * @cfg {Boolean} todayHighlight default false
18315  * @cfg {Boolean} todayBtn default false
18316  * @cfg {Boolean} calendarWeeks default false
18317  * @cfg {Object} daysOfWeekDisabled default empty
18318  * @cfg {Boolean} singleMode default false (true | false)
18319  * 
18320  * @cfg {Boolean} keyboardNavigation default true
18321  * @cfg {String} language default en
18322  * 
18323  * @constructor
18324  * Create a new DateField
18325  * @param {Object} config The config object
18326  */
18327
18328 Roo.bootstrap.DateField = function(config){
18329     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18330      this.addEvents({
18331             /**
18332              * @event show
18333              * Fires when this field show.
18334              * @param {Roo.bootstrap.DateField} this
18335              * @param {Mixed} date The date value
18336              */
18337             show : true,
18338             /**
18339              * @event show
18340              * Fires when this field hide.
18341              * @param {Roo.bootstrap.DateField} this
18342              * @param {Mixed} date The date value
18343              */
18344             hide : true,
18345             /**
18346              * @event select
18347              * Fires when select a date.
18348              * @param {Roo.bootstrap.DateField} this
18349              * @param {Mixed} date The date value
18350              */
18351             select : true,
18352             /**
18353              * @event beforeselect
18354              * Fires when before select a date.
18355              * @param {Roo.bootstrap.DateField} this
18356              * @param {Mixed} date The date value
18357              */
18358             beforeselect : true
18359         });
18360 };
18361
18362 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18363     
18364     /**
18365      * @cfg {String} format
18366      * The default date format string which can be overriden for localization support.  The format must be
18367      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18368      */
18369     format : "m/d/y",
18370     /**
18371      * @cfg {String} altFormats
18372      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18373      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18374      */
18375     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18376     
18377     weekStart : 0,
18378     
18379     viewMode : '',
18380     
18381     minViewMode : '',
18382     
18383     todayHighlight : false,
18384     
18385     todayBtn: false,
18386     
18387     language: 'en',
18388     
18389     keyboardNavigation: true,
18390     
18391     calendarWeeks: false,
18392     
18393     startDate: -Infinity,
18394     
18395     endDate: Infinity,
18396     
18397     daysOfWeekDisabled: [],
18398     
18399     _events: [],
18400     
18401     singleMode : false,
18402     
18403     UTCDate: function()
18404     {
18405         return new Date(Date.UTC.apply(Date, arguments));
18406     },
18407     
18408     UTCToday: function()
18409     {
18410         var today = new Date();
18411         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18412     },
18413     
18414     getDate: function() {
18415             var d = this.getUTCDate();
18416             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18417     },
18418     
18419     getUTCDate: function() {
18420             return this.date;
18421     },
18422     
18423     setDate: function(d) {
18424             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18425     },
18426     
18427     setUTCDate: function(d) {
18428             this.date = d;
18429             this.setValue(this.formatDate(this.date));
18430     },
18431         
18432     onRender: function(ct, position)
18433     {
18434         
18435         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18436         
18437         this.language = this.language || 'en';
18438         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18439         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18440         
18441         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18442         this.format = this.format || 'm/d/y';
18443         this.isInline = false;
18444         this.isInput = true;
18445         this.component = this.el.select('.add-on', true).first() || false;
18446         this.component = (this.component && this.component.length === 0) ? false : this.component;
18447         this.hasInput = this.component && this.inputEl().length;
18448         
18449         if (typeof(this.minViewMode === 'string')) {
18450             switch (this.minViewMode) {
18451                 case 'months':
18452                     this.minViewMode = 1;
18453                     break;
18454                 case 'years':
18455                     this.minViewMode = 2;
18456                     break;
18457                 default:
18458                     this.minViewMode = 0;
18459                     break;
18460             }
18461         }
18462         
18463         if (typeof(this.viewMode === 'string')) {
18464             switch (this.viewMode) {
18465                 case 'months':
18466                     this.viewMode = 1;
18467                     break;
18468                 case 'years':
18469                     this.viewMode = 2;
18470                     break;
18471                 default:
18472                     this.viewMode = 0;
18473                     break;
18474             }
18475         }
18476                 
18477         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18478         
18479 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18480         
18481         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18482         
18483         this.picker().on('mousedown', this.onMousedown, this);
18484         this.picker().on('click', this.onClick, this);
18485         
18486         this.picker().addClass('datepicker-dropdown');
18487         
18488         this.startViewMode = this.viewMode;
18489         
18490         if(this.singleMode){
18491             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18492                 v.setVisibilityMode(Roo.Element.DISPLAY);
18493                 v.hide();
18494             });
18495             
18496             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18497                 v.setStyle('width', '189px');
18498             });
18499         }
18500         
18501         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18502             if(!this.calendarWeeks){
18503                 v.remove();
18504                 return;
18505             }
18506             
18507             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18508             v.attr('colspan', function(i, val){
18509                 return parseInt(val) + 1;
18510             });
18511         });
18512                         
18513         
18514         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18515         
18516         this.setStartDate(this.startDate);
18517         this.setEndDate(this.endDate);
18518         
18519         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18520         
18521         this.fillDow();
18522         this.fillMonths();
18523         this.update();
18524         this.showMode();
18525         
18526         if(this.isInline) {
18527             this.show();
18528         }
18529     },
18530     
18531     picker : function()
18532     {
18533         return this.pickerEl;
18534 //        return this.el.select('.datepicker', true).first();
18535     },
18536     
18537     fillDow: function()
18538     {
18539         var dowCnt = this.weekStart;
18540         
18541         var dow = {
18542             tag: 'tr',
18543             cn: [
18544                 
18545             ]
18546         };
18547         
18548         if(this.calendarWeeks){
18549             dow.cn.push({
18550                 tag: 'th',
18551                 cls: 'cw',
18552                 html: '&nbsp;'
18553             })
18554         }
18555         
18556         while (dowCnt < this.weekStart + 7) {
18557             dow.cn.push({
18558                 tag: 'th',
18559                 cls: 'dow',
18560                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18561             });
18562         }
18563         
18564         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18565     },
18566     
18567     fillMonths: function()
18568     {    
18569         var i = 0;
18570         var months = this.picker().select('>.datepicker-months td', true).first();
18571         
18572         months.dom.innerHTML = '';
18573         
18574         while (i < 12) {
18575             var month = {
18576                 tag: 'span',
18577                 cls: 'month',
18578                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18579             };
18580             
18581             months.createChild(month);
18582         }
18583         
18584     },
18585     
18586     update: function()
18587     {
18588         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;
18589         
18590         if (this.date < this.startDate) {
18591             this.viewDate = new Date(this.startDate);
18592         } else if (this.date > this.endDate) {
18593             this.viewDate = new Date(this.endDate);
18594         } else {
18595             this.viewDate = new Date(this.date);
18596         }
18597         
18598         this.fill();
18599     },
18600     
18601     fill: function() 
18602     {
18603         var d = new Date(this.viewDate),
18604                 year = d.getUTCFullYear(),
18605                 month = d.getUTCMonth(),
18606                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18607                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18608                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18609                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18610                 currentDate = this.date && this.date.valueOf(),
18611                 today = this.UTCToday();
18612         
18613         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18614         
18615 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18616         
18617 //        this.picker.select('>tfoot th.today').
18618 //                                              .text(dates[this.language].today)
18619 //                                              .toggle(this.todayBtn !== false);
18620     
18621         this.updateNavArrows();
18622         this.fillMonths();
18623                                                 
18624         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18625         
18626         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18627          
18628         prevMonth.setUTCDate(day);
18629         
18630         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18631         
18632         var nextMonth = new Date(prevMonth);
18633         
18634         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18635         
18636         nextMonth = nextMonth.valueOf();
18637         
18638         var fillMonths = false;
18639         
18640         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18641         
18642         while(prevMonth.valueOf() < nextMonth) {
18643             var clsName = '';
18644             
18645             if (prevMonth.getUTCDay() === this.weekStart) {
18646                 if(fillMonths){
18647                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18648                 }
18649                     
18650                 fillMonths = {
18651                     tag: 'tr',
18652                     cn: []
18653                 };
18654                 
18655                 if(this.calendarWeeks){
18656                     // ISO 8601: First week contains first thursday.
18657                     // ISO also states week starts on Monday, but we can be more abstract here.
18658                     var
18659                     // Start of current week: based on weekstart/current date
18660                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18661                     // Thursday of this week
18662                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18663                     // First Thursday of year, year from thursday
18664                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18665                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18666                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18667                     
18668                     fillMonths.cn.push({
18669                         tag: 'td',
18670                         cls: 'cw',
18671                         html: calWeek
18672                     });
18673                 }
18674             }
18675             
18676             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18677                 clsName += ' old';
18678             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18679                 clsName += ' new';
18680             }
18681             if (this.todayHighlight &&
18682                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18683                 prevMonth.getUTCMonth() == today.getMonth() &&
18684                 prevMonth.getUTCDate() == today.getDate()) {
18685                 clsName += ' today';
18686             }
18687             
18688             if (currentDate && prevMonth.valueOf() === currentDate) {
18689                 clsName += ' active';
18690             }
18691             
18692             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18693                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18694                     clsName += ' disabled';
18695             }
18696             
18697             fillMonths.cn.push({
18698                 tag: 'td',
18699                 cls: 'day ' + clsName,
18700                 html: prevMonth.getDate()
18701             });
18702             
18703             prevMonth.setDate(prevMonth.getDate()+1);
18704         }
18705           
18706         var currentYear = this.date && this.date.getUTCFullYear();
18707         var currentMonth = this.date && this.date.getUTCMonth();
18708         
18709         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18710         
18711         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18712             v.removeClass('active');
18713             
18714             if(currentYear === year && k === currentMonth){
18715                 v.addClass('active');
18716             }
18717             
18718             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18719                 v.addClass('disabled');
18720             }
18721             
18722         });
18723         
18724         
18725         year = parseInt(year/10, 10) * 10;
18726         
18727         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18728         
18729         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18730         
18731         year -= 1;
18732         for (var i = -1; i < 11; i++) {
18733             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18734                 tag: 'span',
18735                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18736                 html: year
18737             });
18738             
18739             year += 1;
18740         }
18741     },
18742     
18743     showMode: function(dir) 
18744     {
18745         if (dir) {
18746             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18747         }
18748         
18749         Roo.each(this.picker().select('>div',true).elements, function(v){
18750             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18751             v.hide();
18752         });
18753         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18754     },
18755     
18756     place: function()
18757     {
18758         if(this.isInline) {
18759             return;
18760         }
18761         
18762         this.picker().removeClass(['bottom', 'top']);
18763         
18764         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18765             /*
18766              * place to the top of element!
18767              *
18768              */
18769             
18770             this.picker().addClass('top');
18771             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18772             
18773             return;
18774         }
18775         
18776         this.picker().addClass('bottom');
18777         
18778         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18779     },
18780     
18781     parseDate : function(value)
18782     {
18783         if(!value || value instanceof Date){
18784             return value;
18785         }
18786         var v = Date.parseDate(value, this.format);
18787         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18788             v = Date.parseDate(value, 'Y-m-d');
18789         }
18790         if(!v && this.altFormats){
18791             if(!this.altFormatsArray){
18792                 this.altFormatsArray = this.altFormats.split("|");
18793             }
18794             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18795                 v = Date.parseDate(value, this.altFormatsArray[i]);
18796             }
18797         }
18798         return v;
18799     },
18800     
18801     formatDate : function(date, fmt)
18802     {   
18803         return (!date || !(date instanceof Date)) ?
18804         date : date.dateFormat(fmt || this.format);
18805     },
18806     
18807     onFocus : function()
18808     {
18809         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18810         this.show();
18811     },
18812     
18813     onBlur : function()
18814     {
18815         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18816         
18817         var d = this.inputEl().getValue();
18818         
18819         this.setValue(d);
18820                 
18821         this.hide();
18822     },
18823     
18824     show : function()
18825     {
18826         this.picker().show();
18827         this.update();
18828         this.place();
18829         
18830         this.fireEvent('show', this, this.date);
18831     },
18832     
18833     hide : function()
18834     {
18835         if(this.isInline) {
18836             return;
18837         }
18838         this.picker().hide();
18839         this.viewMode = this.startViewMode;
18840         this.showMode();
18841         
18842         this.fireEvent('hide', this, this.date);
18843         
18844     },
18845     
18846     onMousedown: function(e)
18847     {
18848         e.stopPropagation();
18849         e.preventDefault();
18850     },
18851     
18852     keyup: function(e)
18853     {
18854         Roo.bootstrap.DateField.superclass.keyup.call(this);
18855         this.update();
18856     },
18857
18858     setValue: function(v)
18859     {
18860         if(this.fireEvent('beforeselect', this, v) !== false){
18861             var d = new Date(this.parseDate(v) ).clearTime();
18862         
18863             if(isNaN(d.getTime())){
18864                 this.date = this.viewDate = '';
18865                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18866                 return;
18867             }
18868
18869             v = this.formatDate(d);
18870
18871             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18872
18873             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18874
18875             this.update();
18876
18877             this.fireEvent('select', this, this.date);
18878         }
18879     },
18880     
18881     getValue: function()
18882     {
18883         return this.formatDate(this.date);
18884     },
18885     
18886     fireKey: function(e)
18887     {
18888         if (!this.picker().isVisible()){
18889             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18890                 this.show();
18891             }
18892             return;
18893         }
18894         
18895         var dateChanged = false,
18896         dir, day, month,
18897         newDate, newViewDate;
18898         
18899         switch(e.keyCode){
18900             case 27: // escape
18901                 this.hide();
18902                 e.preventDefault();
18903                 break;
18904             case 37: // left
18905             case 39: // right
18906                 if (!this.keyboardNavigation) {
18907                     break;
18908                 }
18909                 dir = e.keyCode == 37 ? -1 : 1;
18910                 
18911                 if (e.ctrlKey){
18912                     newDate = this.moveYear(this.date, dir);
18913                     newViewDate = this.moveYear(this.viewDate, dir);
18914                 } else if (e.shiftKey){
18915                     newDate = this.moveMonth(this.date, dir);
18916                     newViewDate = this.moveMonth(this.viewDate, dir);
18917                 } else {
18918                     newDate = new Date(this.date);
18919                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18920                     newViewDate = new Date(this.viewDate);
18921                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18922                 }
18923                 if (this.dateWithinRange(newDate)){
18924                     this.date = newDate;
18925                     this.viewDate = newViewDate;
18926                     this.setValue(this.formatDate(this.date));
18927 //                    this.update();
18928                     e.preventDefault();
18929                     dateChanged = true;
18930                 }
18931                 break;
18932             case 38: // up
18933             case 40: // down
18934                 if (!this.keyboardNavigation) {
18935                     break;
18936                 }
18937                 dir = e.keyCode == 38 ? -1 : 1;
18938                 if (e.ctrlKey){
18939                     newDate = this.moveYear(this.date, dir);
18940                     newViewDate = this.moveYear(this.viewDate, dir);
18941                 } else if (e.shiftKey){
18942                     newDate = this.moveMonth(this.date, dir);
18943                     newViewDate = this.moveMonth(this.viewDate, dir);
18944                 } else {
18945                     newDate = new Date(this.date);
18946                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18947                     newViewDate = new Date(this.viewDate);
18948                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18949                 }
18950                 if (this.dateWithinRange(newDate)){
18951                     this.date = newDate;
18952                     this.viewDate = newViewDate;
18953                     this.setValue(this.formatDate(this.date));
18954 //                    this.update();
18955                     e.preventDefault();
18956                     dateChanged = true;
18957                 }
18958                 break;
18959             case 13: // enter
18960                 this.setValue(this.formatDate(this.date));
18961                 this.hide();
18962                 e.preventDefault();
18963                 break;
18964             case 9: // tab
18965                 this.setValue(this.formatDate(this.date));
18966                 this.hide();
18967                 break;
18968             case 16: // shift
18969             case 17: // ctrl
18970             case 18: // alt
18971                 break;
18972             default :
18973                 this.hide();
18974                 
18975         }
18976     },
18977     
18978     
18979     onClick: function(e) 
18980     {
18981         e.stopPropagation();
18982         e.preventDefault();
18983         
18984         var target = e.getTarget();
18985         
18986         if(target.nodeName.toLowerCase() === 'i'){
18987             target = Roo.get(target).dom.parentNode;
18988         }
18989         
18990         var nodeName = target.nodeName;
18991         var className = target.className;
18992         var html = target.innerHTML;
18993         //Roo.log(nodeName);
18994         
18995         switch(nodeName.toLowerCase()) {
18996             case 'th':
18997                 switch(className) {
18998                     case 'switch':
18999                         this.showMode(1);
19000                         break;
19001                     case 'prev':
19002                     case 'next':
19003                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19004                         switch(this.viewMode){
19005                                 case 0:
19006                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19007                                         break;
19008                                 case 1:
19009                                 case 2:
19010                                         this.viewDate = this.moveYear(this.viewDate, dir);
19011                                         break;
19012                         }
19013                         this.fill();
19014                         break;
19015                     case 'today':
19016                         var date = new Date();
19017                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19018 //                        this.fill()
19019                         this.setValue(this.formatDate(this.date));
19020                         
19021                         this.hide();
19022                         break;
19023                 }
19024                 break;
19025             case 'span':
19026                 if (className.indexOf('disabled') < 0) {
19027                     this.viewDate.setUTCDate(1);
19028                     if (className.indexOf('month') > -1) {
19029                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19030                     } else {
19031                         var year = parseInt(html, 10) || 0;
19032                         this.viewDate.setUTCFullYear(year);
19033                         
19034                     }
19035                     
19036                     if(this.singleMode){
19037                         this.setValue(this.formatDate(this.viewDate));
19038                         this.hide();
19039                         return;
19040                     }
19041                     
19042                     this.showMode(-1);
19043                     this.fill();
19044                 }
19045                 break;
19046                 
19047             case 'td':
19048                 //Roo.log(className);
19049                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19050                     var day = parseInt(html, 10) || 1;
19051                     var year = this.viewDate.getUTCFullYear(),
19052                         month = this.viewDate.getUTCMonth();
19053
19054                     if (className.indexOf('old') > -1) {
19055                         if(month === 0 ){
19056                             month = 11;
19057                             year -= 1;
19058                         }else{
19059                             month -= 1;
19060                         }
19061                     } else if (className.indexOf('new') > -1) {
19062                         if (month == 11) {
19063                             month = 0;
19064                             year += 1;
19065                         } else {
19066                             month += 1;
19067                         }
19068                     }
19069                     //Roo.log([year,month,day]);
19070                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19071                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19072 //                    this.fill();
19073                     //Roo.log(this.formatDate(this.date));
19074                     this.setValue(this.formatDate(this.date));
19075                     this.hide();
19076                 }
19077                 break;
19078         }
19079     },
19080     
19081     setStartDate: function(startDate)
19082     {
19083         this.startDate = startDate || -Infinity;
19084         if (this.startDate !== -Infinity) {
19085             this.startDate = this.parseDate(this.startDate);
19086         }
19087         this.update();
19088         this.updateNavArrows();
19089     },
19090
19091     setEndDate: function(endDate)
19092     {
19093         this.endDate = endDate || Infinity;
19094         if (this.endDate !== Infinity) {
19095             this.endDate = this.parseDate(this.endDate);
19096         }
19097         this.update();
19098         this.updateNavArrows();
19099     },
19100     
19101     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19102     {
19103         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19104         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19105             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19106         }
19107         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19108             return parseInt(d, 10);
19109         });
19110         this.update();
19111         this.updateNavArrows();
19112     },
19113     
19114     updateNavArrows: function() 
19115     {
19116         if(this.singleMode){
19117             return;
19118         }
19119         
19120         var d = new Date(this.viewDate),
19121         year = d.getUTCFullYear(),
19122         month = d.getUTCMonth();
19123         
19124         Roo.each(this.picker().select('.prev', true).elements, function(v){
19125             v.show();
19126             switch (this.viewMode) {
19127                 case 0:
19128
19129                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19130                         v.hide();
19131                     }
19132                     break;
19133                 case 1:
19134                 case 2:
19135                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19136                         v.hide();
19137                     }
19138                     break;
19139             }
19140         });
19141         
19142         Roo.each(this.picker().select('.next', true).elements, function(v){
19143             v.show();
19144             switch (this.viewMode) {
19145                 case 0:
19146
19147                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19148                         v.hide();
19149                     }
19150                     break;
19151                 case 1:
19152                 case 2:
19153                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19154                         v.hide();
19155                     }
19156                     break;
19157             }
19158         })
19159     },
19160     
19161     moveMonth: function(date, dir)
19162     {
19163         if (!dir) {
19164             return date;
19165         }
19166         var new_date = new Date(date.valueOf()),
19167         day = new_date.getUTCDate(),
19168         month = new_date.getUTCMonth(),
19169         mag = Math.abs(dir),
19170         new_month, test;
19171         dir = dir > 0 ? 1 : -1;
19172         if (mag == 1){
19173             test = dir == -1
19174             // If going back one month, make sure month is not current month
19175             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19176             ? function(){
19177                 return new_date.getUTCMonth() == month;
19178             }
19179             // If going forward one month, make sure month is as expected
19180             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19181             : function(){
19182                 return new_date.getUTCMonth() != new_month;
19183             };
19184             new_month = month + dir;
19185             new_date.setUTCMonth(new_month);
19186             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19187             if (new_month < 0 || new_month > 11) {
19188                 new_month = (new_month + 12) % 12;
19189             }
19190         } else {
19191             // For magnitudes >1, move one month at a time...
19192             for (var i=0; i<mag; i++) {
19193                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19194                 new_date = this.moveMonth(new_date, dir);
19195             }
19196             // ...then reset the day, keeping it in the new month
19197             new_month = new_date.getUTCMonth();
19198             new_date.setUTCDate(day);
19199             test = function(){
19200                 return new_month != new_date.getUTCMonth();
19201             };
19202         }
19203         // Common date-resetting loop -- if date is beyond end of month, make it
19204         // end of month
19205         while (test()){
19206             new_date.setUTCDate(--day);
19207             new_date.setUTCMonth(new_month);
19208         }
19209         return new_date;
19210     },
19211
19212     moveYear: function(date, dir)
19213     {
19214         return this.moveMonth(date, dir*12);
19215     },
19216
19217     dateWithinRange: function(date)
19218     {
19219         return date >= this.startDate && date <= this.endDate;
19220     },
19221
19222     
19223     remove: function() 
19224     {
19225         this.picker().remove();
19226     },
19227     
19228     validateValue : function(value)
19229     {
19230         if(this.getVisibilityEl().hasClass('hidden')){
19231             return true;
19232         }
19233         
19234         if(value.length < 1)  {
19235             if(this.allowBlank){
19236                 return true;
19237             }
19238             return false;
19239         }
19240         
19241         if(value.length < this.minLength){
19242             return false;
19243         }
19244         if(value.length > this.maxLength){
19245             return false;
19246         }
19247         if(this.vtype){
19248             var vt = Roo.form.VTypes;
19249             if(!vt[this.vtype](value, this)){
19250                 return false;
19251             }
19252         }
19253         if(typeof this.validator == "function"){
19254             var msg = this.validator(value);
19255             if(msg !== true){
19256                 return false;
19257             }
19258         }
19259         
19260         if(this.regex && !this.regex.test(value)){
19261             return false;
19262         }
19263         
19264         if(typeof(this.parseDate(value)) == 'undefined'){
19265             return false;
19266         }
19267         
19268         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19269             return false;
19270         }      
19271         
19272         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19273             return false;
19274         } 
19275         
19276         
19277         return true;
19278     },
19279     
19280     setVisible : function(visible)
19281     {
19282         if(!this.getEl()){
19283             return;
19284         }
19285         
19286         this.getEl().removeClass('hidden');
19287         
19288         if(visible){
19289             return;
19290         }
19291         
19292         this.getEl().addClass('hidden');
19293     }
19294    
19295 });
19296
19297 Roo.apply(Roo.bootstrap.DateField,  {
19298     
19299     head : {
19300         tag: 'thead',
19301         cn: [
19302         {
19303             tag: 'tr',
19304             cn: [
19305             {
19306                 tag: 'th',
19307                 cls: 'prev',
19308                 html: '<i class="fa fa-arrow-left"/>'
19309             },
19310             {
19311                 tag: 'th',
19312                 cls: 'switch',
19313                 colspan: '5'
19314             },
19315             {
19316                 tag: 'th',
19317                 cls: 'next',
19318                 html: '<i class="fa fa-arrow-right"/>'
19319             }
19320
19321             ]
19322         }
19323         ]
19324     },
19325     
19326     content : {
19327         tag: 'tbody',
19328         cn: [
19329         {
19330             tag: 'tr',
19331             cn: [
19332             {
19333                 tag: 'td',
19334                 colspan: '7'
19335             }
19336             ]
19337         }
19338         ]
19339     },
19340     
19341     footer : {
19342         tag: 'tfoot',
19343         cn: [
19344         {
19345             tag: 'tr',
19346             cn: [
19347             {
19348                 tag: 'th',
19349                 colspan: '7',
19350                 cls: 'today'
19351             }
19352                     
19353             ]
19354         }
19355         ]
19356     },
19357     
19358     dates:{
19359         en: {
19360             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19361             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19362             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19363             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19364             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19365             today: "Today"
19366         }
19367     },
19368     
19369     modes: [
19370     {
19371         clsName: 'days',
19372         navFnc: 'Month',
19373         navStep: 1
19374     },
19375     {
19376         clsName: 'months',
19377         navFnc: 'FullYear',
19378         navStep: 1
19379     },
19380     {
19381         clsName: 'years',
19382         navFnc: 'FullYear',
19383         navStep: 10
19384     }]
19385 });
19386
19387 Roo.apply(Roo.bootstrap.DateField,  {
19388   
19389     template : {
19390         tag: 'div',
19391         cls: 'datepicker dropdown-menu roo-dynamic',
19392         cn: [
19393         {
19394             tag: 'div',
19395             cls: 'datepicker-days',
19396             cn: [
19397             {
19398                 tag: 'table',
19399                 cls: 'table-condensed',
19400                 cn:[
19401                 Roo.bootstrap.DateField.head,
19402                 {
19403                     tag: 'tbody'
19404                 },
19405                 Roo.bootstrap.DateField.footer
19406                 ]
19407             }
19408             ]
19409         },
19410         {
19411             tag: 'div',
19412             cls: 'datepicker-months',
19413             cn: [
19414             {
19415                 tag: 'table',
19416                 cls: 'table-condensed',
19417                 cn:[
19418                 Roo.bootstrap.DateField.head,
19419                 Roo.bootstrap.DateField.content,
19420                 Roo.bootstrap.DateField.footer
19421                 ]
19422             }
19423             ]
19424         },
19425         {
19426             tag: 'div',
19427             cls: 'datepicker-years',
19428             cn: [
19429             {
19430                 tag: 'table',
19431                 cls: 'table-condensed',
19432                 cn:[
19433                 Roo.bootstrap.DateField.head,
19434                 Roo.bootstrap.DateField.content,
19435                 Roo.bootstrap.DateField.footer
19436                 ]
19437             }
19438             ]
19439         }
19440         ]
19441     }
19442 });
19443
19444  
19445
19446  /*
19447  * - LGPL
19448  *
19449  * TimeField
19450  * 
19451  */
19452
19453 /**
19454  * @class Roo.bootstrap.TimeField
19455  * @extends Roo.bootstrap.Input
19456  * Bootstrap DateField class
19457  * 
19458  * 
19459  * @constructor
19460  * Create a new TimeField
19461  * @param {Object} config The config object
19462  */
19463
19464 Roo.bootstrap.TimeField = function(config){
19465     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19466     this.addEvents({
19467             /**
19468              * @event show
19469              * Fires when this field show.
19470              * @param {Roo.bootstrap.DateField} thisthis
19471              * @param {Mixed} date The date value
19472              */
19473             show : true,
19474             /**
19475              * @event show
19476              * Fires when this field hide.
19477              * @param {Roo.bootstrap.DateField} this
19478              * @param {Mixed} date The date value
19479              */
19480             hide : true,
19481             /**
19482              * @event select
19483              * Fires when select a date.
19484              * @param {Roo.bootstrap.DateField} this
19485              * @param {Mixed} date The date value
19486              */
19487             select : true
19488         });
19489 };
19490
19491 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19492     
19493     /**
19494      * @cfg {String} format
19495      * The default time format string which can be overriden for localization support.  The format must be
19496      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19497      */
19498     format : "H:i",
19499        
19500     onRender: function(ct, position)
19501     {
19502         
19503         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19504                 
19505         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19506         
19507         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19508         
19509         this.pop = this.picker().select('>.datepicker-time',true).first();
19510         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19511         
19512         this.picker().on('mousedown', this.onMousedown, this);
19513         this.picker().on('click', this.onClick, this);
19514         
19515         this.picker().addClass('datepicker-dropdown');
19516     
19517         this.fillTime();
19518         this.update();
19519             
19520         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19521         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19522         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19523         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19524         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19525         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19526
19527     },
19528     
19529     fireKey: function(e){
19530         if (!this.picker().isVisible()){
19531             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19532                 this.show();
19533             }
19534             return;
19535         }
19536
19537         e.preventDefault();
19538         
19539         switch(e.keyCode){
19540             case 27: // escape
19541                 this.hide();
19542                 break;
19543             case 37: // left
19544             case 39: // right
19545                 this.onTogglePeriod();
19546                 break;
19547             case 38: // up
19548                 this.onIncrementMinutes();
19549                 break;
19550             case 40: // down
19551                 this.onDecrementMinutes();
19552                 break;
19553             case 13: // enter
19554             case 9: // tab
19555                 this.setTime();
19556                 break;
19557         }
19558     },
19559     
19560     onClick: function(e) {
19561         e.stopPropagation();
19562         e.preventDefault();
19563     },
19564     
19565     picker : function()
19566     {
19567         return this.el.select('.datepicker', true).first();
19568     },
19569     
19570     fillTime: function()
19571     {    
19572         var time = this.pop.select('tbody', true).first();
19573         
19574         time.dom.innerHTML = '';
19575         
19576         time.createChild({
19577             tag: 'tr',
19578             cn: [
19579                 {
19580                     tag: 'td',
19581                     cn: [
19582                         {
19583                             tag: 'a',
19584                             href: '#',
19585                             cls: 'btn',
19586                             cn: [
19587                                 {
19588                                     tag: 'span',
19589                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19590                                 }
19591                             ]
19592                         } 
19593                     ]
19594                 },
19595                 {
19596                     tag: 'td',
19597                     cls: 'separator'
19598                 },
19599                 {
19600                     tag: 'td',
19601                     cn: [
19602                         {
19603                             tag: 'a',
19604                             href: '#',
19605                             cls: 'btn',
19606                             cn: [
19607                                 {
19608                                     tag: 'span',
19609                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19610                                 }
19611                             ]
19612                         }
19613                     ]
19614                 },
19615                 {
19616                     tag: 'td',
19617                     cls: 'separator'
19618                 }
19619             ]
19620         });
19621         
19622         time.createChild({
19623             tag: 'tr',
19624             cn: [
19625                 {
19626                     tag: 'td',
19627                     cn: [
19628                         {
19629                             tag: 'span',
19630                             cls: 'timepicker-hour',
19631                             html: '00'
19632                         }  
19633                     ]
19634                 },
19635                 {
19636                     tag: 'td',
19637                     cls: 'separator',
19638                     html: ':'
19639                 },
19640                 {
19641                     tag: 'td',
19642                     cn: [
19643                         {
19644                             tag: 'span',
19645                             cls: 'timepicker-minute',
19646                             html: '00'
19647                         }  
19648                     ]
19649                 },
19650                 {
19651                     tag: 'td',
19652                     cls: 'separator'
19653                 },
19654                 {
19655                     tag: 'td',
19656                     cn: [
19657                         {
19658                             tag: 'button',
19659                             type: 'button',
19660                             cls: 'btn btn-primary period',
19661                             html: 'AM'
19662                             
19663                         }
19664                     ]
19665                 }
19666             ]
19667         });
19668         
19669         time.createChild({
19670             tag: 'tr',
19671             cn: [
19672                 {
19673                     tag: 'td',
19674                     cn: [
19675                         {
19676                             tag: 'a',
19677                             href: '#',
19678                             cls: 'btn',
19679                             cn: [
19680                                 {
19681                                     tag: 'span',
19682                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19683                                 }
19684                             ]
19685                         }
19686                     ]
19687                 },
19688                 {
19689                     tag: 'td',
19690                     cls: 'separator'
19691                 },
19692                 {
19693                     tag: 'td',
19694                     cn: [
19695                         {
19696                             tag: 'a',
19697                             href: '#',
19698                             cls: 'btn',
19699                             cn: [
19700                                 {
19701                                     tag: 'span',
19702                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19703                                 }
19704                             ]
19705                         }
19706                     ]
19707                 },
19708                 {
19709                     tag: 'td',
19710                     cls: 'separator'
19711                 }
19712             ]
19713         });
19714         
19715     },
19716     
19717     update: function()
19718     {
19719         
19720         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19721         
19722         this.fill();
19723     },
19724     
19725     fill: function() 
19726     {
19727         var hours = this.time.getHours();
19728         var minutes = this.time.getMinutes();
19729         var period = 'AM';
19730         
19731         if(hours > 11){
19732             period = 'PM';
19733         }
19734         
19735         if(hours == 0){
19736             hours = 12;
19737         }
19738         
19739         
19740         if(hours > 12){
19741             hours = hours - 12;
19742         }
19743         
19744         if(hours < 10){
19745             hours = '0' + hours;
19746         }
19747         
19748         if(minutes < 10){
19749             minutes = '0' + minutes;
19750         }
19751         
19752         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19753         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19754         this.pop.select('button', true).first().dom.innerHTML = period;
19755         
19756     },
19757     
19758     place: function()
19759     {   
19760         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19761         
19762         var cls = ['bottom'];
19763         
19764         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19765             cls.pop();
19766             cls.push('top');
19767         }
19768         
19769         cls.push('right');
19770         
19771         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19772             cls.pop();
19773             cls.push('left');
19774         }
19775         
19776         this.picker().addClass(cls.join('-'));
19777         
19778         var _this = this;
19779         
19780         Roo.each(cls, function(c){
19781             if(c == 'bottom'){
19782                 _this.picker().setTop(_this.inputEl().getHeight());
19783                 return;
19784             }
19785             if(c == 'top'){
19786                 _this.picker().setTop(0 - _this.picker().getHeight());
19787                 return;
19788             }
19789             
19790             if(c == 'left'){
19791                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19792                 return;
19793             }
19794             if(c == 'right'){
19795                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19796                 return;
19797             }
19798         });
19799         
19800     },
19801   
19802     onFocus : function()
19803     {
19804         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19805         this.show();
19806     },
19807     
19808     onBlur : function()
19809     {
19810         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19811         this.hide();
19812     },
19813     
19814     show : function()
19815     {
19816         this.picker().show();
19817         this.pop.show();
19818         this.update();
19819         this.place();
19820         
19821         this.fireEvent('show', this, this.date);
19822     },
19823     
19824     hide : function()
19825     {
19826         this.picker().hide();
19827         this.pop.hide();
19828         
19829         this.fireEvent('hide', this, this.date);
19830     },
19831     
19832     setTime : function()
19833     {
19834         this.hide();
19835         this.setValue(this.time.format(this.format));
19836         
19837         this.fireEvent('select', this, this.date);
19838         
19839         
19840     },
19841     
19842     onMousedown: function(e){
19843         e.stopPropagation();
19844         e.preventDefault();
19845     },
19846     
19847     onIncrementHours: function()
19848     {
19849         Roo.log('onIncrementHours');
19850         this.time = this.time.add(Date.HOUR, 1);
19851         this.update();
19852         
19853     },
19854     
19855     onDecrementHours: function()
19856     {
19857         Roo.log('onDecrementHours');
19858         this.time = this.time.add(Date.HOUR, -1);
19859         this.update();
19860     },
19861     
19862     onIncrementMinutes: function()
19863     {
19864         Roo.log('onIncrementMinutes');
19865         this.time = this.time.add(Date.MINUTE, 1);
19866         this.update();
19867     },
19868     
19869     onDecrementMinutes: function()
19870     {
19871         Roo.log('onDecrementMinutes');
19872         this.time = this.time.add(Date.MINUTE, -1);
19873         this.update();
19874     },
19875     
19876     onTogglePeriod: function()
19877     {
19878         Roo.log('onTogglePeriod');
19879         this.time = this.time.add(Date.HOUR, 12);
19880         this.update();
19881     }
19882     
19883    
19884 });
19885
19886 Roo.apply(Roo.bootstrap.TimeField,  {
19887     
19888     content : {
19889         tag: 'tbody',
19890         cn: [
19891             {
19892                 tag: 'tr',
19893                 cn: [
19894                 {
19895                     tag: 'td',
19896                     colspan: '7'
19897                 }
19898                 ]
19899             }
19900         ]
19901     },
19902     
19903     footer : {
19904         tag: 'tfoot',
19905         cn: [
19906             {
19907                 tag: 'tr',
19908                 cn: [
19909                 {
19910                     tag: 'th',
19911                     colspan: '7',
19912                     cls: '',
19913                     cn: [
19914                         {
19915                             tag: 'button',
19916                             cls: 'btn btn-info ok',
19917                             html: 'OK'
19918                         }
19919                     ]
19920                 }
19921
19922                 ]
19923             }
19924         ]
19925     }
19926 });
19927
19928 Roo.apply(Roo.bootstrap.TimeField,  {
19929   
19930     template : {
19931         tag: 'div',
19932         cls: 'datepicker dropdown-menu',
19933         cn: [
19934             {
19935                 tag: 'div',
19936                 cls: 'datepicker-time',
19937                 cn: [
19938                 {
19939                     tag: 'table',
19940                     cls: 'table-condensed',
19941                     cn:[
19942                     Roo.bootstrap.TimeField.content,
19943                     Roo.bootstrap.TimeField.footer
19944                     ]
19945                 }
19946                 ]
19947             }
19948         ]
19949     }
19950 });
19951
19952  
19953
19954  /*
19955  * - LGPL
19956  *
19957  * MonthField
19958  * 
19959  */
19960
19961 /**
19962  * @class Roo.bootstrap.MonthField
19963  * @extends Roo.bootstrap.Input
19964  * Bootstrap MonthField class
19965  * 
19966  * @cfg {String} language default en
19967  * 
19968  * @constructor
19969  * Create a new MonthField
19970  * @param {Object} config The config object
19971  */
19972
19973 Roo.bootstrap.MonthField = function(config){
19974     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19975     
19976     this.addEvents({
19977         /**
19978          * @event show
19979          * Fires when this field show.
19980          * @param {Roo.bootstrap.MonthField} this
19981          * @param {Mixed} date The date value
19982          */
19983         show : true,
19984         /**
19985          * @event show
19986          * Fires when this field hide.
19987          * @param {Roo.bootstrap.MonthField} this
19988          * @param {Mixed} date The date value
19989          */
19990         hide : true,
19991         /**
19992          * @event select
19993          * Fires when select a date.
19994          * @param {Roo.bootstrap.MonthField} this
19995          * @param {String} oldvalue The old value
19996          * @param {String} newvalue The new value
19997          */
19998         select : true
19999     });
20000 };
20001
20002 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20003     
20004     onRender: function(ct, position)
20005     {
20006         
20007         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20008         
20009         this.language = this.language || 'en';
20010         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20011         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20012         
20013         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20014         this.isInline = false;
20015         this.isInput = true;
20016         this.component = this.el.select('.add-on', true).first() || false;
20017         this.component = (this.component && this.component.length === 0) ? false : this.component;
20018         this.hasInput = this.component && this.inputEL().length;
20019         
20020         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20021         
20022         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20023         
20024         this.picker().on('mousedown', this.onMousedown, this);
20025         this.picker().on('click', this.onClick, this);
20026         
20027         this.picker().addClass('datepicker-dropdown');
20028         
20029         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20030             v.setStyle('width', '189px');
20031         });
20032         
20033         this.fillMonths();
20034         
20035         this.update();
20036         
20037         if(this.isInline) {
20038             this.show();
20039         }
20040         
20041     },
20042     
20043     setValue: function(v, suppressEvent)
20044     {   
20045         var o = this.getValue();
20046         
20047         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20048         
20049         this.update();
20050
20051         if(suppressEvent !== true){
20052             this.fireEvent('select', this, o, v);
20053         }
20054         
20055     },
20056     
20057     getValue: function()
20058     {
20059         return this.value;
20060     },
20061     
20062     onClick: function(e) 
20063     {
20064         e.stopPropagation();
20065         e.preventDefault();
20066         
20067         var target = e.getTarget();
20068         
20069         if(target.nodeName.toLowerCase() === 'i'){
20070             target = Roo.get(target).dom.parentNode;
20071         }
20072         
20073         var nodeName = target.nodeName;
20074         var className = target.className;
20075         var html = target.innerHTML;
20076         
20077         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20078             return;
20079         }
20080         
20081         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20082         
20083         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20084         
20085         this.hide();
20086                         
20087     },
20088     
20089     picker : function()
20090     {
20091         return this.pickerEl;
20092     },
20093     
20094     fillMonths: function()
20095     {    
20096         var i = 0;
20097         var months = this.picker().select('>.datepicker-months td', true).first();
20098         
20099         months.dom.innerHTML = '';
20100         
20101         while (i < 12) {
20102             var month = {
20103                 tag: 'span',
20104                 cls: 'month',
20105                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20106             };
20107             
20108             months.createChild(month);
20109         }
20110         
20111     },
20112     
20113     update: function()
20114     {
20115         var _this = this;
20116         
20117         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20118             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20119         }
20120         
20121         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20122             e.removeClass('active');
20123             
20124             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20125                 e.addClass('active');
20126             }
20127         })
20128     },
20129     
20130     place: function()
20131     {
20132         if(this.isInline) {
20133             return;
20134         }
20135         
20136         this.picker().removeClass(['bottom', 'top']);
20137         
20138         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20139             /*
20140              * place to the top of element!
20141              *
20142              */
20143             
20144             this.picker().addClass('top');
20145             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20146             
20147             return;
20148         }
20149         
20150         this.picker().addClass('bottom');
20151         
20152         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20153     },
20154     
20155     onFocus : function()
20156     {
20157         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20158         this.show();
20159     },
20160     
20161     onBlur : function()
20162     {
20163         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20164         
20165         var d = this.inputEl().getValue();
20166         
20167         this.setValue(d);
20168                 
20169         this.hide();
20170     },
20171     
20172     show : function()
20173     {
20174         this.picker().show();
20175         this.picker().select('>.datepicker-months', true).first().show();
20176         this.update();
20177         this.place();
20178         
20179         this.fireEvent('show', this, this.date);
20180     },
20181     
20182     hide : function()
20183     {
20184         if(this.isInline) {
20185             return;
20186         }
20187         this.picker().hide();
20188         this.fireEvent('hide', this, this.date);
20189         
20190     },
20191     
20192     onMousedown: function(e)
20193     {
20194         e.stopPropagation();
20195         e.preventDefault();
20196     },
20197     
20198     keyup: function(e)
20199     {
20200         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20201         this.update();
20202     },
20203
20204     fireKey: function(e)
20205     {
20206         if (!this.picker().isVisible()){
20207             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20208                 this.show();
20209             }
20210             return;
20211         }
20212         
20213         var dir;
20214         
20215         switch(e.keyCode){
20216             case 27: // escape
20217                 this.hide();
20218                 e.preventDefault();
20219                 break;
20220             case 37: // left
20221             case 39: // right
20222                 dir = e.keyCode == 37 ? -1 : 1;
20223                 
20224                 this.vIndex = this.vIndex + dir;
20225                 
20226                 if(this.vIndex < 0){
20227                     this.vIndex = 0;
20228                 }
20229                 
20230                 if(this.vIndex > 11){
20231                     this.vIndex = 11;
20232                 }
20233                 
20234                 if(isNaN(this.vIndex)){
20235                     this.vIndex = 0;
20236                 }
20237                 
20238                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20239                 
20240                 break;
20241             case 38: // up
20242             case 40: // down
20243                 
20244                 dir = e.keyCode == 38 ? -1 : 1;
20245                 
20246                 this.vIndex = this.vIndex + dir * 4;
20247                 
20248                 if(this.vIndex < 0){
20249                     this.vIndex = 0;
20250                 }
20251                 
20252                 if(this.vIndex > 11){
20253                     this.vIndex = 11;
20254                 }
20255                 
20256                 if(isNaN(this.vIndex)){
20257                     this.vIndex = 0;
20258                 }
20259                 
20260                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20261                 break;
20262                 
20263             case 13: // enter
20264                 
20265                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20266                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20267                 }
20268                 
20269                 this.hide();
20270                 e.preventDefault();
20271                 break;
20272             case 9: // tab
20273                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20274                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20275                 }
20276                 this.hide();
20277                 break;
20278             case 16: // shift
20279             case 17: // ctrl
20280             case 18: // alt
20281                 break;
20282             default :
20283                 this.hide();
20284                 
20285         }
20286     },
20287     
20288     remove: function() 
20289     {
20290         this.picker().remove();
20291     }
20292    
20293 });
20294
20295 Roo.apply(Roo.bootstrap.MonthField,  {
20296     
20297     content : {
20298         tag: 'tbody',
20299         cn: [
20300         {
20301             tag: 'tr',
20302             cn: [
20303             {
20304                 tag: 'td',
20305                 colspan: '7'
20306             }
20307             ]
20308         }
20309         ]
20310     },
20311     
20312     dates:{
20313         en: {
20314             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20315             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20316         }
20317     }
20318 });
20319
20320 Roo.apply(Roo.bootstrap.MonthField,  {
20321   
20322     template : {
20323         tag: 'div',
20324         cls: 'datepicker dropdown-menu roo-dynamic',
20325         cn: [
20326             {
20327                 tag: 'div',
20328                 cls: 'datepicker-months',
20329                 cn: [
20330                 {
20331                     tag: 'table',
20332                     cls: 'table-condensed',
20333                     cn:[
20334                         Roo.bootstrap.DateField.content
20335                     ]
20336                 }
20337                 ]
20338             }
20339         ]
20340     }
20341 });
20342
20343  
20344
20345  
20346  /*
20347  * - LGPL
20348  *
20349  * CheckBox
20350  * 
20351  */
20352
20353 /**
20354  * @class Roo.bootstrap.CheckBox
20355  * @extends Roo.bootstrap.Input
20356  * Bootstrap CheckBox class
20357  * 
20358  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20359  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20360  * @cfg {String} boxLabel The text that appears beside the checkbox
20361  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20362  * @cfg {Boolean} checked initnal the element
20363  * @cfg {Boolean} inline inline the element (default false)
20364  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20365  * @cfg {String} tooltip label tooltip
20366  * 
20367  * @constructor
20368  * Create a new CheckBox
20369  * @param {Object} config The config object
20370  */
20371
20372 Roo.bootstrap.CheckBox = function(config){
20373     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20374    
20375     this.addEvents({
20376         /**
20377         * @event check
20378         * Fires when the element is checked or unchecked.
20379         * @param {Roo.bootstrap.CheckBox} this This input
20380         * @param {Boolean} checked The new checked value
20381         */
20382        check : true,
20383        /**
20384         * @event click
20385         * Fires when the element is click.
20386         * @param {Roo.bootstrap.CheckBox} this This input
20387         */
20388        click : true
20389     });
20390     
20391 };
20392
20393 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20394   
20395     inputType: 'checkbox',
20396     inputValue: 1,
20397     valueOff: 0,
20398     boxLabel: false,
20399     checked: false,
20400     weight : false,
20401     inline: false,
20402     tooltip : '',
20403     
20404     getAutoCreate : function()
20405     {
20406         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20407         
20408         var id = Roo.id();
20409         
20410         var cfg = {};
20411         
20412         cfg.cls = 'form-group ' + this.inputType; //input-group
20413         
20414         if(this.inline){
20415             cfg.cls += ' ' + this.inputType + '-inline';
20416         }
20417         
20418         var input =  {
20419             tag: 'input',
20420             id : id,
20421             type : this.inputType,
20422             value : this.inputValue,
20423             cls : 'roo-' + this.inputType, //'form-box',
20424             placeholder : this.placeholder || ''
20425             
20426         };
20427         
20428         if(this.inputType != 'radio'){
20429             var hidden =  {
20430                 tag: 'input',
20431                 type : 'hidden',
20432                 cls : 'roo-hidden-value',
20433                 value : this.checked ? this.inputValue : this.valueOff
20434             };
20435         }
20436         
20437             
20438         if (this.weight) { // Validity check?
20439             cfg.cls += " " + this.inputType + "-" + this.weight;
20440         }
20441         
20442         if (this.disabled) {
20443             input.disabled=true;
20444         }
20445         
20446         if(this.checked){
20447             input.checked = this.checked;
20448         }
20449         
20450         if (this.name) {
20451             
20452             input.name = this.name;
20453             
20454             if(this.inputType != 'radio'){
20455                 hidden.name = this.name;
20456                 input.name = '_hidden_' + this.name;
20457             }
20458         }
20459         
20460         if (this.size) {
20461             input.cls += ' input-' + this.size;
20462         }
20463         
20464         var settings=this;
20465         
20466         ['xs','sm','md','lg'].map(function(size){
20467             if (settings[size]) {
20468                 cfg.cls += ' col-' + size + '-' + settings[size];
20469             }
20470         });
20471         
20472         var inputblock = input;
20473          
20474         if (this.before || this.after) {
20475             
20476             inputblock = {
20477                 cls : 'input-group',
20478                 cn :  [] 
20479             };
20480             
20481             if (this.before) {
20482                 inputblock.cn.push({
20483                     tag :'span',
20484                     cls : 'input-group-addon',
20485                     html : this.before
20486                 });
20487             }
20488             
20489             inputblock.cn.push(input);
20490             
20491             if(this.inputType != 'radio'){
20492                 inputblock.cn.push(hidden);
20493             }
20494             
20495             if (this.after) {
20496                 inputblock.cn.push({
20497                     tag :'span',
20498                     cls : 'input-group-addon',
20499                     html : this.after
20500                 });
20501             }
20502             
20503         }
20504         
20505         if (align ==='left' && this.fieldLabel.length) {
20506 //                Roo.log("left and has label");
20507             cfg.cn = [
20508                 {
20509                     tag: 'label',
20510                     'for' :  id,
20511                     cls : 'control-label',
20512                     html : this.fieldLabel
20513                 },
20514                 {
20515                     cls : "", 
20516                     cn: [
20517                         inputblock
20518                     ]
20519                 }
20520             ];
20521             
20522             if(this.labelWidth > 12){
20523                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20524             }
20525             
20526             if(this.labelWidth < 13 && this.labelmd == 0){
20527                 this.labelmd = this.labelWidth;
20528             }
20529             
20530             if(this.labellg > 0){
20531                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20532                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20533             }
20534             
20535             if(this.labelmd > 0){
20536                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20537                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20538             }
20539             
20540             if(this.labelsm > 0){
20541                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20542                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20543             }
20544             
20545             if(this.labelxs > 0){
20546                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20547                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20548             }
20549             
20550         } else if ( this.fieldLabel.length) {
20551 //                Roo.log(" label");
20552                 cfg.cn = [
20553                    
20554                     {
20555                         tag: this.boxLabel ? 'span' : 'label',
20556                         'for': id,
20557                         cls: 'control-label box-input-label',
20558                         //cls : 'input-group-addon',
20559                         html : this.fieldLabel
20560                     },
20561                     
20562                     inputblock
20563                     
20564                 ];
20565
20566         } else {
20567             
20568 //                Roo.log(" no label && no align");
20569                 cfg.cn = [  inputblock ] ;
20570                 
20571                 
20572         }
20573         
20574         if(this.boxLabel){
20575              var boxLabelCfg = {
20576                 tag: 'label',
20577                 //'for': id, // box label is handled by onclick - so no for...
20578                 cls: 'box-label',
20579                 html: this.boxLabel
20580             };
20581             
20582             if(this.tooltip){
20583                 boxLabelCfg.tooltip = this.tooltip;
20584             }
20585              
20586             cfg.cn.push(boxLabelCfg);
20587         }
20588         
20589         if(this.inputType != 'radio'){
20590             cfg.cn.push(hidden);
20591         }
20592         
20593         return cfg;
20594         
20595     },
20596     
20597     /**
20598      * return the real input element.
20599      */
20600     inputEl: function ()
20601     {
20602         return this.el.select('input.roo-' + this.inputType,true).first();
20603     },
20604     hiddenEl: function ()
20605     {
20606         return this.el.select('input.roo-hidden-value',true).first();
20607     },
20608     
20609     labelEl: function()
20610     {
20611         return this.el.select('label.control-label',true).first();
20612     },
20613     /* depricated... */
20614     
20615     label: function()
20616     {
20617         return this.labelEl();
20618     },
20619     
20620     boxLabelEl: function()
20621     {
20622         return this.el.select('label.box-label',true).first();
20623     },
20624     
20625     initEvents : function()
20626     {
20627 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20628         
20629         this.inputEl().on('click', this.onClick,  this);
20630         
20631         if (this.boxLabel) { 
20632             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20633         }
20634         
20635         this.startValue = this.getValue();
20636         
20637         if(this.groupId){
20638             Roo.bootstrap.CheckBox.register(this);
20639         }
20640     },
20641     
20642     onClick : function(e)
20643     {   
20644         if(this.fireEvent('click', this, e) !== false){
20645             this.setChecked(!this.checked);
20646         }
20647         
20648     },
20649     
20650     setChecked : function(state,suppressEvent)
20651     {
20652         this.startValue = this.getValue();
20653
20654         if(this.inputType == 'radio'){
20655             
20656             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20657                 e.dom.checked = false;
20658             });
20659             
20660             this.inputEl().dom.checked = true;
20661             
20662             this.inputEl().dom.value = this.inputValue;
20663             
20664             if(suppressEvent !== true){
20665                 this.fireEvent('check', this, true);
20666             }
20667             
20668             this.validate();
20669             
20670             return;
20671         }
20672         
20673         this.checked = state;
20674         
20675         this.inputEl().dom.checked = state;
20676         
20677         
20678         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20679         
20680         if(suppressEvent !== true){
20681             this.fireEvent('check', this, state);
20682         }
20683         
20684         this.validate();
20685     },
20686     
20687     getValue : function()
20688     {
20689         if(this.inputType == 'radio'){
20690             return this.getGroupValue();
20691         }
20692         
20693         return this.hiddenEl().dom.value;
20694         
20695     },
20696     
20697     getGroupValue : function()
20698     {
20699         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20700             return '';
20701         }
20702         
20703         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20704     },
20705     
20706     setValue : function(v,suppressEvent)
20707     {
20708         if(this.inputType == 'radio'){
20709             this.setGroupValue(v, suppressEvent);
20710             return;
20711         }
20712         
20713         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20714         
20715         this.validate();
20716     },
20717     
20718     setGroupValue : function(v, suppressEvent)
20719     {
20720         this.startValue = this.getValue();
20721         
20722         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20723             e.dom.checked = false;
20724             
20725             if(e.dom.value == v){
20726                 e.dom.checked = true;
20727             }
20728         });
20729         
20730         if(suppressEvent !== true){
20731             this.fireEvent('check', this, true);
20732         }
20733
20734         this.validate();
20735         
20736         return;
20737     },
20738     
20739     validate : function()
20740     {
20741         if(this.getVisibilityEl().hasClass('hidden')){
20742             return true;
20743         }
20744         
20745         if(
20746                 this.disabled || 
20747                 (this.inputType == 'radio' && this.validateRadio()) ||
20748                 (this.inputType == 'checkbox' && this.validateCheckbox())
20749         ){
20750             this.markValid();
20751             return true;
20752         }
20753         
20754         this.markInvalid();
20755         return false;
20756     },
20757     
20758     validateRadio : function()
20759     {
20760         if(this.getVisibilityEl().hasClass('hidden')){
20761             return true;
20762         }
20763         
20764         if(this.allowBlank){
20765             return true;
20766         }
20767         
20768         var valid = false;
20769         
20770         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20771             if(!e.dom.checked){
20772                 return;
20773             }
20774             
20775             valid = true;
20776             
20777             return false;
20778         });
20779         
20780         return valid;
20781     },
20782     
20783     validateCheckbox : function()
20784     {
20785         if(!this.groupId){
20786             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20787             //return (this.getValue() == this.inputValue) ? true : false;
20788         }
20789         
20790         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20791         
20792         if(!group){
20793             return false;
20794         }
20795         
20796         var r = false;
20797         
20798         for(var i in group){
20799             if(group[i].el.isVisible(true)){
20800                 r = false;
20801                 break;
20802             }
20803             
20804             r = true;
20805         }
20806         
20807         for(var i in group){
20808             if(r){
20809                 break;
20810             }
20811             
20812             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20813         }
20814         
20815         return r;
20816     },
20817     
20818     /**
20819      * Mark this field as valid
20820      */
20821     markValid : function()
20822     {
20823         var _this = this;
20824         
20825         this.fireEvent('valid', this);
20826         
20827         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20828         
20829         if(this.groupId){
20830             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20831         }
20832         
20833         if(label){
20834             label.markValid();
20835         }
20836
20837         if(this.inputType == 'radio'){
20838             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20839                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20840                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20841             });
20842             
20843             return;
20844         }
20845
20846         if(!this.groupId){
20847             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20848             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20849             return;
20850         }
20851         
20852         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20853         
20854         if(!group){
20855             return;
20856         }
20857         
20858         for(var i in group){
20859             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20860             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20861         }
20862     },
20863     
20864      /**
20865      * Mark this field as invalid
20866      * @param {String} msg The validation message
20867      */
20868     markInvalid : function(msg)
20869     {
20870         if(this.allowBlank){
20871             return;
20872         }
20873         
20874         var _this = this;
20875         
20876         this.fireEvent('invalid', this, msg);
20877         
20878         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20879         
20880         if(this.groupId){
20881             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20882         }
20883         
20884         if(label){
20885             label.markInvalid();
20886         }
20887             
20888         if(this.inputType == 'radio'){
20889             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20890                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20891                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20892             });
20893             
20894             return;
20895         }
20896         
20897         if(!this.groupId){
20898             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20899             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20900             return;
20901         }
20902         
20903         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20904         
20905         if(!group){
20906             return;
20907         }
20908         
20909         for(var i in group){
20910             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20911             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20912         }
20913         
20914     },
20915     
20916     clearInvalid : function()
20917     {
20918         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20919         
20920         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20921         
20922         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20923         
20924         if (label && label.iconEl) {
20925             label.iconEl.removeClass(label.validClass);
20926             label.iconEl.removeClass(label.invalidClass);
20927         }
20928     },
20929     
20930     disable : function()
20931     {
20932         if(this.inputType != 'radio'){
20933             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20934             return;
20935         }
20936         
20937         var _this = this;
20938         
20939         if(this.rendered){
20940             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20941                 _this.getActionEl().addClass(this.disabledClass);
20942                 e.dom.disabled = true;
20943             });
20944         }
20945         
20946         this.disabled = true;
20947         this.fireEvent("disable", this);
20948         return this;
20949     },
20950
20951     enable : function()
20952     {
20953         if(this.inputType != 'radio'){
20954             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20955             return;
20956         }
20957         
20958         var _this = this;
20959         
20960         if(this.rendered){
20961             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20962                 _this.getActionEl().removeClass(this.disabledClass);
20963                 e.dom.disabled = false;
20964             });
20965         }
20966         
20967         this.disabled = false;
20968         this.fireEvent("enable", this);
20969         return this;
20970     },
20971     
20972     setBoxLabel : function(v)
20973     {
20974         this.boxLabel = v;
20975         
20976         if(this.rendered){
20977             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20978         }
20979     }
20980
20981 });
20982
20983 Roo.apply(Roo.bootstrap.CheckBox, {
20984     
20985     groups: {},
20986     
20987      /**
20988     * register a CheckBox Group
20989     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20990     */
20991     register : function(checkbox)
20992     {
20993         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20994             this.groups[checkbox.groupId] = {};
20995         }
20996         
20997         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20998             return;
20999         }
21000         
21001         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21002         
21003     },
21004     /**
21005     * fetch a CheckBox Group based on the group ID
21006     * @param {string} the group ID
21007     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21008     */
21009     get: function(groupId) {
21010         if (typeof(this.groups[groupId]) == 'undefined') {
21011             return false;
21012         }
21013         
21014         return this.groups[groupId] ;
21015     }
21016     
21017     
21018 });
21019 /*
21020  * - LGPL
21021  *
21022  * RadioItem
21023  * 
21024  */
21025
21026 /**
21027  * @class Roo.bootstrap.Radio
21028  * @extends Roo.bootstrap.Component
21029  * Bootstrap Radio class
21030  * @cfg {String} boxLabel - the label associated
21031  * @cfg {String} value - the value of radio
21032  * 
21033  * @constructor
21034  * Create a new Radio
21035  * @param {Object} config The config object
21036  */
21037 Roo.bootstrap.Radio = function(config){
21038     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21039     
21040 };
21041
21042 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21043     
21044     boxLabel : '',
21045     
21046     value : '',
21047     
21048     getAutoCreate : function()
21049     {
21050         var cfg = {
21051             tag : 'div',
21052             cls : 'form-group radio',
21053             cn : [
21054                 {
21055                     tag : 'label',
21056                     cls : 'box-label',
21057                     html : this.boxLabel
21058                 }
21059             ]
21060         };
21061         
21062         return cfg;
21063     },
21064     
21065     initEvents : function() 
21066     {
21067         this.parent().register(this);
21068         
21069         this.el.on('click', this.onClick, this);
21070         
21071     },
21072     
21073     onClick : function(e)
21074     {
21075         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21076             this.setChecked(true);
21077         }
21078     },
21079     
21080     setChecked : function(state, suppressEvent)
21081     {
21082         this.parent().setValue(this.value, suppressEvent);
21083         
21084     },
21085     
21086     setBoxLabel : function(v)
21087     {
21088         this.boxLabel = v;
21089         
21090         if(this.rendered){
21091             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21092         }
21093     }
21094     
21095 });
21096  
21097
21098  /*
21099  * - LGPL
21100  *
21101  * Input
21102  * 
21103  */
21104
21105 /**
21106  * @class Roo.bootstrap.SecurePass
21107  * @extends Roo.bootstrap.Input
21108  * Bootstrap SecurePass class
21109  *
21110  * 
21111  * @constructor
21112  * Create a new SecurePass
21113  * @param {Object} config The config object
21114  */
21115  
21116 Roo.bootstrap.SecurePass = function (config) {
21117     // these go here, so the translation tool can replace them..
21118     this.errors = {
21119         PwdEmpty: "Please type a password, and then retype it to confirm.",
21120         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21121         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21122         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21123         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21124         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21125         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21126         TooWeak: "Your password is Too Weak."
21127     },
21128     this.meterLabel = "Password strength:";
21129     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21130     this.meterClass = [
21131         "roo-password-meter-tooweak", 
21132         "roo-password-meter-weak", 
21133         "roo-password-meter-medium", 
21134         "roo-password-meter-strong", 
21135         "roo-password-meter-grey"
21136     ];
21137     
21138     this.errors = {};
21139     
21140     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21141 }
21142
21143 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21144     /**
21145      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21146      * {
21147      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21148      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21149      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21150      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21151      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21152      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21153      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21154      * })
21155      */
21156     // private
21157     
21158     meterWidth: 300,
21159     errorMsg :'',    
21160     errors: false,
21161     imageRoot: '/',
21162     /**
21163      * @cfg {String/Object} Label for the strength meter (defaults to
21164      * 'Password strength:')
21165      */
21166     // private
21167     meterLabel: '',
21168     /**
21169      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21170      * ['Weak', 'Medium', 'Strong'])
21171      */
21172     // private    
21173     pwdStrengths: false,    
21174     // private
21175     strength: 0,
21176     // private
21177     _lastPwd: null,
21178     // private
21179     kCapitalLetter: 0,
21180     kSmallLetter: 1,
21181     kDigit: 2,
21182     kPunctuation: 3,
21183     
21184     insecure: false,
21185     // private
21186     initEvents: function ()
21187     {
21188         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21189
21190         if (this.el.is('input[type=password]') && Roo.isSafari) {
21191             this.el.on('keydown', this.SafariOnKeyDown, this);
21192         }
21193
21194         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21195     },
21196     // private
21197     onRender: function (ct, position)
21198     {
21199         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21200         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21201         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21202
21203         this.trigger.createChild({
21204                    cn: [
21205                     {
21206                     //id: 'PwdMeter',
21207                     tag: 'div',
21208                     cls: 'roo-password-meter-grey col-xs-12',
21209                     style: {
21210                         //width: 0,
21211                         //width: this.meterWidth + 'px'                                                
21212                         }
21213                     },
21214                     {                            
21215                          cls: 'roo-password-meter-text'                          
21216                     }
21217                 ]            
21218         });
21219
21220          
21221         if (this.hideTrigger) {
21222             this.trigger.setDisplayed(false);
21223         }
21224         this.setSize(this.width || '', this.height || '');
21225     },
21226     // private
21227     onDestroy: function ()
21228     {
21229         if (this.trigger) {
21230             this.trigger.removeAllListeners();
21231             this.trigger.remove();
21232         }
21233         if (this.wrap) {
21234             this.wrap.remove();
21235         }
21236         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21237     },
21238     // private
21239     checkStrength: function ()
21240     {
21241         var pwd = this.inputEl().getValue();
21242         if (pwd == this._lastPwd) {
21243             return;
21244         }
21245
21246         var strength;
21247         if (this.ClientSideStrongPassword(pwd)) {
21248             strength = 3;
21249         } else if (this.ClientSideMediumPassword(pwd)) {
21250             strength = 2;
21251         } else if (this.ClientSideWeakPassword(pwd)) {
21252             strength = 1;
21253         } else {
21254             strength = 0;
21255         }
21256         
21257         Roo.log('strength1: ' + strength);
21258         
21259         //var pm = this.trigger.child('div/div/div').dom;
21260         var pm = this.trigger.child('div/div');
21261         pm.removeClass(this.meterClass);
21262         pm.addClass(this.meterClass[strength]);
21263                 
21264         
21265         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21266                 
21267         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21268         
21269         this._lastPwd = pwd;
21270     },
21271     reset: function ()
21272     {
21273         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21274         
21275         this._lastPwd = '';
21276         
21277         var pm = this.trigger.child('div/div');
21278         pm.removeClass(this.meterClass);
21279         pm.addClass('roo-password-meter-grey');        
21280         
21281         
21282         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21283         
21284         pt.innerHTML = '';
21285         this.inputEl().dom.type='password';
21286     },
21287     // private
21288     validateValue: function (value)
21289     {
21290         
21291         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21292             return false;
21293         }
21294         if (value.length == 0) {
21295             if (this.allowBlank) {
21296                 this.clearInvalid();
21297                 return true;
21298             }
21299
21300             this.markInvalid(this.errors.PwdEmpty);
21301             this.errorMsg = this.errors.PwdEmpty;
21302             return false;
21303         }
21304         
21305         if(this.insecure){
21306             return true;
21307         }
21308         
21309         if ('[\x21-\x7e]*'.match(value)) {
21310             this.markInvalid(this.errors.PwdBadChar);
21311             this.errorMsg = this.errors.PwdBadChar;
21312             return false;
21313         }
21314         if (value.length < 6) {
21315             this.markInvalid(this.errors.PwdShort);
21316             this.errorMsg = this.errors.PwdShort;
21317             return false;
21318         }
21319         if (value.length > 16) {
21320             this.markInvalid(this.errors.PwdLong);
21321             this.errorMsg = this.errors.PwdLong;
21322             return false;
21323         }
21324         var strength;
21325         if (this.ClientSideStrongPassword(value)) {
21326             strength = 3;
21327         } else if (this.ClientSideMediumPassword(value)) {
21328             strength = 2;
21329         } else if (this.ClientSideWeakPassword(value)) {
21330             strength = 1;
21331         } else {
21332             strength = 0;
21333         }
21334
21335         
21336         if (strength < 2) {
21337             //this.markInvalid(this.errors.TooWeak);
21338             this.errorMsg = this.errors.TooWeak;
21339             //return false;
21340         }
21341         
21342         
21343         console.log('strength2: ' + strength);
21344         
21345         //var pm = this.trigger.child('div/div/div').dom;
21346         
21347         var pm = this.trigger.child('div/div');
21348         pm.removeClass(this.meterClass);
21349         pm.addClass(this.meterClass[strength]);
21350                 
21351         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21352                 
21353         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21354         
21355         this.errorMsg = ''; 
21356         return true;
21357     },
21358     // private
21359     CharacterSetChecks: function (type)
21360     {
21361         this.type = type;
21362         this.fResult = false;
21363     },
21364     // private
21365     isctype: function (character, type)
21366     {
21367         switch (type) {  
21368             case this.kCapitalLetter:
21369                 if (character >= 'A' && character <= 'Z') {
21370                     return true;
21371                 }
21372                 break;
21373             
21374             case this.kSmallLetter:
21375                 if (character >= 'a' && character <= 'z') {
21376                     return true;
21377                 }
21378                 break;
21379             
21380             case this.kDigit:
21381                 if (character >= '0' && character <= '9') {
21382                     return true;
21383                 }
21384                 break;
21385             
21386             case this.kPunctuation:
21387                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21388                     return true;
21389                 }
21390                 break;
21391             
21392             default:
21393                 return false;
21394         }
21395
21396     },
21397     // private
21398     IsLongEnough: function (pwd, size)
21399     {
21400         return !(pwd == null || isNaN(size) || pwd.length < size);
21401     },
21402     // private
21403     SpansEnoughCharacterSets: function (word, nb)
21404     {
21405         if (!this.IsLongEnough(word, nb))
21406         {
21407             return false;
21408         }
21409
21410         var characterSetChecks = new Array(
21411             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21412             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21413         );
21414         
21415         for (var index = 0; index < word.length; ++index) {
21416             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21417                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21418                     characterSetChecks[nCharSet].fResult = true;
21419                     break;
21420                 }
21421             }
21422         }
21423
21424         var nCharSets = 0;
21425         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21426             if (characterSetChecks[nCharSet].fResult) {
21427                 ++nCharSets;
21428             }
21429         }
21430
21431         if (nCharSets < nb) {
21432             return false;
21433         }
21434         return true;
21435     },
21436     // private
21437     ClientSideStrongPassword: function (pwd)
21438     {
21439         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21440     },
21441     // private
21442     ClientSideMediumPassword: function (pwd)
21443     {
21444         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21445     },
21446     // private
21447     ClientSideWeakPassword: function (pwd)
21448     {
21449         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21450     }
21451           
21452 })//<script type="text/javascript">
21453
21454 /*
21455  * Based  Ext JS Library 1.1.1
21456  * Copyright(c) 2006-2007, Ext JS, LLC.
21457  * LGPL
21458  *
21459  */
21460  
21461 /**
21462  * @class Roo.HtmlEditorCore
21463  * @extends Roo.Component
21464  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21465  *
21466  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21467  */
21468
21469 Roo.HtmlEditorCore = function(config){
21470     
21471     
21472     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21473     
21474     
21475     this.addEvents({
21476         /**
21477          * @event initialize
21478          * Fires when the editor is fully initialized (including the iframe)
21479          * @param {Roo.HtmlEditorCore} this
21480          */
21481         initialize: true,
21482         /**
21483          * @event activate
21484          * Fires when the editor is first receives the focus. Any insertion must wait
21485          * until after this event.
21486          * @param {Roo.HtmlEditorCore} this
21487          */
21488         activate: true,
21489          /**
21490          * @event beforesync
21491          * Fires before the textarea is updated with content from the editor iframe. Return false
21492          * to cancel the sync.
21493          * @param {Roo.HtmlEditorCore} this
21494          * @param {String} html
21495          */
21496         beforesync: true,
21497          /**
21498          * @event beforepush
21499          * Fires before the iframe editor is updated with content from the textarea. Return false
21500          * to cancel the push.
21501          * @param {Roo.HtmlEditorCore} this
21502          * @param {String} html
21503          */
21504         beforepush: true,
21505          /**
21506          * @event sync
21507          * Fires when the textarea is updated with content from the editor iframe.
21508          * @param {Roo.HtmlEditorCore} this
21509          * @param {String} html
21510          */
21511         sync: true,
21512          /**
21513          * @event push
21514          * Fires when the iframe editor is updated with content from the textarea.
21515          * @param {Roo.HtmlEditorCore} this
21516          * @param {String} html
21517          */
21518         push: true,
21519         
21520         /**
21521          * @event editorevent
21522          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21523          * @param {Roo.HtmlEditorCore} this
21524          */
21525         editorevent: true
21526         
21527     });
21528     
21529     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21530     
21531     // defaults : white / black...
21532     this.applyBlacklists();
21533     
21534     
21535     
21536 };
21537
21538
21539 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21540
21541
21542      /**
21543      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21544      */
21545     
21546     owner : false,
21547     
21548      /**
21549      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21550      *                        Roo.resizable.
21551      */
21552     resizable : false,
21553      /**
21554      * @cfg {Number} height (in pixels)
21555      */   
21556     height: 300,
21557    /**
21558      * @cfg {Number} width (in pixels)
21559      */   
21560     width: 500,
21561     
21562     /**
21563      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21564      * 
21565      */
21566     stylesheets: false,
21567     
21568     // id of frame..
21569     frameId: false,
21570     
21571     // private properties
21572     validationEvent : false,
21573     deferHeight: true,
21574     initialized : false,
21575     activated : false,
21576     sourceEditMode : false,
21577     onFocus : Roo.emptyFn,
21578     iframePad:3,
21579     hideMode:'offsets',
21580     
21581     clearUp: true,
21582     
21583     // blacklist + whitelisted elements..
21584     black: false,
21585     white: false,
21586      
21587     bodyCls : '',
21588
21589     /**
21590      * Protected method that will not generally be called directly. It
21591      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21592      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21593      */
21594     getDocMarkup : function(){
21595         // body styles..
21596         var st = '';
21597         
21598         // inherit styels from page...?? 
21599         if (this.stylesheets === false) {
21600             
21601             Roo.get(document.head).select('style').each(function(node) {
21602                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21603             });
21604             
21605             Roo.get(document.head).select('link').each(function(node) { 
21606                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21607             });
21608             
21609         } else if (!this.stylesheets.length) {
21610                 // simple..
21611                 st = '<style type="text/css">' +
21612                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21613                    '</style>';
21614         } else { 
21615             st = '<style type="text/css">' +
21616                     this.stylesheets +
21617                 '</style>';
21618         }
21619         
21620         st +=  '<style type="text/css">' +
21621             'IMG { cursor: pointer } ' +
21622         '</style>';
21623
21624         var cls = 'roo-htmleditor-body';
21625         
21626         if(this.bodyCls.length){
21627             cls += ' ' + this.bodyCls;
21628         }
21629         
21630         return '<html><head>' + st  +
21631             //<style type="text/css">' +
21632             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21633             //'</style>' +
21634             ' </head><body class="' +  cls + '"></body></html>';
21635     },
21636
21637     // private
21638     onRender : function(ct, position)
21639     {
21640         var _t = this;
21641         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21642         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21643         
21644         
21645         this.el.dom.style.border = '0 none';
21646         this.el.dom.setAttribute('tabIndex', -1);
21647         this.el.addClass('x-hidden hide');
21648         
21649         
21650         
21651         if(Roo.isIE){ // fix IE 1px bogus margin
21652             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21653         }
21654        
21655         
21656         this.frameId = Roo.id();
21657         
21658          
21659         
21660         var iframe = this.owner.wrap.createChild({
21661             tag: 'iframe',
21662             cls: 'form-control', // bootstrap..
21663             id: this.frameId,
21664             name: this.frameId,
21665             frameBorder : 'no',
21666             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21667         }, this.el
21668         );
21669         
21670         
21671         this.iframe = iframe.dom;
21672
21673          this.assignDocWin();
21674         
21675         this.doc.designMode = 'on';
21676        
21677         this.doc.open();
21678         this.doc.write(this.getDocMarkup());
21679         this.doc.close();
21680
21681         
21682         var task = { // must defer to wait for browser to be ready
21683             run : function(){
21684                 //console.log("run task?" + this.doc.readyState);
21685                 this.assignDocWin();
21686                 if(this.doc.body || this.doc.readyState == 'complete'){
21687                     try {
21688                         this.doc.designMode="on";
21689                     } catch (e) {
21690                         return;
21691                     }
21692                     Roo.TaskMgr.stop(task);
21693                     this.initEditor.defer(10, this);
21694                 }
21695             },
21696             interval : 10,
21697             duration: 10000,
21698             scope: this
21699         };
21700         Roo.TaskMgr.start(task);
21701
21702     },
21703
21704     // private
21705     onResize : function(w, h)
21706     {
21707          Roo.log('resize: ' +w + ',' + h );
21708         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21709         if(!this.iframe){
21710             return;
21711         }
21712         if(typeof w == 'number'){
21713             
21714             this.iframe.style.width = w + 'px';
21715         }
21716         if(typeof h == 'number'){
21717             
21718             this.iframe.style.height = h + 'px';
21719             if(this.doc){
21720                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21721             }
21722         }
21723         
21724     },
21725
21726     /**
21727      * Toggles the editor between standard and source edit mode.
21728      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21729      */
21730     toggleSourceEdit : function(sourceEditMode){
21731         
21732         this.sourceEditMode = sourceEditMode === true;
21733         
21734         if(this.sourceEditMode){
21735  
21736             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21737             
21738         }else{
21739             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21740             //this.iframe.className = '';
21741             this.deferFocus();
21742         }
21743         //this.setSize(this.owner.wrap.getSize());
21744         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21745     },
21746
21747     
21748   
21749
21750     /**
21751      * Protected method that will not generally be called directly. If you need/want
21752      * custom HTML cleanup, this is the method you should override.
21753      * @param {String} html The HTML to be cleaned
21754      * return {String} The cleaned HTML
21755      */
21756     cleanHtml : function(html){
21757         html = String(html);
21758         if(html.length > 5){
21759             if(Roo.isSafari){ // strip safari nonsense
21760                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21761             }
21762         }
21763         if(html == '&nbsp;'){
21764             html = '';
21765         }
21766         return html;
21767     },
21768
21769     /**
21770      * HTML Editor -> Textarea
21771      * Protected method that will not generally be called directly. Syncs the contents
21772      * of the editor iframe with the textarea.
21773      */
21774     syncValue : function(){
21775         if(this.initialized){
21776             var bd = (this.doc.body || this.doc.documentElement);
21777             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21778             var html = bd.innerHTML;
21779             if(Roo.isSafari){
21780                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21781                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21782                 if(m && m[1]){
21783                     html = '<div style="'+m[0]+'">' + html + '</div>';
21784                 }
21785             }
21786             html = this.cleanHtml(html);
21787             // fix up the special chars.. normaly like back quotes in word...
21788             // however we do not want to do this with chinese..
21789             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21790                 var cc = b.charCodeAt();
21791                 if (
21792                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21793                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21794                     (cc >= 0xf900 && cc < 0xfb00 )
21795                 ) {
21796                         return b;
21797                 }
21798                 return "&#"+cc+";" 
21799             });
21800             if(this.owner.fireEvent('beforesync', this, html) !== false){
21801                 this.el.dom.value = html;
21802                 this.owner.fireEvent('sync', this, html);
21803             }
21804         }
21805     },
21806
21807     /**
21808      * Protected method that will not generally be called directly. Pushes the value of the textarea
21809      * into the iframe editor.
21810      */
21811     pushValue : function(){
21812         if(this.initialized){
21813             var v = this.el.dom.value.trim();
21814             
21815 //            if(v.length < 1){
21816 //                v = '&#160;';
21817 //            }
21818             
21819             if(this.owner.fireEvent('beforepush', this, v) !== false){
21820                 var d = (this.doc.body || this.doc.documentElement);
21821                 d.innerHTML = v;
21822                 this.cleanUpPaste();
21823                 this.el.dom.value = d.innerHTML;
21824                 this.owner.fireEvent('push', this, v);
21825             }
21826         }
21827     },
21828
21829     // private
21830     deferFocus : function(){
21831         this.focus.defer(10, this);
21832     },
21833
21834     // doc'ed in Field
21835     focus : function(){
21836         if(this.win && !this.sourceEditMode){
21837             this.win.focus();
21838         }else{
21839             this.el.focus();
21840         }
21841     },
21842     
21843     assignDocWin: function()
21844     {
21845         var iframe = this.iframe;
21846         
21847          if(Roo.isIE){
21848             this.doc = iframe.contentWindow.document;
21849             this.win = iframe.contentWindow;
21850         } else {
21851 //            if (!Roo.get(this.frameId)) {
21852 //                return;
21853 //            }
21854 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21855 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21856             
21857             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21858                 return;
21859             }
21860             
21861             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21862             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21863         }
21864     },
21865     
21866     // private
21867     initEditor : function(){
21868         //console.log("INIT EDITOR");
21869         this.assignDocWin();
21870         
21871         
21872         
21873         this.doc.designMode="on";
21874         this.doc.open();
21875         this.doc.write(this.getDocMarkup());
21876         this.doc.close();
21877         
21878         var dbody = (this.doc.body || this.doc.documentElement);
21879         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21880         // this copies styles from the containing element into thsi one..
21881         // not sure why we need all of this..
21882         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21883         
21884         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21885         //ss['background-attachment'] = 'fixed'; // w3c
21886         dbody.bgProperties = 'fixed'; // ie
21887         //Roo.DomHelper.applyStyles(dbody, ss);
21888         Roo.EventManager.on(this.doc, {
21889             //'mousedown': this.onEditorEvent,
21890             'mouseup': this.onEditorEvent,
21891             'dblclick': this.onEditorEvent,
21892             'click': this.onEditorEvent,
21893             'keyup': this.onEditorEvent,
21894             buffer:100,
21895             scope: this
21896         });
21897         if(Roo.isGecko){
21898             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21899         }
21900         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21901             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21902         }
21903         this.initialized = true;
21904
21905         this.owner.fireEvent('initialize', this);
21906         this.pushValue();
21907     },
21908
21909     // private
21910     onDestroy : function(){
21911         
21912         
21913         
21914         if(this.rendered){
21915             
21916             //for (var i =0; i < this.toolbars.length;i++) {
21917             //    // fixme - ask toolbars for heights?
21918             //    this.toolbars[i].onDestroy();
21919            // }
21920             
21921             //this.wrap.dom.innerHTML = '';
21922             //this.wrap.remove();
21923         }
21924     },
21925
21926     // private
21927     onFirstFocus : function(){
21928         
21929         this.assignDocWin();
21930         
21931         
21932         this.activated = true;
21933          
21934     
21935         if(Roo.isGecko){ // prevent silly gecko errors
21936             this.win.focus();
21937             var s = this.win.getSelection();
21938             if(!s.focusNode || s.focusNode.nodeType != 3){
21939                 var r = s.getRangeAt(0);
21940                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21941                 r.collapse(true);
21942                 this.deferFocus();
21943             }
21944             try{
21945                 this.execCmd('useCSS', true);
21946                 this.execCmd('styleWithCSS', false);
21947             }catch(e){}
21948         }
21949         this.owner.fireEvent('activate', this);
21950     },
21951
21952     // private
21953     adjustFont: function(btn){
21954         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21955         //if(Roo.isSafari){ // safari
21956         //    adjust *= 2;
21957        // }
21958         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21959         if(Roo.isSafari){ // safari
21960             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21961             v =  (v < 10) ? 10 : v;
21962             v =  (v > 48) ? 48 : v;
21963             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21964             
21965         }
21966         
21967         
21968         v = Math.max(1, v+adjust);
21969         
21970         this.execCmd('FontSize', v  );
21971     },
21972
21973     onEditorEvent : function(e)
21974     {
21975         this.owner.fireEvent('editorevent', this, e);
21976       //  this.updateToolbar();
21977         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21978     },
21979
21980     insertTag : function(tg)
21981     {
21982         // could be a bit smarter... -> wrap the current selected tRoo..
21983         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21984             
21985             range = this.createRange(this.getSelection());
21986             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21987             wrappingNode.appendChild(range.extractContents());
21988             range.insertNode(wrappingNode);
21989
21990             return;
21991             
21992             
21993             
21994         }
21995         this.execCmd("formatblock",   tg);
21996         
21997     },
21998     
21999     insertText : function(txt)
22000     {
22001         
22002         
22003         var range = this.createRange();
22004         range.deleteContents();
22005                //alert(Sender.getAttribute('label'));
22006                
22007         range.insertNode(this.doc.createTextNode(txt));
22008     } ,
22009     
22010      
22011
22012     /**
22013      * Executes a Midas editor command on the editor document and performs necessary focus and
22014      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22015      * @param {String} cmd The Midas command
22016      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22017      */
22018     relayCmd : function(cmd, value){
22019         this.win.focus();
22020         this.execCmd(cmd, value);
22021         this.owner.fireEvent('editorevent', this);
22022         //this.updateToolbar();
22023         this.owner.deferFocus();
22024     },
22025
22026     /**
22027      * Executes a Midas editor command directly on the editor document.
22028      * For visual commands, you should use {@link #relayCmd} instead.
22029      * <b>This should only be called after the editor is initialized.</b>
22030      * @param {String} cmd The Midas command
22031      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22032      */
22033     execCmd : function(cmd, value){
22034         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22035         this.syncValue();
22036     },
22037  
22038  
22039    
22040     /**
22041      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22042      * to insert tRoo.
22043      * @param {String} text | dom node.. 
22044      */
22045     insertAtCursor : function(text)
22046     {
22047         
22048         if(!this.activated){
22049             return;
22050         }
22051         /*
22052         if(Roo.isIE){
22053             this.win.focus();
22054             var r = this.doc.selection.createRange();
22055             if(r){
22056                 r.collapse(true);
22057                 r.pasteHTML(text);
22058                 this.syncValue();
22059                 this.deferFocus();
22060             
22061             }
22062             return;
22063         }
22064         */
22065         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22066             this.win.focus();
22067             
22068             
22069             // from jquery ui (MIT licenced)
22070             var range, node;
22071             var win = this.win;
22072             
22073             if (win.getSelection && win.getSelection().getRangeAt) {
22074                 range = win.getSelection().getRangeAt(0);
22075                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22076                 range.insertNode(node);
22077             } else if (win.document.selection && win.document.selection.createRange) {
22078                 // no firefox support
22079                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22080                 win.document.selection.createRange().pasteHTML(txt);
22081             } else {
22082                 // no firefox support
22083                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22084                 this.execCmd('InsertHTML', txt);
22085             } 
22086             
22087             this.syncValue();
22088             
22089             this.deferFocus();
22090         }
22091     },
22092  // private
22093     mozKeyPress : function(e){
22094         if(e.ctrlKey){
22095             var c = e.getCharCode(), cmd;
22096           
22097             if(c > 0){
22098                 c = String.fromCharCode(c).toLowerCase();
22099                 switch(c){
22100                     case 'b':
22101                         cmd = 'bold';
22102                         break;
22103                     case 'i':
22104                         cmd = 'italic';
22105                         break;
22106                     
22107                     case 'u':
22108                         cmd = 'underline';
22109                         break;
22110                     
22111                     case 'v':
22112                         this.cleanUpPaste.defer(100, this);
22113                         return;
22114                         
22115                 }
22116                 if(cmd){
22117                     this.win.focus();
22118                     this.execCmd(cmd);
22119                     this.deferFocus();
22120                     e.preventDefault();
22121                 }
22122                 
22123             }
22124         }
22125     },
22126
22127     // private
22128     fixKeys : function(){ // load time branching for fastest keydown performance
22129         if(Roo.isIE){
22130             return function(e){
22131                 var k = e.getKey(), r;
22132                 if(k == e.TAB){
22133                     e.stopEvent();
22134                     r = this.doc.selection.createRange();
22135                     if(r){
22136                         r.collapse(true);
22137                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22138                         this.deferFocus();
22139                     }
22140                     return;
22141                 }
22142                 
22143                 if(k == e.ENTER){
22144                     r = this.doc.selection.createRange();
22145                     if(r){
22146                         var target = r.parentElement();
22147                         if(!target || target.tagName.toLowerCase() != 'li'){
22148                             e.stopEvent();
22149                             r.pasteHTML('<br />');
22150                             r.collapse(false);
22151                             r.select();
22152                         }
22153                     }
22154                 }
22155                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22156                     this.cleanUpPaste.defer(100, this);
22157                     return;
22158                 }
22159                 
22160                 
22161             };
22162         }else if(Roo.isOpera){
22163             return function(e){
22164                 var k = e.getKey();
22165                 if(k == e.TAB){
22166                     e.stopEvent();
22167                     this.win.focus();
22168                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22169                     this.deferFocus();
22170                 }
22171                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22172                     this.cleanUpPaste.defer(100, this);
22173                     return;
22174                 }
22175                 
22176             };
22177         }else if(Roo.isSafari){
22178             return function(e){
22179                 var k = e.getKey();
22180                 
22181                 if(k == e.TAB){
22182                     e.stopEvent();
22183                     this.execCmd('InsertText','\t');
22184                     this.deferFocus();
22185                     return;
22186                 }
22187                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22188                     this.cleanUpPaste.defer(100, this);
22189                     return;
22190                 }
22191                 
22192              };
22193         }
22194     }(),
22195     
22196     getAllAncestors: function()
22197     {
22198         var p = this.getSelectedNode();
22199         var a = [];
22200         if (!p) {
22201             a.push(p); // push blank onto stack..
22202             p = this.getParentElement();
22203         }
22204         
22205         
22206         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22207             a.push(p);
22208             p = p.parentNode;
22209         }
22210         a.push(this.doc.body);
22211         return a;
22212     },
22213     lastSel : false,
22214     lastSelNode : false,
22215     
22216     
22217     getSelection : function() 
22218     {
22219         this.assignDocWin();
22220         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22221     },
22222     
22223     getSelectedNode: function() 
22224     {
22225         // this may only work on Gecko!!!
22226         
22227         // should we cache this!!!!
22228         
22229         
22230         
22231          
22232         var range = this.createRange(this.getSelection()).cloneRange();
22233         
22234         if (Roo.isIE) {
22235             var parent = range.parentElement();
22236             while (true) {
22237                 var testRange = range.duplicate();
22238                 testRange.moveToElementText(parent);
22239                 if (testRange.inRange(range)) {
22240                     break;
22241                 }
22242                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22243                     break;
22244                 }
22245                 parent = parent.parentElement;
22246             }
22247             return parent;
22248         }
22249         
22250         // is ancestor a text element.
22251         var ac =  range.commonAncestorContainer;
22252         if (ac.nodeType == 3) {
22253             ac = ac.parentNode;
22254         }
22255         
22256         var ar = ac.childNodes;
22257          
22258         var nodes = [];
22259         var other_nodes = [];
22260         var has_other_nodes = false;
22261         for (var i=0;i<ar.length;i++) {
22262             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22263                 continue;
22264             }
22265             // fullly contained node.
22266             
22267             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22268                 nodes.push(ar[i]);
22269                 continue;
22270             }
22271             
22272             // probably selected..
22273             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22274                 other_nodes.push(ar[i]);
22275                 continue;
22276             }
22277             // outer..
22278             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22279                 continue;
22280             }
22281             
22282             
22283             has_other_nodes = true;
22284         }
22285         if (!nodes.length && other_nodes.length) {
22286             nodes= other_nodes;
22287         }
22288         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22289             return false;
22290         }
22291         
22292         return nodes[0];
22293     },
22294     createRange: function(sel)
22295     {
22296         // this has strange effects when using with 
22297         // top toolbar - not sure if it's a great idea.
22298         //this.editor.contentWindow.focus();
22299         if (typeof sel != "undefined") {
22300             try {
22301                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22302             } catch(e) {
22303                 return this.doc.createRange();
22304             }
22305         } else {
22306             return this.doc.createRange();
22307         }
22308     },
22309     getParentElement: function()
22310     {
22311         
22312         this.assignDocWin();
22313         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22314         
22315         var range = this.createRange(sel);
22316          
22317         try {
22318             var p = range.commonAncestorContainer;
22319             while (p.nodeType == 3) { // text node
22320                 p = p.parentNode;
22321             }
22322             return p;
22323         } catch (e) {
22324             return null;
22325         }
22326     
22327     },
22328     /***
22329      *
22330      * Range intersection.. the hard stuff...
22331      *  '-1' = before
22332      *  '0' = hits..
22333      *  '1' = after.
22334      *         [ -- selected range --- ]
22335      *   [fail]                        [fail]
22336      *
22337      *    basically..
22338      *      if end is before start or  hits it. fail.
22339      *      if start is after end or hits it fail.
22340      *
22341      *   if either hits (but other is outside. - then it's not 
22342      *   
22343      *    
22344      **/
22345     
22346     
22347     // @see http://www.thismuchiknow.co.uk/?p=64.
22348     rangeIntersectsNode : function(range, node)
22349     {
22350         var nodeRange = node.ownerDocument.createRange();
22351         try {
22352             nodeRange.selectNode(node);
22353         } catch (e) {
22354             nodeRange.selectNodeContents(node);
22355         }
22356     
22357         var rangeStartRange = range.cloneRange();
22358         rangeStartRange.collapse(true);
22359     
22360         var rangeEndRange = range.cloneRange();
22361         rangeEndRange.collapse(false);
22362     
22363         var nodeStartRange = nodeRange.cloneRange();
22364         nodeStartRange.collapse(true);
22365     
22366         var nodeEndRange = nodeRange.cloneRange();
22367         nodeEndRange.collapse(false);
22368     
22369         return rangeStartRange.compareBoundaryPoints(
22370                  Range.START_TO_START, nodeEndRange) == -1 &&
22371                rangeEndRange.compareBoundaryPoints(
22372                  Range.START_TO_START, nodeStartRange) == 1;
22373         
22374          
22375     },
22376     rangeCompareNode : function(range, node)
22377     {
22378         var nodeRange = node.ownerDocument.createRange();
22379         try {
22380             nodeRange.selectNode(node);
22381         } catch (e) {
22382             nodeRange.selectNodeContents(node);
22383         }
22384         
22385         
22386         range.collapse(true);
22387     
22388         nodeRange.collapse(true);
22389      
22390         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22391         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22392          
22393         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22394         
22395         var nodeIsBefore   =  ss == 1;
22396         var nodeIsAfter    = ee == -1;
22397         
22398         if (nodeIsBefore && nodeIsAfter) {
22399             return 0; // outer
22400         }
22401         if (!nodeIsBefore && nodeIsAfter) {
22402             return 1; //right trailed.
22403         }
22404         
22405         if (nodeIsBefore && !nodeIsAfter) {
22406             return 2;  // left trailed.
22407         }
22408         // fully contined.
22409         return 3;
22410     },
22411
22412     // private? - in a new class?
22413     cleanUpPaste :  function()
22414     {
22415         // cleans up the whole document..
22416         Roo.log('cleanuppaste');
22417         
22418         this.cleanUpChildren(this.doc.body);
22419         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22420         if (clean != this.doc.body.innerHTML) {
22421             this.doc.body.innerHTML = clean;
22422         }
22423         
22424     },
22425     
22426     cleanWordChars : function(input) {// change the chars to hex code
22427         var he = Roo.HtmlEditorCore;
22428         
22429         var output = input;
22430         Roo.each(he.swapCodes, function(sw) { 
22431             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22432             
22433             output = output.replace(swapper, sw[1]);
22434         });
22435         
22436         return output;
22437     },
22438     
22439     
22440     cleanUpChildren : function (n)
22441     {
22442         if (!n.childNodes.length) {
22443             return;
22444         }
22445         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22446            this.cleanUpChild(n.childNodes[i]);
22447         }
22448     },
22449     
22450     
22451         
22452     
22453     cleanUpChild : function (node)
22454     {
22455         var ed = this;
22456         //console.log(node);
22457         if (node.nodeName == "#text") {
22458             // clean up silly Windows -- stuff?
22459             return; 
22460         }
22461         if (node.nodeName == "#comment") {
22462             node.parentNode.removeChild(node);
22463             // clean up silly Windows -- stuff?
22464             return; 
22465         }
22466         var lcname = node.tagName.toLowerCase();
22467         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22468         // whitelist of tags..
22469         
22470         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22471             // remove node.
22472             node.parentNode.removeChild(node);
22473             return;
22474             
22475         }
22476         
22477         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22478         
22479         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22480         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22481         
22482         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22483         //    remove_keep_children = true;
22484         //}
22485         
22486         if (remove_keep_children) {
22487             this.cleanUpChildren(node);
22488             // inserts everything just before this node...
22489             while (node.childNodes.length) {
22490                 var cn = node.childNodes[0];
22491                 node.removeChild(cn);
22492                 node.parentNode.insertBefore(cn, node);
22493             }
22494             node.parentNode.removeChild(node);
22495             return;
22496         }
22497         
22498         if (!node.attributes || !node.attributes.length) {
22499             this.cleanUpChildren(node);
22500             return;
22501         }
22502         
22503         function cleanAttr(n,v)
22504         {
22505             
22506             if (v.match(/^\./) || v.match(/^\//)) {
22507                 return;
22508             }
22509             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22510                 return;
22511             }
22512             if (v.match(/^#/)) {
22513                 return;
22514             }
22515 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22516             node.removeAttribute(n);
22517             
22518         }
22519         
22520         var cwhite = this.cwhite;
22521         var cblack = this.cblack;
22522             
22523         function cleanStyle(n,v)
22524         {
22525             if (v.match(/expression/)) { //XSS?? should we even bother..
22526                 node.removeAttribute(n);
22527                 return;
22528             }
22529             
22530             var parts = v.split(/;/);
22531             var clean = [];
22532             
22533             Roo.each(parts, function(p) {
22534                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22535                 if (!p.length) {
22536                     return true;
22537                 }
22538                 var l = p.split(':').shift().replace(/\s+/g,'');
22539                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22540                 
22541                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22542 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22543                     //node.removeAttribute(n);
22544                     return true;
22545                 }
22546                 //Roo.log()
22547                 // only allow 'c whitelisted system attributes'
22548                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22549 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22550                     //node.removeAttribute(n);
22551                     return true;
22552                 }
22553                 
22554                 
22555                  
22556                 
22557                 clean.push(p);
22558                 return true;
22559             });
22560             if (clean.length) { 
22561                 node.setAttribute(n, clean.join(';'));
22562             } else {
22563                 node.removeAttribute(n);
22564             }
22565             
22566         }
22567         
22568         
22569         for (var i = node.attributes.length-1; i > -1 ; i--) {
22570             var a = node.attributes[i];
22571             //console.log(a);
22572             
22573             if (a.name.toLowerCase().substr(0,2)=='on')  {
22574                 node.removeAttribute(a.name);
22575                 continue;
22576             }
22577             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22578                 node.removeAttribute(a.name);
22579                 continue;
22580             }
22581             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22582                 cleanAttr(a.name,a.value); // fixme..
22583                 continue;
22584             }
22585             if (a.name == 'style') {
22586                 cleanStyle(a.name,a.value);
22587                 continue;
22588             }
22589             /// clean up MS crap..
22590             // tecnically this should be a list of valid class'es..
22591             
22592             
22593             if (a.name == 'class') {
22594                 if (a.value.match(/^Mso/)) {
22595                     node.className = '';
22596                 }
22597                 
22598                 if (a.value.match(/^body$/)) {
22599                     node.className = '';
22600                 }
22601                 continue;
22602             }
22603             
22604             // style cleanup!?
22605             // class cleanup?
22606             
22607         }
22608         
22609         
22610         this.cleanUpChildren(node);
22611         
22612         
22613     },
22614     
22615     /**
22616      * Clean up MS wordisms...
22617      */
22618     cleanWord : function(node)
22619     {
22620         
22621         
22622         if (!node) {
22623             this.cleanWord(this.doc.body);
22624             return;
22625         }
22626         if (node.nodeName == "#text") {
22627             // clean up silly Windows -- stuff?
22628             return; 
22629         }
22630         if (node.nodeName == "#comment") {
22631             node.parentNode.removeChild(node);
22632             // clean up silly Windows -- stuff?
22633             return; 
22634         }
22635         
22636         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22637             node.parentNode.removeChild(node);
22638             return;
22639         }
22640         
22641         // remove - but keep children..
22642         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22643             while (node.childNodes.length) {
22644                 var cn = node.childNodes[0];
22645                 node.removeChild(cn);
22646                 node.parentNode.insertBefore(cn, node);
22647             }
22648             node.parentNode.removeChild(node);
22649             this.iterateChildren(node, this.cleanWord);
22650             return;
22651         }
22652         // clean styles
22653         if (node.className.length) {
22654             
22655             var cn = node.className.split(/\W+/);
22656             var cna = [];
22657             Roo.each(cn, function(cls) {
22658                 if (cls.match(/Mso[a-zA-Z]+/)) {
22659                     return;
22660                 }
22661                 cna.push(cls);
22662             });
22663             node.className = cna.length ? cna.join(' ') : '';
22664             if (!cna.length) {
22665                 node.removeAttribute("class");
22666             }
22667         }
22668         
22669         if (node.hasAttribute("lang")) {
22670             node.removeAttribute("lang");
22671         }
22672         
22673         if (node.hasAttribute("style")) {
22674             
22675             var styles = node.getAttribute("style").split(";");
22676             var nstyle = [];
22677             Roo.each(styles, function(s) {
22678                 if (!s.match(/:/)) {
22679                     return;
22680                 }
22681                 var kv = s.split(":");
22682                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22683                     return;
22684                 }
22685                 // what ever is left... we allow.
22686                 nstyle.push(s);
22687             });
22688             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22689             if (!nstyle.length) {
22690                 node.removeAttribute('style');
22691             }
22692         }
22693         this.iterateChildren(node, this.cleanWord);
22694         
22695         
22696         
22697     },
22698     /**
22699      * iterateChildren of a Node, calling fn each time, using this as the scole..
22700      * @param {DomNode} node node to iterate children of.
22701      * @param {Function} fn method of this class to call on each item.
22702      */
22703     iterateChildren : function(node, fn)
22704     {
22705         if (!node.childNodes.length) {
22706                 return;
22707         }
22708         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22709            fn.call(this, node.childNodes[i])
22710         }
22711     },
22712     
22713     
22714     /**
22715      * cleanTableWidths.
22716      *
22717      * Quite often pasting from word etc.. results in tables with column and widths.
22718      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22719      *
22720      */
22721     cleanTableWidths : function(node)
22722     {
22723          
22724          
22725         if (!node) {
22726             this.cleanTableWidths(this.doc.body);
22727             return;
22728         }
22729         
22730         // ignore list...
22731         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22732             return; 
22733         }
22734         Roo.log(node.tagName);
22735         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22736             this.iterateChildren(node, this.cleanTableWidths);
22737             return;
22738         }
22739         if (node.hasAttribute('width')) {
22740             node.removeAttribute('width');
22741         }
22742         
22743          
22744         if (node.hasAttribute("style")) {
22745             // pretty basic...
22746             
22747             var styles = node.getAttribute("style").split(";");
22748             var nstyle = [];
22749             Roo.each(styles, function(s) {
22750                 if (!s.match(/:/)) {
22751                     return;
22752                 }
22753                 var kv = s.split(":");
22754                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22755                     return;
22756                 }
22757                 // what ever is left... we allow.
22758                 nstyle.push(s);
22759             });
22760             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22761             if (!nstyle.length) {
22762                 node.removeAttribute('style');
22763             }
22764         }
22765         
22766         this.iterateChildren(node, this.cleanTableWidths);
22767         
22768         
22769     },
22770     
22771     
22772     
22773     
22774     domToHTML : function(currentElement, depth, nopadtext) {
22775         
22776         depth = depth || 0;
22777         nopadtext = nopadtext || false;
22778     
22779         if (!currentElement) {
22780             return this.domToHTML(this.doc.body);
22781         }
22782         
22783         //Roo.log(currentElement);
22784         var j;
22785         var allText = false;
22786         var nodeName = currentElement.nodeName;
22787         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22788         
22789         if  (nodeName == '#text') {
22790             
22791             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22792         }
22793         
22794         
22795         var ret = '';
22796         if (nodeName != 'BODY') {
22797              
22798             var i = 0;
22799             // Prints the node tagName, such as <A>, <IMG>, etc
22800             if (tagName) {
22801                 var attr = [];
22802                 for(i = 0; i < currentElement.attributes.length;i++) {
22803                     // quoting?
22804                     var aname = currentElement.attributes.item(i).name;
22805                     if (!currentElement.attributes.item(i).value.length) {
22806                         continue;
22807                     }
22808                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22809                 }
22810                 
22811                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22812             } 
22813             else {
22814                 
22815                 // eack
22816             }
22817         } else {
22818             tagName = false;
22819         }
22820         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22821             return ret;
22822         }
22823         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22824             nopadtext = true;
22825         }
22826         
22827         
22828         // Traverse the tree
22829         i = 0;
22830         var currentElementChild = currentElement.childNodes.item(i);
22831         var allText = true;
22832         var innerHTML  = '';
22833         lastnode = '';
22834         while (currentElementChild) {
22835             // Formatting code (indent the tree so it looks nice on the screen)
22836             var nopad = nopadtext;
22837             if (lastnode == 'SPAN') {
22838                 nopad  = true;
22839             }
22840             // text
22841             if  (currentElementChild.nodeName == '#text') {
22842                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22843                 toadd = nopadtext ? toadd : toadd.trim();
22844                 if (!nopad && toadd.length > 80) {
22845                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22846                 }
22847                 innerHTML  += toadd;
22848                 
22849                 i++;
22850                 currentElementChild = currentElement.childNodes.item(i);
22851                 lastNode = '';
22852                 continue;
22853             }
22854             allText = false;
22855             
22856             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22857                 
22858             // Recursively traverse the tree structure of the child node
22859             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22860             lastnode = currentElementChild.nodeName;
22861             i++;
22862             currentElementChild=currentElement.childNodes.item(i);
22863         }
22864         
22865         ret += innerHTML;
22866         
22867         if (!allText) {
22868                 // The remaining code is mostly for formatting the tree
22869             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22870         }
22871         
22872         
22873         if (tagName) {
22874             ret+= "</"+tagName+">";
22875         }
22876         return ret;
22877         
22878     },
22879         
22880     applyBlacklists : function()
22881     {
22882         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22883         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22884         
22885         this.white = [];
22886         this.black = [];
22887         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22888             if (b.indexOf(tag) > -1) {
22889                 return;
22890             }
22891             this.white.push(tag);
22892             
22893         }, this);
22894         
22895         Roo.each(w, function(tag) {
22896             if (b.indexOf(tag) > -1) {
22897                 return;
22898             }
22899             if (this.white.indexOf(tag) > -1) {
22900                 return;
22901             }
22902             this.white.push(tag);
22903             
22904         }, this);
22905         
22906         
22907         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22908             if (w.indexOf(tag) > -1) {
22909                 return;
22910             }
22911             this.black.push(tag);
22912             
22913         }, this);
22914         
22915         Roo.each(b, function(tag) {
22916             if (w.indexOf(tag) > -1) {
22917                 return;
22918             }
22919             if (this.black.indexOf(tag) > -1) {
22920                 return;
22921             }
22922             this.black.push(tag);
22923             
22924         }, this);
22925         
22926         
22927         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22928         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22929         
22930         this.cwhite = [];
22931         this.cblack = [];
22932         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22933             if (b.indexOf(tag) > -1) {
22934                 return;
22935             }
22936             this.cwhite.push(tag);
22937             
22938         }, this);
22939         
22940         Roo.each(w, function(tag) {
22941             if (b.indexOf(tag) > -1) {
22942                 return;
22943             }
22944             if (this.cwhite.indexOf(tag) > -1) {
22945                 return;
22946             }
22947             this.cwhite.push(tag);
22948             
22949         }, this);
22950         
22951         
22952         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22953             if (w.indexOf(tag) > -1) {
22954                 return;
22955             }
22956             this.cblack.push(tag);
22957             
22958         }, this);
22959         
22960         Roo.each(b, function(tag) {
22961             if (w.indexOf(tag) > -1) {
22962                 return;
22963             }
22964             if (this.cblack.indexOf(tag) > -1) {
22965                 return;
22966             }
22967             this.cblack.push(tag);
22968             
22969         }, this);
22970     },
22971     
22972     setStylesheets : function(stylesheets)
22973     {
22974         if(typeof(stylesheets) == 'string'){
22975             Roo.get(this.iframe.contentDocument.head).createChild({
22976                 tag : 'link',
22977                 rel : 'stylesheet',
22978                 type : 'text/css',
22979                 href : stylesheets
22980             });
22981             
22982             return;
22983         }
22984         var _this = this;
22985      
22986         Roo.each(stylesheets, function(s) {
22987             if(!s.length){
22988                 return;
22989             }
22990             
22991             Roo.get(_this.iframe.contentDocument.head).createChild({
22992                 tag : 'link',
22993                 rel : 'stylesheet',
22994                 type : 'text/css',
22995                 href : s
22996             });
22997         });
22998
22999         
23000     },
23001     
23002     removeStylesheets : function()
23003     {
23004         var _this = this;
23005         
23006         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23007             s.remove();
23008         });
23009     },
23010     
23011     setStyle : function(style)
23012     {
23013         Roo.get(this.iframe.contentDocument.head).createChild({
23014             tag : 'style',
23015             type : 'text/css',
23016             html : style
23017         });
23018
23019         return;
23020     }
23021     
23022     // hide stuff that is not compatible
23023     /**
23024      * @event blur
23025      * @hide
23026      */
23027     /**
23028      * @event change
23029      * @hide
23030      */
23031     /**
23032      * @event focus
23033      * @hide
23034      */
23035     /**
23036      * @event specialkey
23037      * @hide
23038      */
23039     /**
23040      * @cfg {String} fieldClass @hide
23041      */
23042     /**
23043      * @cfg {String} focusClass @hide
23044      */
23045     /**
23046      * @cfg {String} autoCreate @hide
23047      */
23048     /**
23049      * @cfg {String} inputType @hide
23050      */
23051     /**
23052      * @cfg {String} invalidClass @hide
23053      */
23054     /**
23055      * @cfg {String} invalidText @hide
23056      */
23057     /**
23058      * @cfg {String} msgFx @hide
23059      */
23060     /**
23061      * @cfg {String} validateOnBlur @hide
23062      */
23063 });
23064
23065 Roo.HtmlEditorCore.white = [
23066         'area', 'br', 'img', 'input', 'hr', 'wbr',
23067         
23068        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23069        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23070        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23071        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23072        'table',   'ul',         'xmp', 
23073        
23074        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23075       'thead',   'tr', 
23076      
23077       'dir', 'menu', 'ol', 'ul', 'dl',
23078        
23079       'embed',  'object'
23080 ];
23081
23082
23083 Roo.HtmlEditorCore.black = [
23084     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23085         'applet', // 
23086         'base',   'basefont', 'bgsound', 'blink',  'body', 
23087         'frame',  'frameset', 'head',    'html',   'ilayer', 
23088         'iframe', 'layer',  'link',     'meta',    'object',   
23089         'script', 'style' ,'title',  'xml' // clean later..
23090 ];
23091 Roo.HtmlEditorCore.clean = [
23092     'script', 'style', 'title', 'xml'
23093 ];
23094 Roo.HtmlEditorCore.remove = [
23095     'font'
23096 ];
23097 // attributes..
23098
23099 Roo.HtmlEditorCore.ablack = [
23100     'on'
23101 ];
23102     
23103 Roo.HtmlEditorCore.aclean = [ 
23104     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23105 ];
23106
23107 // protocols..
23108 Roo.HtmlEditorCore.pwhite= [
23109         'http',  'https',  'mailto'
23110 ];
23111
23112 // white listed style attributes.
23113 Roo.HtmlEditorCore.cwhite= [
23114       //  'text-align', /// default is to allow most things..
23115       
23116          
23117 //        'font-size'//??
23118 ];
23119
23120 // black listed style attributes.
23121 Roo.HtmlEditorCore.cblack= [
23122       //  'font-size' -- this can be set by the project 
23123 ];
23124
23125
23126 Roo.HtmlEditorCore.swapCodes   =[ 
23127     [    8211, "--" ], 
23128     [    8212, "--" ], 
23129     [    8216,  "'" ],  
23130     [    8217, "'" ],  
23131     [    8220, '"' ],  
23132     [    8221, '"' ],  
23133     [    8226, "*" ],  
23134     [    8230, "..." ]
23135 ]; 
23136
23137     /*
23138  * - LGPL
23139  *
23140  * HtmlEditor
23141  * 
23142  */
23143
23144 /**
23145  * @class Roo.bootstrap.HtmlEditor
23146  * @extends Roo.bootstrap.TextArea
23147  * Bootstrap HtmlEditor class
23148
23149  * @constructor
23150  * Create a new HtmlEditor
23151  * @param {Object} config The config object
23152  */
23153
23154 Roo.bootstrap.HtmlEditor = function(config){
23155     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23156     if (!this.toolbars) {
23157         this.toolbars = [];
23158     }
23159     
23160     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23161     this.addEvents({
23162             /**
23163              * @event initialize
23164              * Fires when the editor is fully initialized (including the iframe)
23165              * @param {HtmlEditor} this
23166              */
23167             initialize: true,
23168             /**
23169              * @event activate
23170              * Fires when the editor is first receives the focus. Any insertion must wait
23171              * until after this event.
23172              * @param {HtmlEditor} this
23173              */
23174             activate: true,
23175              /**
23176              * @event beforesync
23177              * Fires before the textarea is updated with content from the editor iframe. Return false
23178              * to cancel the sync.
23179              * @param {HtmlEditor} this
23180              * @param {String} html
23181              */
23182             beforesync: true,
23183              /**
23184              * @event beforepush
23185              * Fires before the iframe editor is updated with content from the textarea. Return false
23186              * to cancel the push.
23187              * @param {HtmlEditor} this
23188              * @param {String} html
23189              */
23190             beforepush: true,
23191              /**
23192              * @event sync
23193              * Fires when the textarea is updated with content from the editor iframe.
23194              * @param {HtmlEditor} this
23195              * @param {String} html
23196              */
23197             sync: true,
23198              /**
23199              * @event push
23200              * Fires when the iframe editor is updated with content from the textarea.
23201              * @param {HtmlEditor} this
23202              * @param {String} html
23203              */
23204             push: true,
23205              /**
23206              * @event editmodechange
23207              * Fires when the editor switches edit modes
23208              * @param {HtmlEditor} this
23209              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23210              */
23211             editmodechange: true,
23212             /**
23213              * @event editorevent
23214              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23215              * @param {HtmlEditor} this
23216              */
23217             editorevent: true,
23218             /**
23219              * @event firstfocus
23220              * Fires when on first focus - needed by toolbars..
23221              * @param {HtmlEditor} this
23222              */
23223             firstfocus: true,
23224             /**
23225              * @event autosave
23226              * Auto save the htmlEditor value as a file into Events
23227              * @param {HtmlEditor} this
23228              */
23229             autosave: true,
23230             /**
23231              * @event savedpreview
23232              * preview the saved version of htmlEditor
23233              * @param {HtmlEditor} this
23234              */
23235             savedpreview: true
23236         });
23237 };
23238
23239
23240 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23241     
23242     
23243       /**
23244      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23245      */
23246     toolbars : false,
23247     
23248      /**
23249     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23250     */
23251     btns : [],
23252    
23253      /**
23254      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23255      *                        Roo.resizable.
23256      */
23257     resizable : false,
23258      /**
23259      * @cfg {Number} height (in pixels)
23260      */   
23261     height: 300,
23262    /**
23263      * @cfg {Number} width (in pixels)
23264      */   
23265     width: false,
23266     
23267     /**
23268      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23269      * 
23270      */
23271     stylesheets: false,
23272     
23273     // id of frame..
23274     frameId: false,
23275     
23276     // private properties
23277     validationEvent : false,
23278     deferHeight: true,
23279     initialized : false,
23280     activated : false,
23281     
23282     onFocus : Roo.emptyFn,
23283     iframePad:3,
23284     hideMode:'offsets',
23285     
23286     tbContainer : false,
23287     
23288     bodyCls : '',
23289     
23290     toolbarContainer :function() {
23291         return this.wrap.select('.x-html-editor-tb',true).first();
23292     },
23293
23294     /**
23295      * Protected method that will not generally be called directly. It
23296      * is called when the editor creates its toolbar. Override this method if you need to
23297      * add custom toolbar buttons.
23298      * @param {HtmlEditor} editor
23299      */
23300     createToolbar : function(){
23301         Roo.log('renewing');
23302         Roo.log("create toolbars");
23303         
23304         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23305         this.toolbars[0].render(this.toolbarContainer());
23306         
23307         return;
23308         
23309 //        if (!editor.toolbars || !editor.toolbars.length) {
23310 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23311 //        }
23312 //        
23313 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23314 //            editor.toolbars[i] = Roo.factory(
23315 //                    typeof(editor.toolbars[i]) == 'string' ?
23316 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23317 //                Roo.bootstrap.HtmlEditor);
23318 //            editor.toolbars[i].init(editor);
23319 //        }
23320     },
23321
23322      
23323     // private
23324     onRender : function(ct, position)
23325     {
23326        // Roo.log("Call onRender: " + this.xtype);
23327         var _t = this;
23328         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23329       
23330         this.wrap = this.inputEl().wrap({
23331             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23332         });
23333         
23334         this.editorcore.onRender(ct, position);
23335          
23336         if (this.resizable) {
23337             this.resizeEl = new Roo.Resizable(this.wrap, {
23338                 pinned : true,
23339                 wrap: true,
23340                 dynamic : true,
23341                 minHeight : this.height,
23342                 height: this.height,
23343                 handles : this.resizable,
23344                 width: this.width,
23345                 listeners : {
23346                     resize : function(r, w, h) {
23347                         _t.onResize(w,h); // -something
23348                     }
23349                 }
23350             });
23351             
23352         }
23353         this.createToolbar(this);
23354        
23355         
23356         if(!this.width && this.resizable){
23357             this.setSize(this.wrap.getSize());
23358         }
23359         if (this.resizeEl) {
23360             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23361             // should trigger onReize..
23362         }
23363         
23364     },
23365
23366     // private
23367     onResize : function(w, h)
23368     {
23369         Roo.log('resize: ' +w + ',' + h );
23370         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23371         var ew = false;
23372         var eh = false;
23373         
23374         if(this.inputEl() ){
23375             if(typeof w == 'number'){
23376                 var aw = w - this.wrap.getFrameWidth('lr');
23377                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23378                 ew = aw;
23379             }
23380             if(typeof h == 'number'){
23381                  var tbh = -11;  // fixme it needs to tool bar size!
23382                 for (var i =0; i < this.toolbars.length;i++) {
23383                     // fixme - ask toolbars for heights?
23384                     tbh += this.toolbars[i].el.getHeight();
23385                     //if (this.toolbars[i].footer) {
23386                     //    tbh += this.toolbars[i].footer.el.getHeight();
23387                     //}
23388                 }
23389               
23390                 
23391                 
23392                 
23393                 
23394                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23395                 ah -= 5; // knock a few pixes off for look..
23396                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23397                 var eh = ah;
23398             }
23399         }
23400         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23401         this.editorcore.onResize(ew,eh);
23402         
23403     },
23404
23405     /**
23406      * Toggles the editor between standard and source edit mode.
23407      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23408      */
23409     toggleSourceEdit : function(sourceEditMode)
23410     {
23411         this.editorcore.toggleSourceEdit(sourceEditMode);
23412         
23413         if(this.editorcore.sourceEditMode){
23414             Roo.log('editor - showing textarea');
23415             
23416 //            Roo.log('in');
23417 //            Roo.log(this.syncValue());
23418             this.syncValue();
23419             this.inputEl().removeClass(['hide', 'x-hidden']);
23420             this.inputEl().dom.removeAttribute('tabIndex');
23421             this.inputEl().focus();
23422         }else{
23423             Roo.log('editor - hiding textarea');
23424 //            Roo.log('out')
23425 //            Roo.log(this.pushValue()); 
23426             this.pushValue();
23427             
23428             this.inputEl().addClass(['hide', 'x-hidden']);
23429             this.inputEl().dom.setAttribute('tabIndex', -1);
23430             //this.deferFocus();
23431         }
23432          
23433         if(this.resizable){
23434             this.setSize(this.wrap.getSize());
23435         }
23436         
23437         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23438     },
23439  
23440     // private (for BoxComponent)
23441     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23442
23443     // private (for BoxComponent)
23444     getResizeEl : function(){
23445         return this.wrap;
23446     },
23447
23448     // private (for BoxComponent)
23449     getPositionEl : function(){
23450         return this.wrap;
23451     },
23452
23453     // private
23454     initEvents : function(){
23455         this.originalValue = this.getValue();
23456     },
23457
23458 //    /**
23459 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23460 //     * @method
23461 //     */
23462 //    markInvalid : Roo.emptyFn,
23463 //    /**
23464 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23465 //     * @method
23466 //     */
23467 //    clearInvalid : Roo.emptyFn,
23468
23469     setValue : function(v){
23470         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23471         this.editorcore.pushValue();
23472     },
23473
23474      
23475     // private
23476     deferFocus : function(){
23477         this.focus.defer(10, this);
23478     },
23479
23480     // doc'ed in Field
23481     focus : function(){
23482         this.editorcore.focus();
23483         
23484     },
23485       
23486
23487     // private
23488     onDestroy : function(){
23489         
23490         
23491         
23492         if(this.rendered){
23493             
23494             for (var i =0; i < this.toolbars.length;i++) {
23495                 // fixme - ask toolbars for heights?
23496                 this.toolbars[i].onDestroy();
23497             }
23498             
23499             this.wrap.dom.innerHTML = '';
23500             this.wrap.remove();
23501         }
23502     },
23503
23504     // private
23505     onFirstFocus : function(){
23506         //Roo.log("onFirstFocus");
23507         this.editorcore.onFirstFocus();
23508          for (var i =0; i < this.toolbars.length;i++) {
23509             this.toolbars[i].onFirstFocus();
23510         }
23511         
23512     },
23513     
23514     // private
23515     syncValue : function()
23516     {   
23517         this.editorcore.syncValue();
23518     },
23519     
23520     pushValue : function()
23521     {   
23522         this.editorcore.pushValue();
23523     }
23524      
23525     
23526     // hide stuff that is not compatible
23527     /**
23528      * @event blur
23529      * @hide
23530      */
23531     /**
23532      * @event change
23533      * @hide
23534      */
23535     /**
23536      * @event focus
23537      * @hide
23538      */
23539     /**
23540      * @event specialkey
23541      * @hide
23542      */
23543     /**
23544      * @cfg {String} fieldClass @hide
23545      */
23546     /**
23547      * @cfg {String} focusClass @hide
23548      */
23549     /**
23550      * @cfg {String} autoCreate @hide
23551      */
23552     /**
23553      * @cfg {String} inputType @hide
23554      */
23555     /**
23556      * @cfg {String} invalidClass @hide
23557      */
23558     /**
23559      * @cfg {String} invalidText @hide
23560      */
23561     /**
23562      * @cfg {String} msgFx @hide
23563      */
23564     /**
23565      * @cfg {String} validateOnBlur @hide
23566      */
23567 });
23568  
23569     
23570    
23571    
23572    
23573       
23574 Roo.namespace('Roo.bootstrap.htmleditor');
23575 /**
23576  * @class Roo.bootstrap.HtmlEditorToolbar1
23577  * Basic Toolbar
23578  * 
23579  * Usage:
23580  *
23581  new Roo.bootstrap.HtmlEditor({
23582     ....
23583     toolbars : [
23584         new Roo.bootstrap.HtmlEditorToolbar1({
23585             disable : { fonts: 1 , format: 1, ..., ... , ...],
23586             btns : [ .... ]
23587         })
23588     }
23589      
23590  * 
23591  * @cfg {Object} disable List of elements to disable..
23592  * @cfg {Array} btns List of additional buttons.
23593  * 
23594  * 
23595  * NEEDS Extra CSS? 
23596  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23597  */
23598  
23599 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23600 {
23601     
23602     Roo.apply(this, config);
23603     
23604     // default disabled, based on 'good practice'..
23605     this.disable = this.disable || {};
23606     Roo.applyIf(this.disable, {
23607         fontSize : true,
23608         colors : true,
23609         specialElements : true
23610     });
23611     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23612     
23613     this.editor = config.editor;
23614     this.editorcore = config.editor.editorcore;
23615     
23616     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23617     
23618     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23619     // dont call parent... till later.
23620 }
23621 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23622      
23623     bar : true,
23624     
23625     editor : false,
23626     editorcore : false,
23627     
23628     
23629     formats : [
23630         "p" ,  
23631         "h1","h2","h3","h4","h5","h6", 
23632         "pre", "code", 
23633         "abbr", "acronym", "address", "cite", "samp", "var",
23634         'div','span'
23635     ],
23636     
23637     onRender : function(ct, position)
23638     {
23639        // Roo.log("Call onRender: " + this.xtype);
23640         
23641        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23642        Roo.log(this.el);
23643        this.el.dom.style.marginBottom = '0';
23644        var _this = this;
23645        var editorcore = this.editorcore;
23646        var editor= this.editor;
23647        
23648        var children = [];
23649        var btn = function(id,cmd , toggle, handler, html){
23650        
23651             var  event = toggle ? 'toggle' : 'click';
23652        
23653             var a = {
23654                 size : 'sm',
23655                 xtype: 'Button',
23656                 xns: Roo.bootstrap,
23657                 glyphicon : id,
23658                 cmd : id || cmd,
23659                 enableToggle:toggle !== false,
23660                 html : html || '',
23661                 pressed : toggle ? false : null,
23662                 listeners : {}
23663             };
23664             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23665                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23666             };
23667             children.push(a);
23668             return a;
23669        }
23670        
23671     //    var cb_box = function...
23672         
23673         var style = {
23674                 xtype: 'Button',
23675                 size : 'sm',
23676                 xns: Roo.bootstrap,
23677                 glyphicon : 'font',
23678                 //html : 'submit'
23679                 menu : {
23680                     xtype: 'Menu',
23681                     xns: Roo.bootstrap,
23682                     items:  []
23683                 }
23684         };
23685         Roo.each(this.formats, function(f) {
23686             style.menu.items.push({
23687                 xtype :'MenuItem',
23688                 xns: Roo.bootstrap,
23689                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23690                 tagname : f,
23691                 listeners : {
23692                     click : function()
23693                     {
23694                         editorcore.insertTag(this.tagname);
23695                         editor.focus();
23696                     }
23697                 }
23698                 
23699             });
23700         });
23701         children.push(style);   
23702         
23703         btn('bold',false,true);
23704         btn('italic',false,true);
23705         btn('align-left', 'justifyleft',true);
23706         btn('align-center', 'justifycenter',true);
23707         btn('align-right' , 'justifyright',true);
23708         btn('link', false, false, function(btn) {
23709             //Roo.log("create link?");
23710             var url = prompt(this.createLinkText, this.defaultLinkValue);
23711             if(url && url != 'http:/'+'/'){
23712                 this.editorcore.relayCmd('createlink', url);
23713             }
23714         }),
23715         btn('list','insertunorderedlist',true);
23716         btn('pencil', false,true, function(btn){
23717                 Roo.log(this);
23718                 this.toggleSourceEdit(btn.pressed);
23719         });
23720         
23721         if (this.editor.btns.length > 0) {
23722             for (var i = 0; i<this.editor.btns.length; i++) {
23723                 children.push(this.editor.btns[i]);
23724             }
23725         }
23726         
23727         /*
23728         var cog = {
23729                 xtype: 'Button',
23730                 size : 'sm',
23731                 xns: Roo.bootstrap,
23732                 glyphicon : 'cog',
23733                 //html : 'submit'
23734                 menu : {
23735                     xtype: 'Menu',
23736                     xns: Roo.bootstrap,
23737                     items:  []
23738                 }
23739         };
23740         
23741         cog.menu.items.push({
23742             xtype :'MenuItem',
23743             xns: Roo.bootstrap,
23744             html : Clean styles,
23745             tagname : f,
23746             listeners : {
23747                 click : function()
23748                 {
23749                     editorcore.insertTag(this.tagname);
23750                     editor.focus();
23751                 }
23752             }
23753             
23754         });
23755        */
23756         
23757          
23758        this.xtype = 'NavSimplebar';
23759         
23760         for(var i=0;i< children.length;i++) {
23761             
23762             this.buttons.add(this.addxtypeChild(children[i]));
23763             
23764         }
23765         
23766         editor.on('editorevent', this.updateToolbar, this);
23767     },
23768     onBtnClick : function(id)
23769     {
23770        this.editorcore.relayCmd(id);
23771        this.editorcore.focus();
23772     },
23773     
23774     /**
23775      * Protected method that will not generally be called directly. It triggers
23776      * a toolbar update by reading the markup state of the current selection in the editor.
23777      */
23778     updateToolbar: function(){
23779
23780         if(!this.editorcore.activated){
23781             this.editor.onFirstFocus(); // is this neeed?
23782             return;
23783         }
23784
23785         var btns = this.buttons; 
23786         var doc = this.editorcore.doc;
23787         btns.get('bold').setActive(doc.queryCommandState('bold'));
23788         btns.get('italic').setActive(doc.queryCommandState('italic'));
23789         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23790         
23791         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23792         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23793         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23794         
23795         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23796         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23797          /*
23798         
23799         var ans = this.editorcore.getAllAncestors();
23800         if (this.formatCombo) {
23801             
23802             
23803             var store = this.formatCombo.store;
23804             this.formatCombo.setValue("");
23805             for (var i =0; i < ans.length;i++) {
23806                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23807                     // select it..
23808                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23809                     break;
23810                 }
23811             }
23812         }
23813         
23814         
23815         
23816         // hides menus... - so this cant be on a menu...
23817         Roo.bootstrap.MenuMgr.hideAll();
23818         */
23819         Roo.bootstrap.MenuMgr.hideAll();
23820         //this.editorsyncValue();
23821     },
23822     onFirstFocus: function() {
23823         this.buttons.each(function(item){
23824            item.enable();
23825         });
23826     },
23827     toggleSourceEdit : function(sourceEditMode){
23828         
23829           
23830         if(sourceEditMode){
23831             Roo.log("disabling buttons");
23832            this.buttons.each( function(item){
23833                 if(item.cmd != 'pencil'){
23834                     item.disable();
23835                 }
23836             });
23837           
23838         }else{
23839             Roo.log("enabling buttons");
23840             if(this.editorcore.initialized){
23841                 this.buttons.each( function(item){
23842                     item.enable();
23843                 });
23844             }
23845             
23846         }
23847         Roo.log("calling toggole on editor");
23848         // tell the editor that it's been pressed..
23849         this.editor.toggleSourceEdit(sourceEditMode);
23850        
23851     }
23852 });
23853
23854
23855
23856
23857
23858 /**
23859  * @class Roo.bootstrap.Table.AbstractSelectionModel
23860  * @extends Roo.util.Observable
23861  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23862  * implemented by descendant classes.  This class should not be directly instantiated.
23863  * @constructor
23864  */
23865 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23866     this.locked = false;
23867     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23868 };
23869
23870
23871 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23872     /** @ignore Called by the grid automatically. Do not call directly. */
23873     init : function(grid){
23874         this.grid = grid;
23875         this.initEvents();
23876     },
23877
23878     /**
23879      * Locks the selections.
23880      */
23881     lock : function(){
23882         this.locked = true;
23883     },
23884
23885     /**
23886      * Unlocks the selections.
23887      */
23888     unlock : function(){
23889         this.locked = false;
23890     },
23891
23892     /**
23893      * Returns true if the selections are locked.
23894      * @return {Boolean}
23895      */
23896     isLocked : function(){
23897         return this.locked;
23898     }
23899 });
23900 /**
23901  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23902  * @class Roo.bootstrap.Table.RowSelectionModel
23903  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23904  * It supports multiple selections and keyboard selection/navigation. 
23905  * @constructor
23906  * @param {Object} config
23907  */
23908
23909 Roo.bootstrap.Table.RowSelectionModel = function(config){
23910     Roo.apply(this, config);
23911     this.selections = new Roo.util.MixedCollection(false, function(o){
23912         return o.id;
23913     });
23914
23915     this.last = false;
23916     this.lastActive = false;
23917
23918     this.addEvents({
23919         /**
23920              * @event selectionchange
23921              * Fires when the selection changes
23922              * @param {SelectionModel} this
23923              */
23924             "selectionchange" : true,
23925         /**
23926              * @event afterselectionchange
23927              * Fires after the selection changes (eg. by key press or clicking)
23928              * @param {SelectionModel} this
23929              */
23930             "afterselectionchange" : true,
23931         /**
23932              * @event beforerowselect
23933              * Fires when a row is selected being selected, return false to cancel.
23934              * @param {SelectionModel} this
23935              * @param {Number} rowIndex The selected index
23936              * @param {Boolean} keepExisting False if other selections will be cleared
23937              */
23938             "beforerowselect" : true,
23939         /**
23940              * @event rowselect
23941              * Fires when a row is selected.
23942              * @param {SelectionModel} this
23943              * @param {Number} rowIndex The selected index
23944              * @param {Roo.data.Record} r The record
23945              */
23946             "rowselect" : true,
23947         /**
23948              * @event rowdeselect
23949              * Fires when a row is deselected.
23950              * @param {SelectionModel} this
23951              * @param {Number} rowIndex The selected index
23952              */
23953         "rowdeselect" : true
23954     });
23955     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23956     this.locked = false;
23957  };
23958
23959 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23960     /**
23961      * @cfg {Boolean} singleSelect
23962      * True to allow selection of only one row at a time (defaults to false)
23963      */
23964     singleSelect : false,
23965
23966     // private
23967     initEvents : function()
23968     {
23969
23970         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23971         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23972         //}else{ // allow click to work like normal
23973          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23974         //}
23975         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23976         this.grid.on("rowclick", this.handleMouseDown, this);
23977         
23978         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23979             "up" : function(e){
23980                 if(!e.shiftKey){
23981                     this.selectPrevious(e.shiftKey);
23982                 }else if(this.last !== false && this.lastActive !== false){
23983                     var last = this.last;
23984                     this.selectRange(this.last,  this.lastActive-1);
23985                     this.grid.getView().focusRow(this.lastActive);
23986                     if(last !== false){
23987                         this.last = last;
23988                     }
23989                 }else{
23990                     this.selectFirstRow();
23991                 }
23992                 this.fireEvent("afterselectionchange", this);
23993             },
23994             "down" : function(e){
23995                 if(!e.shiftKey){
23996                     this.selectNext(e.shiftKey);
23997                 }else if(this.last !== false && this.lastActive !== false){
23998                     var last = this.last;
23999                     this.selectRange(this.last,  this.lastActive+1);
24000                     this.grid.getView().focusRow(this.lastActive);
24001                     if(last !== false){
24002                         this.last = last;
24003                     }
24004                 }else{
24005                     this.selectFirstRow();
24006                 }
24007                 this.fireEvent("afterselectionchange", this);
24008             },
24009             scope: this
24010         });
24011         this.grid.store.on('load', function(){
24012             this.selections.clear();
24013         },this);
24014         /*
24015         var view = this.grid.view;
24016         view.on("refresh", this.onRefresh, this);
24017         view.on("rowupdated", this.onRowUpdated, this);
24018         view.on("rowremoved", this.onRemove, this);
24019         */
24020     },
24021
24022     // private
24023     onRefresh : function()
24024     {
24025         var ds = this.grid.store, i, v = this.grid.view;
24026         var s = this.selections;
24027         s.each(function(r){
24028             if((i = ds.indexOfId(r.id)) != -1){
24029                 v.onRowSelect(i);
24030             }else{
24031                 s.remove(r);
24032             }
24033         });
24034     },
24035
24036     // private
24037     onRemove : function(v, index, r){
24038         this.selections.remove(r);
24039     },
24040
24041     // private
24042     onRowUpdated : function(v, index, r){
24043         if(this.isSelected(r)){
24044             v.onRowSelect(index);
24045         }
24046     },
24047
24048     /**
24049      * Select records.
24050      * @param {Array} records The records to select
24051      * @param {Boolean} keepExisting (optional) True to keep existing selections
24052      */
24053     selectRecords : function(records, keepExisting)
24054     {
24055         if(!keepExisting){
24056             this.clearSelections();
24057         }
24058             var ds = this.grid.store;
24059         for(var i = 0, len = records.length; i < len; i++){
24060             this.selectRow(ds.indexOf(records[i]), true);
24061         }
24062     },
24063
24064     /**
24065      * Gets the number of selected rows.
24066      * @return {Number}
24067      */
24068     getCount : function(){
24069         return this.selections.length;
24070     },
24071
24072     /**
24073      * Selects the first row in the grid.
24074      */
24075     selectFirstRow : function(){
24076         this.selectRow(0);
24077     },
24078
24079     /**
24080      * Select the last row.
24081      * @param {Boolean} keepExisting (optional) True to keep existing selections
24082      */
24083     selectLastRow : function(keepExisting){
24084         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24085         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24086     },
24087
24088     /**
24089      * Selects the row immediately following the last selected row.
24090      * @param {Boolean} keepExisting (optional) True to keep existing selections
24091      */
24092     selectNext : function(keepExisting)
24093     {
24094             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24095             this.selectRow(this.last+1, keepExisting);
24096             this.grid.getView().focusRow(this.last);
24097         }
24098     },
24099
24100     /**
24101      * Selects the row that precedes the last selected row.
24102      * @param {Boolean} keepExisting (optional) True to keep existing selections
24103      */
24104     selectPrevious : function(keepExisting){
24105         if(this.last){
24106             this.selectRow(this.last-1, keepExisting);
24107             this.grid.getView().focusRow(this.last);
24108         }
24109     },
24110
24111     /**
24112      * Returns the selected records
24113      * @return {Array} Array of selected records
24114      */
24115     getSelections : function(){
24116         return [].concat(this.selections.items);
24117     },
24118
24119     /**
24120      * Returns the first selected record.
24121      * @return {Record}
24122      */
24123     getSelected : function(){
24124         return this.selections.itemAt(0);
24125     },
24126
24127
24128     /**
24129      * Clears all selections.
24130      */
24131     clearSelections : function(fast)
24132     {
24133         if(this.locked) {
24134             return;
24135         }
24136         if(fast !== true){
24137                 var ds = this.grid.store;
24138             var s = this.selections;
24139             s.each(function(r){
24140                 this.deselectRow(ds.indexOfId(r.id));
24141             }, this);
24142             s.clear();
24143         }else{
24144             this.selections.clear();
24145         }
24146         this.last = false;
24147     },
24148
24149
24150     /**
24151      * Selects all rows.
24152      */
24153     selectAll : function(){
24154         if(this.locked) {
24155             return;
24156         }
24157         this.selections.clear();
24158         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24159             this.selectRow(i, true);
24160         }
24161     },
24162
24163     /**
24164      * Returns True if there is a selection.
24165      * @return {Boolean}
24166      */
24167     hasSelection : function(){
24168         return this.selections.length > 0;
24169     },
24170
24171     /**
24172      * Returns True if the specified row is selected.
24173      * @param {Number/Record} record The record or index of the record to check
24174      * @return {Boolean}
24175      */
24176     isSelected : function(index){
24177             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24178         return (r && this.selections.key(r.id) ? true : false);
24179     },
24180
24181     /**
24182      * Returns True if the specified record id is selected.
24183      * @param {String} id The id of record to check
24184      * @return {Boolean}
24185      */
24186     isIdSelected : function(id){
24187         return (this.selections.key(id) ? true : false);
24188     },
24189
24190
24191     // private
24192     handleMouseDBClick : function(e, t){
24193         
24194     },
24195     // private
24196     handleMouseDown : function(e, t)
24197     {
24198             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24199         if(this.isLocked() || rowIndex < 0 ){
24200             return;
24201         };
24202         if(e.shiftKey && this.last !== false){
24203             var last = this.last;
24204             this.selectRange(last, rowIndex, e.ctrlKey);
24205             this.last = last; // reset the last
24206             t.focus();
24207     
24208         }else{
24209             var isSelected = this.isSelected(rowIndex);
24210             //Roo.log("select row:" + rowIndex);
24211             if(isSelected){
24212                 this.deselectRow(rowIndex);
24213             } else {
24214                         this.selectRow(rowIndex, true);
24215             }
24216     
24217             /*
24218                 if(e.button !== 0 && isSelected){
24219                 alert('rowIndex 2: ' + rowIndex);
24220                     view.focusRow(rowIndex);
24221                 }else if(e.ctrlKey && isSelected){
24222                     this.deselectRow(rowIndex);
24223                 }else if(!isSelected){
24224                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24225                     view.focusRow(rowIndex);
24226                 }
24227             */
24228         }
24229         this.fireEvent("afterselectionchange", this);
24230     },
24231     // private
24232     handleDragableRowClick :  function(grid, rowIndex, e) 
24233     {
24234         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24235             this.selectRow(rowIndex, false);
24236             grid.view.focusRow(rowIndex);
24237              this.fireEvent("afterselectionchange", this);
24238         }
24239     },
24240     
24241     /**
24242      * Selects multiple rows.
24243      * @param {Array} rows Array of the indexes of the row to select
24244      * @param {Boolean} keepExisting (optional) True to keep existing selections
24245      */
24246     selectRows : function(rows, keepExisting){
24247         if(!keepExisting){
24248             this.clearSelections();
24249         }
24250         for(var i = 0, len = rows.length; i < len; i++){
24251             this.selectRow(rows[i], true);
24252         }
24253     },
24254
24255     /**
24256      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24257      * @param {Number} startRow The index of the first row in the range
24258      * @param {Number} endRow The index of the last row in the range
24259      * @param {Boolean} keepExisting (optional) True to retain existing selections
24260      */
24261     selectRange : function(startRow, endRow, keepExisting){
24262         if(this.locked) {
24263             return;
24264         }
24265         if(!keepExisting){
24266             this.clearSelections();
24267         }
24268         if(startRow <= endRow){
24269             for(var i = startRow; i <= endRow; i++){
24270                 this.selectRow(i, true);
24271             }
24272         }else{
24273             for(var i = startRow; i >= endRow; i--){
24274                 this.selectRow(i, true);
24275             }
24276         }
24277     },
24278
24279     /**
24280      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24281      * @param {Number} startRow The index of the first row in the range
24282      * @param {Number} endRow The index of the last row in the range
24283      */
24284     deselectRange : function(startRow, endRow, preventViewNotify){
24285         if(this.locked) {
24286             return;
24287         }
24288         for(var i = startRow; i <= endRow; i++){
24289             this.deselectRow(i, preventViewNotify);
24290         }
24291     },
24292
24293     /**
24294      * Selects a row.
24295      * @param {Number} row The index of the row to select
24296      * @param {Boolean} keepExisting (optional) True to keep existing selections
24297      */
24298     selectRow : function(index, keepExisting, preventViewNotify)
24299     {
24300             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24301             return;
24302         }
24303         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24304             if(!keepExisting || this.singleSelect){
24305                 this.clearSelections();
24306             }
24307             
24308             var r = this.grid.store.getAt(index);
24309             //console.log('selectRow - record id :' + r.id);
24310             
24311             this.selections.add(r);
24312             this.last = this.lastActive = index;
24313             if(!preventViewNotify){
24314                 var proxy = new Roo.Element(
24315                                 this.grid.getRowDom(index)
24316                 );
24317                 proxy.addClass('bg-info info');
24318             }
24319             this.fireEvent("rowselect", this, index, r);
24320             this.fireEvent("selectionchange", this);
24321         }
24322     },
24323
24324     /**
24325      * Deselects a row.
24326      * @param {Number} row The index of the row to deselect
24327      */
24328     deselectRow : function(index, preventViewNotify)
24329     {
24330         if(this.locked) {
24331             return;
24332         }
24333         if(this.last == index){
24334             this.last = false;
24335         }
24336         if(this.lastActive == index){
24337             this.lastActive = false;
24338         }
24339         
24340         var r = this.grid.store.getAt(index);
24341         if (!r) {
24342             return;
24343         }
24344         
24345         this.selections.remove(r);
24346         //.console.log('deselectRow - record id :' + r.id);
24347         if(!preventViewNotify){
24348         
24349             var proxy = new Roo.Element(
24350                 this.grid.getRowDom(index)
24351             );
24352             proxy.removeClass('bg-info info');
24353         }
24354         this.fireEvent("rowdeselect", this, index);
24355         this.fireEvent("selectionchange", this);
24356     },
24357
24358     // private
24359     restoreLast : function(){
24360         if(this._last){
24361             this.last = this._last;
24362         }
24363     },
24364
24365     // private
24366     acceptsNav : function(row, col, cm){
24367         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24368     },
24369
24370     // private
24371     onEditorKey : function(field, e){
24372         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24373         if(k == e.TAB){
24374             e.stopEvent();
24375             ed.completeEdit();
24376             if(e.shiftKey){
24377                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24378             }else{
24379                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24380             }
24381         }else if(k == e.ENTER && !e.ctrlKey){
24382             e.stopEvent();
24383             ed.completeEdit();
24384             if(e.shiftKey){
24385                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24386             }else{
24387                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24388             }
24389         }else if(k == e.ESC){
24390             ed.cancelEdit();
24391         }
24392         if(newCell){
24393             g.startEditing(newCell[0], newCell[1]);
24394         }
24395     }
24396 });
24397 /*
24398  * Based on:
24399  * Ext JS Library 1.1.1
24400  * Copyright(c) 2006-2007, Ext JS, LLC.
24401  *
24402  * Originally Released Under LGPL - original licence link has changed is not relivant.
24403  *
24404  * Fork - LGPL
24405  * <script type="text/javascript">
24406  */
24407  
24408 /**
24409  * @class Roo.bootstrap.PagingToolbar
24410  * @extends Roo.bootstrap.NavSimplebar
24411  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24412  * @constructor
24413  * Create a new PagingToolbar
24414  * @param {Object} config The config object
24415  * @param {Roo.data.Store} store
24416  */
24417 Roo.bootstrap.PagingToolbar = function(config)
24418 {
24419     // old args format still supported... - xtype is prefered..
24420         // created from xtype...
24421     
24422     this.ds = config.dataSource;
24423     
24424     if (config.store && !this.ds) {
24425         this.store= Roo.factory(config.store, Roo.data);
24426         this.ds = this.store;
24427         this.ds.xmodule = this.xmodule || false;
24428     }
24429     
24430     this.toolbarItems = [];
24431     if (config.items) {
24432         this.toolbarItems = config.items;
24433     }
24434     
24435     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24436     
24437     this.cursor = 0;
24438     
24439     if (this.ds) { 
24440         this.bind(this.ds);
24441     }
24442     
24443     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24444     
24445 };
24446
24447 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24448     /**
24449      * @cfg {Roo.data.Store} dataSource
24450      * The underlying data store providing the paged data
24451      */
24452     /**
24453      * @cfg {String/HTMLElement/Element} container
24454      * container The id or element that will contain the toolbar
24455      */
24456     /**
24457      * @cfg {Boolean} displayInfo
24458      * True to display the displayMsg (defaults to false)
24459      */
24460     /**
24461      * @cfg {Number} pageSize
24462      * The number of records to display per page (defaults to 20)
24463      */
24464     pageSize: 20,
24465     /**
24466      * @cfg {String} displayMsg
24467      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24468      */
24469     displayMsg : 'Displaying {0} - {1} of {2}',
24470     /**
24471      * @cfg {String} emptyMsg
24472      * The message to display when no records are found (defaults to "No data to display")
24473      */
24474     emptyMsg : 'No data to display',
24475     /**
24476      * Customizable piece of the default paging text (defaults to "Page")
24477      * @type String
24478      */
24479     beforePageText : "Page",
24480     /**
24481      * Customizable piece of the default paging text (defaults to "of %0")
24482      * @type String
24483      */
24484     afterPageText : "of {0}",
24485     /**
24486      * Customizable piece of the default paging text (defaults to "First Page")
24487      * @type String
24488      */
24489     firstText : "First Page",
24490     /**
24491      * Customizable piece of the default paging text (defaults to "Previous Page")
24492      * @type String
24493      */
24494     prevText : "Previous Page",
24495     /**
24496      * Customizable piece of the default paging text (defaults to "Next Page")
24497      * @type String
24498      */
24499     nextText : "Next Page",
24500     /**
24501      * Customizable piece of the default paging text (defaults to "Last Page")
24502      * @type String
24503      */
24504     lastText : "Last Page",
24505     /**
24506      * Customizable piece of the default paging text (defaults to "Refresh")
24507      * @type String
24508      */
24509     refreshText : "Refresh",
24510
24511     buttons : false,
24512     // private
24513     onRender : function(ct, position) 
24514     {
24515         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24516         this.navgroup.parentId = this.id;
24517         this.navgroup.onRender(this.el, null);
24518         // add the buttons to the navgroup
24519         
24520         if(this.displayInfo){
24521             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24522             this.displayEl = this.el.select('.x-paging-info', true).first();
24523 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24524 //            this.displayEl = navel.el.select('span',true).first();
24525         }
24526         
24527         var _this = this;
24528         
24529         if(this.buttons){
24530             Roo.each(_this.buttons, function(e){ // this might need to use render????
24531                Roo.factory(e).onRender(_this.el, null);
24532             });
24533         }
24534             
24535         Roo.each(_this.toolbarItems, function(e) {
24536             _this.navgroup.addItem(e);
24537         });
24538         
24539         
24540         this.first = this.navgroup.addItem({
24541             tooltip: this.firstText,
24542             cls: "prev",
24543             icon : 'fa fa-backward',
24544             disabled: true,
24545             preventDefault: true,
24546             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24547         });
24548         
24549         this.prev =  this.navgroup.addItem({
24550             tooltip: this.prevText,
24551             cls: "prev",
24552             icon : 'fa fa-step-backward',
24553             disabled: true,
24554             preventDefault: true,
24555             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24556         });
24557     //this.addSeparator();
24558         
24559         
24560         var field = this.navgroup.addItem( {
24561             tagtype : 'span',
24562             cls : 'x-paging-position',
24563             
24564             html : this.beforePageText  +
24565                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24566                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24567          } ); //?? escaped?
24568         
24569         this.field = field.el.select('input', true).first();
24570         this.field.on("keydown", this.onPagingKeydown, this);
24571         this.field.on("focus", function(){this.dom.select();});
24572     
24573     
24574         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24575         //this.field.setHeight(18);
24576         //this.addSeparator();
24577         this.next = this.navgroup.addItem({
24578             tooltip: this.nextText,
24579             cls: "next",
24580             html : ' <i class="fa fa-step-forward">',
24581             disabled: true,
24582             preventDefault: true,
24583             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24584         });
24585         this.last = this.navgroup.addItem({
24586             tooltip: this.lastText,
24587             icon : 'fa fa-forward',
24588             cls: "next",
24589             disabled: true,
24590             preventDefault: true,
24591             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24592         });
24593     //this.addSeparator();
24594         this.loading = this.navgroup.addItem({
24595             tooltip: this.refreshText,
24596             icon: 'fa fa-refresh',
24597             preventDefault: true,
24598             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24599         });
24600         
24601     },
24602
24603     // private
24604     updateInfo : function(){
24605         if(this.displayEl){
24606             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24607             var msg = count == 0 ?
24608                 this.emptyMsg :
24609                 String.format(
24610                     this.displayMsg,
24611                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24612                 );
24613             this.displayEl.update(msg);
24614         }
24615     },
24616
24617     // private
24618     onLoad : function(ds, r, o)
24619     {
24620         this.cursor = o.params.start ? o.params.start : 0;
24621         
24622         var d = this.getPageData(),
24623             ap = d.activePage,
24624             ps = d.pages;
24625         
24626         
24627         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24628         this.field.dom.value = ap;
24629         this.first.setDisabled(ap == 1);
24630         this.prev.setDisabled(ap == 1);
24631         this.next.setDisabled(ap == ps);
24632         this.last.setDisabled(ap == ps);
24633         this.loading.enable();
24634         this.updateInfo();
24635     },
24636
24637     // private
24638     getPageData : function(){
24639         var total = this.ds.getTotalCount();
24640         return {
24641             total : total,
24642             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24643             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24644         };
24645     },
24646
24647     // private
24648     onLoadError : function(){
24649         this.loading.enable();
24650     },
24651
24652     // private
24653     onPagingKeydown : function(e){
24654         var k = e.getKey();
24655         var d = this.getPageData();
24656         if(k == e.RETURN){
24657             var v = this.field.dom.value, pageNum;
24658             if(!v || isNaN(pageNum = parseInt(v, 10))){
24659                 this.field.dom.value = d.activePage;
24660                 return;
24661             }
24662             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24663             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24664             e.stopEvent();
24665         }
24666         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))
24667         {
24668           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24669           this.field.dom.value = pageNum;
24670           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24671           e.stopEvent();
24672         }
24673         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24674         {
24675           var v = this.field.dom.value, pageNum; 
24676           var increment = (e.shiftKey) ? 10 : 1;
24677           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24678                 increment *= -1;
24679           }
24680           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24681             this.field.dom.value = d.activePage;
24682             return;
24683           }
24684           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24685           {
24686             this.field.dom.value = parseInt(v, 10) + increment;
24687             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24688             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24689           }
24690           e.stopEvent();
24691         }
24692     },
24693
24694     // private
24695     beforeLoad : function(){
24696         if(this.loading){
24697             this.loading.disable();
24698         }
24699     },
24700
24701     // private
24702     onClick : function(which){
24703         
24704         var ds = this.ds;
24705         if (!ds) {
24706             return;
24707         }
24708         
24709         switch(which){
24710             case "first":
24711                 ds.load({params:{start: 0, limit: this.pageSize}});
24712             break;
24713             case "prev":
24714                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24715             break;
24716             case "next":
24717                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24718             break;
24719             case "last":
24720                 var total = ds.getTotalCount();
24721                 var extra = total % this.pageSize;
24722                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24723                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24724             break;
24725             case "refresh":
24726                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24727             break;
24728         }
24729     },
24730
24731     /**
24732      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24733      * @param {Roo.data.Store} store The data store to unbind
24734      */
24735     unbind : function(ds){
24736         ds.un("beforeload", this.beforeLoad, this);
24737         ds.un("load", this.onLoad, this);
24738         ds.un("loadexception", this.onLoadError, this);
24739         ds.un("remove", this.updateInfo, this);
24740         ds.un("add", this.updateInfo, this);
24741         this.ds = undefined;
24742     },
24743
24744     /**
24745      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24746      * @param {Roo.data.Store} store The data store to bind
24747      */
24748     bind : function(ds){
24749         ds.on("beforeload", this.beforeLoad, this);
24750         ds.on("load", this.onLoad, this);
24751         ds.on("loadexception", this.onLoadError, this);
24752         ds.on("remove", this.updateInfo, this);
24753         ds.on("add", this.updateInfo, this);
24754         this.ds = ds;
24755     }
24756 });/*
24757  * - LGPL
24758  *
24759  * element
24760  * 
24761  */
24762
24763 /**
24764  * @class Roo.bootstrap.MessageBar
24765  * @extends Roo.bootstrap.Component
24766  * Bootstrap MessageBar class
24767  * @cfg {String} html contents of the MessageBar
24768  * @cfg {String} weight (info | success | warning | danger) default info
24769  * @cfg {String} beforeClass insert the bar before the given class
24770  * @cfg {Boolean} closable (true | false) default false
24771  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24772  * 
24773  * @constructor
24774  * Create a new Element
24775  * @param {Object} config The config object
24776  */
24777
24778 Roo.bootstrap.MessageBar = function(config){
24779     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24780 };
24781
24782 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24783     
24784     html: '',
24785     weight: 'info',
24786     closable: false,
24787     fixed: false,
24788     beforeClass: 'bootstrap-sticky-wrap',
24789     
24790     getAutoCreate : function(){
24791         
24792         var cfg = {
24793             tag: 'div',
24794             cls: 'alert alert-dismissable alert-' + this.weight,
24795             cn: [
24796                 {
24797                     tag: 'span',
24798                     cls: 'message',
24799                     html: this.html || ''
24800                 }
24801             ]
24802         };
24803         
24804         if(this.fixed){
24805             cfg.cls += ' alert-messages-fixed';
24806         }
24807         
24808         if(this.closable){
24809             cfg.cn.push({
24810                 tag: 'button',
24811                 cls: 'close',
24812                 html: 'x'
24813             });
24814         }
24815         
24816         return cfg;
24817     },
24818     
24819     onRender : function(ct, position)
24820     {
24821         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24822         
24823         if(!this.el){
24824             var cfg = Roo.apply({},  this.getAutoCreate());
24825             cfg.id = Roo.id();
24826             
24827             if (this.cls) {
24828                 cfg.cls += ' ' + this.cls;
24829             }
24830             if (this.style) {
24831                 cfg.style = this.style;
24832             }
24833             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24834             
24835             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24836         }
24837         
24838         this.el.select('>button.close').on('click', this.hide, this);
24839         
24840     },
24841     
24842     show : function()
24843     {
24844         if (!this.rendered) {
24845             this.render();
24846         }
24847         
24848         this.el.show();
24849         
24850         this.fireEvent('show', this);
24851         
24852     },
24853     
24854     hide : function()
24855     {
24856         if (!this.rendered) {
24857             this.render();
24858         }
24859         
24860         this.el.hide();
24861         
24862         this.fireEvent('hide', this);
24863     },
24864     
24865     update : function()
24866     {
24867 //        var e = this.el.dom.firstChild;
24868 //        
24869 //        if(this.closable){
24870 //            e = e.nextSibling;
24871 //        }
24872 //        
24873 //        e.data = this.html || '';
24874
24875         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24876     }
24877    
24878 });
24879
24880  
24881
24882      /*
24883  * - LGPL
24884  *
24885  * Graph
24886  * 
24887  */
24888
24889
24890 /**
24891  * @class Roo.bootstrap.Graph
24892  * @extends Roo.bootstrap.Component
24893  * Bootstrap Graph class
24894 > Prameters
24895  -sm {number} sm 4
24896  -md {number} md 5
24897  @cfg {String} graphtype  bar | vbar | pie
24898  @cfg {number} g_x coodinator | centre x (pie)
24899  @cfg {number} g_y coodinator | centre y (pie)
24900  @cfg {number} g_r radius (pie)
24901  @cfg {number} g_height height of the chart (respected by all elements in the set)
24902  @cfg {number} g_width width of the chart (respected by all elements in the set)
24903  @cfg {Object} title The title of the chart
24904     
24905  -{Array}  values
24906  -opts (object) options for the chart 
24907      o {
24908      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24909      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24910      o vgutter (number)
24911      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.
24912      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24913      o to
24914      o stretch (boolean)
24915      o }
24916  -opts (object) options for the pie
24917      o{
24918      o cut
24919      o startAngle (number)
24920      o endAngle (number)
24921      } 
24922  *
24923  * @constructor
24924  * Create a new Input
24925  * @param {Object} config The config object
24926  */
24927
24928 Roo.bootstrap.Graph = function(config){
24929     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24930     
24931     this.addEvents({
24932         // img events
24933         /**
24934          * @event click
24935          * The img click event for the img.
24936          * @param {Roo.EventObject} e
24937          */
24938         "click" : true
24939     });
24940 };
24941
24942 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24943     
24944     sm: 4,
24945     md: 5,
24946     graphtype: 'bar',
24947     g_height: 250,
24948     g_width: 400,
24949     g_x: 50,
24950     g_y: 50,
24951     g_r: 30,
24952     opts:{
24953         //g_colors: this.colors,
24954         g_type: 'soft',
24955         g_gutter: '20%'
24956
24957     },
24958     title : false,
24959
24960     getAutoCreate : function(){
24961         
24962         var cfg = {
24963             tag: 'div',
24964             html : null
24965         };
24966         
24967         
24968         return  cfg;
24969     },
24970
24971     onRender : function(ct,position){
24972         
24973         
24974         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24975         
24976         if (typeof(Raphael) == 'undefined') {
24977             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24978             return;
24979         }
24980         
24981         this.raphael = Raphael(this.el.dom);
24982         
24983                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24984                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24985                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24986                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24987                 /*
24988                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24989                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24990                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24991                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24992                 
24993                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24994                 r.barchart(330, 10, 300, 220, data1);
24995                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24996                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24997                 */
24998                 
24999                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25000                 // r.barchart(30, 30, 560, 250,  xdata, {
25001                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25002                 //     axis : "0 0 1 1",
25003                 //     axisxlabels :  xdata
25004                 //     //yvalues : cols,
25005                    
25006                 // });
25007 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25008 //        
25009 //        this.load(null,xdata,{
25010 //                axis : "0 0 1 1",
25011 //                axisxlabels :  xdata
25012 //                });
25013
25014     },
25015
25016     load : function(graphtype,xdata,opts)
25017     {
25018         this.raphael.clear();
25019         if(!graphtype) {
25020             graphtype = this.graphtype;
25021         }
25022         if(!opts){
25023             opts = this.opts;
25024         }
25025         var r = this.raphael,
25026             fin = function () {
25027                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25028             },
25029             fout = function () {
25030                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25031             },
25032             pfin = function() {
25033                 this.sector.stop();
25034                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25035
25036                 if (this.label) {
25037                     this.label[0].stop();
25038                     this.label[0].attr({ r: 7.5 });
25039                     this.label[1].attr({ "font-weight": 800 });
25040                 }
25041             },
25042             pfout = function() {
25043                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25044
25045                 if (this.label) {
25046                     this.label[0].animate({ r: 5 }, 500, "bounce");
25047                     this.label[1].attr({ "font-weight": 400 });
25048                 }
25049             };
25050
25051         switch(graphtype){
25052             case 'bar':
25053                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25054                 break;
25055             case 'hbar':
25056                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25057                 break;
25058             case 'pie':
25059 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25060 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25061 //            
25062                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25063                 
25064                 break;
25065
25066         }
25067         
25068         if(this.title){
25069             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25070         }
25071         
25072     },
25073     
25074     setTitle: function(o)
25075     {
25076         this.title = o;
25077     },
25078     
25079     initEvents: function() {
25080         
25081         if(!this.href){
25082             this.el.on('click', this.onClick, this);
25083         }
25084     },
25085     
25086     onClick : function(e)
25087     {
25088         Roo.log('img onclick');
25089         this.fireEvent('click', this, e);
25090     }
25091    
25092 });
25093
25094  
25095 /*
25096  * - LGPL
25097  *
25098  * numberBox
25099  * 
25100  */
25101 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25102
25103 /**
25104  * @class Roo.bootstrap.dash.NumberBox
25105  * @extends Roo.bootstrap.Component
25106  * Bootstrap NumberBox class
25107  * @cfg {String} headline Box headline
25108  * @cfg {String} content Box content
25109  * @cfg {String} icon Box icon
25110  * @cfg {String} footer Footer text
25111  * @cfg {String} fhref Footer href
25112  * 
25113  * @constructor
25114  * Create a new NumberBox
25115  * @param {Object} config The config object
25116  */
25117
25118
25119 Roo.bootstrap.dash.NumberBox = function(config){
25120     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25121     
25122 };
25123
25124 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25125     
25126     headline : '',
25127     content : '',
25128     icon : '',
25129     footer : '',
25130     fhref : '',
25131     ficon : '',
25132     
25133     getAutoCreate : function(){
25134         
25135         var cfg = {
25136             tag : 'div',
25137             cls : 'small-box ',
25138             cn : [
25139                 {
25140                     tag : 'div',
25141                     cls : 'inner',
25142                     cn :[
25143                         {
25144                             tag : 'h3',
25145                             cls : 'roo-headline',
25146                             html : this.headline
25147                         },
25148                         {
25149                             tag : 'p',
25150                             cls : 'roo-content',
25151                             html : this.content
25152                         }
25153                     ]
25154                 }
25155             ]
25156         };
25157         
25158         if(this.icon){
25159             cfg.cn.push({
25160                 tag : 'div',
25161                 cls : 'icon',
25162                 cn :[
25163                     {
25164                         tag : 'i',
25165                         cls : 'ion ' + this.icon
25166                     }
25167                 ]
25168             });
25169         }
25170         
25171         if(this.footer){
25172             var footer = {
25173                 tag : 'a',
25174                 cls : 'small-box-footer',
25175                 href : this.fhref || '#',
25176                 html : this.footer
25177             };
25178             
25179             cfg.cn.push(footer);
25180             
25181         }
25182         
25183         return  cfg;
25184     },
25185
25186     onRender : function(ct,position){
25187         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25188
25189
25190        
25191                 
25192     },
25193
25194     setHeadline: function (value)
25195     {
25196         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25197     },
25198     
25199     setFooter: function (value, href)
25200     {
25201         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25202         
25203         if(href){
25204             this.el.select('a.small-box-footer',true).first().attr('href', href);
25205         }
25206         
25207     },
25208
25209     setContent: function (value)
25210     {
25211         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25212     },
25213
25214     initEvents: function() 
25215     {   
25216         
25217     }
25218     
25219 });
25220
25221  
25222 /*
25223  * - LGPL
25224  *
25225  * TabBox
25226  * 
25227  */
25228 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25229
25230 /**
25231  * @class Roo.bootstrap.dash.TabBox
25232  * @extends Roo.bootstrap.Component
25233  * Bootstrap TabBox class
25234  * @cfg {String} title Title of the TabBox
25235  * @cfg {String} icon Icon of the TabBox
25236  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25237  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25238  * 
25239  * @constructor
25240  * Create a new TabBox
25241  * @param {Object} config The config object
25242  */
25243
25244
25245 Roo.bootstrap.dash.TabBox = function(config){
25246     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25247     this.addEvents({
25248         // raw events
25249         /**
25250          * @event addpane
25251          * When a pane is added
25252          * @param {Roo.bootstrap.dash.TabPane} pane
25253          */
25254         "addpane" : true,
25255         /**
25256          * @event activatepane
25257          * When a pane is activated
25258          * @param {Roo.bootstrap.dash.TabPane} pane
25259          */
25260         "activatepane" : true
25261         
25262          
25263     });
25264     
25265     this.panes = [];
25266 };
25267
25268 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25269
25270     title : '',
25271     icon : false,
25272     showtabs : true,
25273     tabScrollable : false,
25274     
25275     getChildContainer : function()
25276     {
25277         return this.el.select('.tab-content', true).first();
25278     },
25279     
25280     getAutoCreate : function(){
25281         
25282         var header = {
25283             tag: 'li',
25284             cls: 'pull-left header',
25285             html: this.title,
25286             cn : []
25287         };
25288         
25289         if(this.icon){
25290             header.cn.push({
25291                 tag: 'i',
25292                 cls: 'fa ' + this.icon
25293             });
25294         }
25295         
25296         var h = {
25297             tag: 'ul',
25298             cls: 'nav nav-tabs pull-right',
25299             cn: [
25300                 header
25301             ]
25302         };
25303         
25304         if(this.tabScrollable){
25305             h = {
25306                 tag: 'div',
25307                 cls: 'tab-header',
25308                 cn: [
25309                     {
25310                         tag: 'ul',
25311                         cls: 'nav nav-tabs pull-right',
25312                         cn: [
25313                             header
25314                         ]
25315                     }
25316                 ]
25317             };
25318         }
25319         
25320         var cfg = {
25321             tag: 'div',
25322             cls: 'nav-tabs-custom',
25323             cn: [
25324                 h,
25325                 {
25326                     tag: 'div',
25327                     cls: 'tab-content no-padding',
25328                     cn: []
25329                 }
25330             ]
25331         };
25332
25333         return  cfg;
25334     },
25335     initEvents : function()
25336     {
25337         //Roo.log('add add pane handler');
25338         this.on('addpane', this.onAddPane, this);
25339     },
25340      /**
25341      * Updates the box title
25342      * @param {String} html to set the title to.
25343      */
25344     setTitle : function(value)
25345     {
25346         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25347     },
25348     onAddPane : function(pane)
25349     {
25350         this.panes.push(pane);
25351         //Roo.log('addpane');
25352         //Roo.log(pane);
25353         // tabs are rendere left to right..
25354         if(!this.showtabs){
25355             return;
25356         }
25357         
25358         var ctr = this.el.select('.nav-tabs', true).first();
25359          
25360          
25361         var existing = ctr.select('.nav-tab',true);
25362         var qty = existing.getCount();;
25363         
25364         
25365         var tab = ctr.createChild({
25366             tag : 'li',
25367             cls : 'nav-tab' + (qty ? '' : ' active'),
25368             cn : [
25369                 {
25370                     tag : 'a',
25371                     href:'#',
25372                     html : pane.title
25373                 }
25374             ]
25375         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25376         pane.tab = tab;
25377         
25378         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25379         if (!qty) {
25380             pane.el.addClass('active');
25381         }
25382         
25383                 
25384     },
25385     onTabClick : function(ev,un,ob,pane)
25386     {
25387         //Roo.log('tab - prev default');
25388         ev.preventDefault();
25389         
25390         
25391         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25392         pane.tab.addClass('active');
25393         //Roo.log(pane.title);
25394         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25395         // technically we should have a deactivate event.. but maybe add later.
25396         // and it should not de-activate the selected tab...
25397         this.fireEvent('activatepane', pane);
25398         pane.el.addClass('active');
25399         pane.fireEvent('activate');
25400         
25401         
25402     },
25403     
25404     getActivePane : function()
25405     {
25406         var r = false;
25407         Roo.each(this.panes, function(p) {
25408             if(p.el.hasClass('active')){
25409                 r = p;
25410                 return false;
25411             }
25412             
25413             return;
25414         });
25415         
25416         return r;
25417     }
25418     
25419     
25420 });
25421
25422  
25423 /*
25424  * - LGPL
25425  *
25426  * Tab pane
25427  * 
25428  */
25429 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25430 /**
25431  * @class Roo.bootstrap.TabPane
25432  * @extends Roo.bootstrap.Component
25433  * Bootstrap TabPane class
25434  * @cfg {Boolean} active (false | true) Default false
25435  * @cfg {String} title title of panel
25436
25437  * 
25438  * @constructor
25439  * Create a new TabPane
25440  * @param {Object} config The config object
25441  */
25442
25443 Roo.bootstrap.dash.TabPane = function(config){
25444     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25445     
25446     this.addEvents({
25447         // raw events
25448         /**
25449          * @event activate
25450          * When a pane is activated
25451          * @param {Roo.bootstrap.dash.TabPane} pane
25452          */
25453         "activate" : true
25454          
25455     });
25456 };
25457
25458 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25459     
25460     active : false,
25461     title : '',
25462     
25463     // the tabBox that this is attached to.
25464     tab : false,
25465      
25466     getAutoCreate : function() 
25467     {
25468         var cfg = {
25469             tag: 'div',
25470             cls: 'tab-pane'
25471         };
25472         
25473         if(this.active){
25474             cfg.cls += ' active';
25475         }
25476         
25477         return cfg;
25478     },
25479     initEvents  : function()
25480     {
25481         //Roo.log('trigger add pane handler');
25482         this.parent().fireEvent('addpane', this)
25483     },
25484     
25485      /**
25486      * Updates the tab title 
25487      * @param {String} html to set the title to.
25488      */
25489     setTitle: function(str)
25490     {
25491         if (!this.tab) {
25492             return;
25493         }
25494         this.title = str;
25495         this.tab.select('a', true).first().dom.innerHTML = str;
25496         
25497     }
25498     
25499     
25500     
25501 });
25502
25503  
25504
25505
25506  /*
25507  * - LGPL
25508  *
25509  * menu
25510  * 
25511  */
25512 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25513
25514 /**
25515  * @class Roo.bootstrap.menu.Menu
25516  * @extends Roo.bootstrap.Component
25517  * Bootstrap Menu class - container for Menu
25518  * @cfg {String} html Text of the menu
25519  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25520  * @cfg {String} icon Font awesome icon
25521  * @cfg {String} pos Menu align to (top | bottom) default bottom
25522  * 
25523  * 
25524  * @constructor
25525  * Create a new Menu
25526  * @param {Object} config The config object
25527  */
25528
25529
25530 Roo.bootstrap.menu.Menu = function(config){
25531     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25532     
25533     this.addEvents({
25534         /**
25535          * @event beforeshow
25536          * Fires before this menu is displayed
25537          * @param {Roo.bootstrap.menu.Menu} this
25538          */
25539         beforeshow : true,
25540         /**
25541          * @event beforehide
25542          * Fires before this menu is hidden
25543          * @param {Roo.bootstrap.menu.Menu} this
25544          */
25545         beforehide : true,
25546         /**
25547          * @event show
25548          * Fires after this menu is displayed
25549          * @param {Roo.bootstrap.menu.Menu} this
25550          */
25551         show : true,
25552         /**
25553          * @event hide
25554          * Fires after this menu is hidden
25555          * @param {Roo.bootstrap.menu.Menu} this
25556          */
25557         hide : true,
25558         /**
25559          * @event click
25560          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25561          * @param {Roo.bootstrap.menu.Menu} this
25562          * @param {Roo.EventObject} e
25563          */
25564         click : true
25565     });
25566     
25567 };
25568
25569 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25570     
25571     submenu : false,
25572     html : '',
25573     weight : 'default',
25574     icon : false,
25575     pos : 'bottom',
25576     
25577     
25578     getChildContainer : function() {
25579         if(this.isSubMenu){
25580             return this.el;
25581         }
25582         
25583         return this.el.select('ul.dropdown-menu', true).first();  
25584     },
25585     
25586     getAutoCreate : function()
25587     {
25588         var text = [
25589             {
25590                 tag : 'span',
25591                 cls : 'roo-menu-text',
25592                 html : this.html
25593             }
25594         ];
25595         
25596         if(this.icon){
25597             text.unshift({
25598                 tag : 'i',
25599                 cls : 'fa ' + this.icon
25600             })
25601         }
25602         
25603         
25604         var cfg = {
25605             tag : 'div',
25606             cls : 'btn-group',
25607             cn : [
25608                 {
25609                     tag : 'button',
25610                     cls : 'dropdown-button btn btn-' + this.weight,
25611                     cn : text
25612                 },
25613                 {
25614                     tag : 'button',
25615                     cls : 'dropdown-toggle btn btn-' + this.weight,
25616                     cn : [
25617                         {
25618                             tag : 'span',
25619                             cls : 'caret'
25620                         }
25621                     ]
25622                 },
25623                 {
25624                     tag : 'ul',
25625                     cls : 'dropdown-menu'
25626                 }
25627             ]
25628             
25629         };
25630         
25631         if(this.pos == 'top'){
25632             cfg.cls += ' dropup';
25633         }
25634         
25635         if(this.isSubMenu){
25636             cfg = {
25637                 tag : 'ul',
25638                 cls : 'dropdown-menu'
25639             }
25640         }
25641         
25642         return cfg;
25643     },
25644     
25645     onRender : function(ct, position)
25646     {
25647         this.isSubMenu = ct.hasClass('dropdown-submenu');
25648         
25649         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25650     },
25651     
25652     initEvents : function() 
25653     {
25654         if(this.isSubMenu){
25655             return;
25656         }
25657         
25658         this.hidden = true;
25659         
25660         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25661         this.triggerEl.on('click', this.onTriggerPress, this);
25662         
25663         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25664         this.buttonEl.on('click', this.onClick, this);
25665         
25666     },
25667     
25668     list : function()
25669     {
25670         if(this.isSubMenu){
25671             return this.el;
25672         }
25673         
25674         return this.el.select('ul.dropdown-menu', true).first();
25675     },
25676     
25677     onClick : function(e)
25678     {
25679         this.fireEvent("click", this, e);
25680     },
25681     
25682     onTriggerPress  : function(e)
25683     {   
25684         if (this.isVisible()) {
25685             this.hide();
25686         } else {
25687             this.show();
25688         }
25689     },
25690     
25691     isVisible : function(){
25692         return !this.hidden;
25693     },
25694     
25695     show : function()
25696     {
25697         this.fireEvent("beforeshow", this);
25698         
25699         this.hidden = false;
25700         this.el.addClass('open');
25701         
25702         Roo.get(document).on("mouseup", this.onMouseUp, this);
25703         
25704         this.fireEvent("show", this);
25705         
25706         
25707     },
25708     
25709     hide : function()
25710     {
25711         this.fireEvent("beforehide", this);
25712         
25713         this.hidden = true;
25714         this.el.removeClass('open');
25715         
25716         Roo.get(document).un("mouseup", this.onMouseUp);
25717         
25718         this.fireEvent("hide", this);
25719     },
25720     
25721     onMouseUp : function()
25722     {
25723         this.hide();
25724     }
25725     
25726 });
25727
25728  
25729  /*
25730  * - LGPL
25731  *
25732  * menu item
25733  * 
25734  */
25735 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25736
25737 /**
25738  * @class Roo.bootstrap.menu.Item
25739  * @extends Roo.bootstrap.Component
25740  * Bootstrap MenuItem class
25741  * @cfg {Boolean} submenu (true | false) default false
25742  * @cfg {String} html text of the item
25743  * @cfg {String} href the link
25744  * @cfg {Boolean} disable (true | false) default false
25745  * @cfg {Boolean} preventDefault (true | false) default true
25746  * @cfg {String} icon Font awesome icon
25747  * @cfg {String} pos Submenu align to (left | right) default right 
25748  * 
25749  * 
25750  * @constructor
25751  * Create a new Item
25752  * @param {Object} config The config object
25753  */
25754
25755
25756 Roo.bootstrap.menu.Item = function(config){
25757     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25758     this.addEvents({
25759         /**
25760          * @event mouseover
25761          * Fires when the mouse is hovering over this menu
25762          * @param {Roo.bootstrap.menu.Item} this
25763          * @param {Roo.EventObject} e
25764          */
25765         mouseover : true,
25766         /**
25767          * @event mouseout
25768          * Fires when the mouse exits this menu
25769          * @param {Roo.bootstrap.menu.Item} this
25770          * @param {Roo.EventObject} e
25771          */
25772         mouseout : true,
25773         // raw events
25774         /**
25775          * @event click
25776          * The raw click event for the entire grid.
25777          * @param {Roo.EventObject} e
25778          */
25779         click : true
25780     });
25781 };
25782
25783 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25784     
25785     submenu : false,
25786     href : '',
25787     html : '',
25788     preventDefault: true,
25789     disable : false,
25790     icon : false,
25791     pos : 'right',
25792     
25793     getAutoCreate : function()
25794     {
25795         var text = [
25796             {
25797                 tag : 'span',
25798                 cls : 'roo-menu-item-text',
25799                 html : this.html
25800             }
25801         ];
25802         
25803         if(this.icon){
25804             text.unshift({
25805                 tag : 'i',
25806                 cls : 'fa ' + this.icon
25807             })
25808         }
25809         
25810         var cfg = {
25811             tag : 'li',
25812             cn : [
25813                 {
25814                     tag : 'a',
25815                     href : this.href || '#',
25816                     cn : text
25817                 }
25818             ]
25819         };
25820         
25821         if(this.disable){
25822             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25823         }
25824         
25825         if(this.submenu){
25826             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25827             
25828             if(this.pos == 'left'){
25829                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25830             }
25831         }
25832         
25833         return cfg;
25834     },
25835     
25836     initEvents : function() 
25837     {
25838         this.el.on('mouseover', this.onMouseOver, this);
25839         this.el.on('mouseout', this.onMouseOut, this);
25840         
25841         this.el.select('a', true).first().on('click', this.onClick, this);
25842         
25843     },
25844     
25845     onClick : function(e)
25846     {
25847         if(this.preventDefault){
25848             e.preventDefault();
25849         }
25850         
25851         this.fireEvent("click", this, e);
25852     },
25853     
25854     onMouseOver : function(e)
25855     {
25856         if(this.submenu && this.pos == 'left'){
25857             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25858         }
25859         
25860         this.fireEvent("mouseover", this, e);
25861     },
25862     
25863     onMouseOut : function(e)
25864     {
25865         this.fireEvent("mouseout", this, e);
25866     }
25867 });
25868
25869  
25870
25871  /*
25872  * - LGPL
25873  *
25874  * menu separator
25875  * 
25876  */
25877 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25878
25879 /**
25880  * @class Roo.bootstrap.menu.Separator
25881  * @extends Roo.bootstrap.Component
25882  * Bootstrap Separator class
25883  * 
25884  * @constructor
25885  * Create a new Separator
25886  * @param {Object} config The config object
25887  */
25888
25889
25890 Roo.bootstrap.menu.Separator = function(config){
25891     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25892 };
25893
25894 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25895     
25896     getAutoCreate : function(){
25897         var cfg = {
25898             tag : 'li',
25899             cls: 'divider'
25900         };
25901         
25902         return cfg;
25903     }
25904    
25905 });
25906
25907  
25908
25909  /*
25910  * - LGPL
25911  *
25912  * Tooltip
25913  * 
25914  */
25915
25916 /**
25917  * @class Roo.bootstrap.Tooltip
25918  * Bootstrap Tooltip class
25919  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25920  * to determine which dom element triggers the tooltip.
25921  * 
25922  * It needs to add support for additional attributes like tooltip-position
25923  * 
25924  * @constructor
25925  * Create a new Toolti
25926  * @param {Object} config The config object
25927  */
25928
25929 Roo.bootstrap.Tooltip = function(config){
25930     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25931     
25932     this.alignment = Roo.bootstrap.Tooltip.alignment;
25933     
25934     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25935         this.alignment = config.alignment;
25936     }
25937     
25938 };
25939
25940 Roo.apply(Roo.bootstrap.Tooltip, {
25941     /**
25942      * @function init initialize tooltip monitoring.
25943      * @static
25944      */
25945     currentEl : false,
25946     currentTip : false,
25947     currentRegion : false,
25948     
25949     //  init : delay?
25950     
25951     init : function()
25952     {
25953         Roo.get(document).on('mouseover', this.enter ,this);
25954         Roo.get(document).on('mouseout', this.leave, this);
25955          
25956         
25957         this.currentTip = new Roo.bootstrap.Tooltip();
25958     },
25959     
25960     enter : function(ev)
25961     {
25962         var dom = ev.getTarget();
25963         
25964         //Roo.log(['enter',dom]);
25965         var el = Roo.fly(dom);
25966         if (this.currentEl) {
25967             //Roo.log(dom);
25968             //Roo.log(this.currentEl);
25969             //Roo.log(this.currentEl.contains(dom));
25970             if (this.currentEl == el) {
25971                 return;
25972             }
25973             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25974                 return;
25975             }
25976
25977         }
25978         
25979         if (this.currentTip.el) {
25980             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25981         }    
25982         //Roo.log(ev);
25983         
25984         if(!el || el.dom == document){
25985             return;
25986         }
25987         
25988         var bindEl = el;
25989         
25990         // you can not look for children, as if el is the body.. then everythign is the child..
25991         if (!el.attr('tooltip')) { //
25992             if (!el.select("[tooltip]").elements.length) {
25993                 return;
25994             }
25995             // is the mouse over this child...?
25996             bindEl = el.select("[tooltip]").first();
25997             var xy = ev.getXY();
25998             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25999                 //Roo.log("not in region.");
26000                 return;
26001             }
26002             //Roo.log("child element over..");
26003             
26004         }
26005         this.currentEl = bindEl;
26006         this.currentTip.bind(bindEl);
26007         this.currentRegion = Roo.lib.Region.getRegion(dom);
26008         this.currentTip.enter();
26009         
26010     },
26011     leave : function(ev)
26012     {
26013         var dom = ev.getTarget();
26014         //Roo.log(['leave',dom]);
26015         if (!this.currentEl) {
26016             return;
26017         }
26018         
26019         
26020         if (dom != this.currentEl.dom) {
26021             return;
26022         }
26023         var xy = ev.getXY();
26024         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26025             return;
26026         }
26027         // only activate leave if mouse cursor is outside... bounding box..
26028         
26029         
26030         
26031         
26032         if (this.currentTip) {
26033             this.currentTip.leave();
26034         }
26035         //Roo.log('clear currentEl');
26036         this.currentEl = false;
26037         
26038         
26039     },
26040     alignment : {
26041         'left' : ['r-l', [-2,0], 'right'],
26042         'right' : ['l-r', [2,0], 'left'],
26043         'bottom' : ['t-b', [0,2], 'top'],
26044         'top' : [ 'b-t', [0,-2], 'bottom']
26045     }
26046     
26047 });
26048
26049
26050 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26051     
26052     
26053     bindEl : false,
26054     
26055     delay : null, // can be { show : 300 , hide: 500}
26056     
26057     timeout : null,
26058     
26059     hoverState : null, //???
26060     
26061     placement : 'bottom', 
26062     
26063     alignment : false,
26064     
26065     getAutoCreate : function(){
26066     
26067         var cfg = {
26068            cls : 'tooltip',
26069            role : 'tooltip',
26070            cn : [
26071                 {
26072                     cls : 'tooltip-arrow'
26073                 },
26074                 {
26075                     cls : 'tooltip-inner'
26076                 }
26077            ]
26078         };
26079         
26080         return cfg;
26081     },
26082     bind : function(el)
26083     {
26084         this.bindEl = el;
26085     },
26086       
26087     
26088     enter : function () {
26089        
26090         if (this.timeout != null) {
26091             clearTimeout(this.timeout);
26092         }
26093         
26094         this.hoverState = 'in';
26095          //Roo.log("enter - show");
26096         if (!this.delay || !this.delay.show) {
26097             this.show();
26098             return;
26099         }
26100         var _t = this;
26101         this.timeout = setTimeout(function () {
26102             if (_t.hoverState == 'in') {
26103                 _t.show();
26104             }
26105         }, this.delay.show);
26106     },
26107     leave : function()
26108     {
26109         clearTimeout(this.timeout);
26110     
26111         this.hoverState = 'out';
26112          if (!this.delay || !this.delay.hide) {
26113             this.hide();
26114             return;
26115         }
26116        
26117         var _t = this;
26118         this.timeout = setTimeout(function () {
26119             //Roo.log("leave - timeout");
26120             
26121             if (_t.hoverState == 'out') {
26122                 _t.hide();
26123                 Roo.bootstrap.Tooltip.currentEl = false;
26124             }
26125         }, delay);
26126     },
26127     
26128     show : function (msg)
26129     {
26130         if (!this.el) {
26131             this.render(document.body);
26132         }
26133         // set content.
26134         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26135         
26136         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26137         
26138         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26139         
26140         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26141         
26142         var placement = typeof this.placement == 'function' ?
26143             this.placement.call(this, this.el, on_el) :
26144             this.placement;
26145             
26146         var autoToken = /\s?auto?\s?/i;
26147         var autoPlace = autoToken.test(placement);
26148         if (autoPlace) {
26149             placement = placement.replace(autoToken, '') || 'top';
26150         }
26151         
26152         //this.el.detach()
26153         //this.el.setXY([0,0]);
26154         this.el.show();
26155         //this.el.dom.style.display='block';
26156         
26157         //this.el.appendTo(on_el);
26158         
26159         var p = this.getPosition();
26160         var box = this.el.getBox();
26161         
26162         if (autoPlace) {
26163             // fixme..
26164         }
26165         
26166         var align = this.alignment[placement];
26167         
26168         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26169         
26170         if(placement == 'top' || placement == 'bottom'){
26171             if(xy[0] < 0){
26172                 placement = 'right';
26173             }
26174             
26175             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26176                 placement = 'left';
26177             }
26178             
26179             var scroll = Roo.select('body', true).first().getScroll();
26180             
26181             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26182                 placement = 'top';
26183             }
26184             
26185         }
26186         
26187         this.el.alignTo(this.bindEl, align[0],align[1]);
26188         //var arrow = this.el.select('.arrow',true).first();
26189         //arrow.set(align[2], 
26190         
26191         this.el.addClass(placement);
26192         
26193         this.el.addClass('in fade');
26194         
26195         this.hoverState = null;
26196         
26197         if (this.el.hasClass('fade')) {
26198             // fade it?
26199         }
26200         
26201     },
26202     hide : function()
26203     {
26204          
26205         if (!this.el) {
26206             return;
26207         }
26208         //this.el.setXY([0,0]);
26209         this.el.removeClass('in');
26210         //this.el.hide();
26211         
26212     }
26213     
26214 });
26215  
26216
26217  /*
26218  * - LGPL
26219  *
26220  * Location Picker
26221  * 
26222  */
26223
26224 /**
26225  * @class Roo.bootstrap.LocationPicker
26226  * @extends Roo.bootstrap.Component
26227  * Bootstrap LocationPicker class
26228  * @cfg {Number} latitude Position when init default 0
26229  * @cfg {Number} longitude Position when init default 0
26230  * @cfg {Number} zoom default 15
26231  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26232  * @cfg {Boolean} mapTypeControl default false
26233  * @cfg {Boolean} disableDoubleClickZoom default false
26234  * @cfg {Boolean} scrollwheel default true
26235  * @cfg {Boolean} streetViewControl default false
26236  * @cfg {Number} radius default 0
26237  * @cfg {String} locationName
26238  * @cfg {Boolean} draggable default true
26239  * @cfg {Boolean} enableAutocomplete default false
26240  * @cfg {Boolean} enableReverseGeocode default true
26241  * @cfg {String} markerTitle
26242  * 
26243  * @constructor
26244  * Create a new LocationPicker
26245  * @param {Object} config The config object
26246  */
26247
26248
26249 Roo.bootstrap.LocationPicker = function(config){
26250     
26251     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26252     
26253     this.addEvents({
26254         /**
26255          * @event initial
26256          * Fires when the picker initialized.
26257          * @param {Roo.bootstrap.LocationPicker} this
26258          * @param {Google Location} location
26259          */
26260         initial : true,
26261         /**
26262          * @event positionchanged
26263          * Fires when the picker position changed.
26264          * @param {Roo.bootstrap.LocationPicker} this
26265          * @param {Google Location} location
26266          */
26267         positionchanged : true,
26268         /**
26269          * @event resize
26270          * Fires when the map resize.
26271          * @param {Roo.bootstrap.LocationPicker} this
26272          */
26273         resize : true,
26274         /**
26275          * @event show
26276          * Fires when the map show.
26277          * @param {Roo.bootstrap.LocationPicker} this
26278          */
26279         show : true,
26280         /**
26281          * @event hide
26282          * Fires when the map hide.
26283          * @param {Roo.bootstrap.LocationPicker} this
26284          */
26285         hide : true,
26286         /**
26287          * @event mapClick
26288          * Fires when click the map.
26289          * @param {Roo.bootstrap.LocationPicker} this
26290          * @param {Map event} e
26291          */
26292         mapClick : true,
26293         /**
26294          * @event mapRightClick
26295          * Fires when right click the map.
26296          * @param {Roo.bootstrap.LocationPicker} this
26297          * @param {Map event} e
26298          */
26299         mapRightClick : true,
26300         /**
26301          * @event markerClick
26302          * Fires when click the marker.
26303          * @param {Roo.bootstrap.LocationPicker} this
26304          * @param {Map event} e
26305          */
26306         markerClick : true,
26307         /**
26308          * @event markerRightClick
26309          * Fires when right click the marker.
26310          * @param {Roo.bootstrap.LocationPicker} this
26311          * @param {Map event} e
26312          */
26313         markerRightClick : true,
26314         /**
26315          * @event OverlayViewDraw
26316          * Fires when OverlayView Draw
26317          * @param {Roo.bootstrap.LocationPicker} this
26318          */
26319         OverlayViewDraw : true,
26320         /**
26321          * @event OverlayViewOnAdd
26322          * Fires when OverlayView Draw
26323          * @param {Roo.bootstrap.LocationPicker} this
26324          */
26325         OverlayViewOnAdd : true,
26326         /**
26327          * @event OverlayViewOnRemove
26328          * Fires when OverlayView Draw
26329          * @param {Roo.bootstrap.LocationPicker} this
26330          */
26331         OverlayViewOnRemove : true,
26332         /**
26333          * @event OverlayViewShow
26334          * Fires when OverlayView Draw
26335          * @param {Roo.bootstrap.LocationPicker} this
26336          * @param {Pixel} cpx
26337          */
26338         OverlayViewShow : true,
26339         /**
26340          * @event OverlayViewHide
26341          * Fires when OverlayView Draw
26342          * @param {Roo.bootstrap.LocationPicker} this
26343          */
26344         OverlayViewHide : true,
26345         /**
26346          * @event loadexception
26347          * Fires when load google lib failed.
26348          * @param {Roo.bootstrap.LocationPicker} this
26349          */
26350         loadexception : true
26351     });
26352         
26353 };
26354
26355 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26356     
26357     gMapContext: false,
26358     
26359     latitude: 0,
26360     longitude: 0,
26361     zoom: 15,
26362     mapTypeId: false,
26363     mapTypeControl: false,
26364     disableDoubleClickZoom: false,
26365     scrollwheel: true,
26366     streetViewControl: false,
26367     radius: 0,
26368     locationName: '',
26369     draggable: true,
26370     enableAutocomplete: false,
26371     enableReverseGeocode: true,
26372     markerTitle: '',
26373     
26374     getAutoCreate: function()
26375     {
26376
26377         var cfg = {
26378             tag: 'div',
26379             cls: 'roo-location-picker'
26380         };
26381         
26382         return cfg
26383     },
26384     
26385     initEvents: function(ct, position)
26386     {       
26387         if(!this.el.getWidth() || this.isApplied()){
26388             return;
26389         }
26390         
26391         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26392         
26393         this.initial();
26394     },
26395     
26396     initial: function()
26397     {
26398         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26399             this.fireEvent('loadexception', this);
26400             return;
26401         }
26402         
26403         if(!this.mapTypeId){
26404             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26405         }
26406         
26407         this.gMapContext = this.GMapContext();
26408         
26409         this.initOverlayView();
26410         
26411         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26412         
26413         var _this = this;
26414                 
26415         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26416             _this.setPosition(_this.gMapContext.marker.position);
26417         });
26418         
26419         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26420             _this.fireEvent('mapClick', this, event);
26421             
26422         });
26423
26424         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26425             _this.fireEvent('mapRightClick', this, event);
26426             
26427         });
26428         
26429         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26430             _this.fireEvent('markerClick', this, event);
26431             
26432         });
26433
26434         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26435             _this.fireEvent('markerRightClick', this, event);
26436             
26437         });
26438         
26439         this.setPosition(this.gMapContext.location);
26440         
26441         this.fireEvent('initial', this, this.gMapContext.location);
26442     },
26443     
26444     initOverlayView: function()
26445     {
26446         var _this = this;
26447         
26448         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26449             
26450             draw: function()
26451             {
26452                 _this.fireEvent('OverlayViewDraw', _this);
26453             },
26454             
26455             onAdd: function()
26456             {
26457                 _this.fireEvent('OverlayViewOnAdd', _this);
26458             },
26459             
26460             onRemove: function()
26461             {
26462                 _this.fireEvent('OverlayViewOnRemove', _this);
26463             },
26464             
26465             show: function(cpx)
26466             {
26467                 _this.fireEvent('OverlayViewShow', _this, cpx);
26468             },
26469             
26470             hide: function()
26471             {
26472                 _this.fireEvent('OverlayViewHide', _this);
26473             }
26474             
26475         });
26476     },
26477     
26478     fromLatLngToContainerPixel: function(event)
26479     {
26480         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26481     },
26482     
26483     isApplied: function() 
26484     {
26485         return this.getGmapContext() == false ? false : true;
26486     },
26487     
26488     getGmapContext: function() 
26489     {
26490         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26491     },
26492     
26493     GMapContext: function() 
26494     {
26495         var position = new google.maps.LatLng(this.latitude, this.longitude);
26496         
26497         var _map = new google.maps.Map(this.el.dom, {
26498             center: position,
26499             zoom: this.zoom,
26500             mapTypeId: this.mapTypeId,
26501             mapTypeControl: this.mapTypeControl,
26502             disableDoubleClickZoom: this.disableDoubleClickZoom,
26503             scrollwheel: this.scrollwheel,
26504             streetViewControl: this.streetViewControl,
26505             locationName: this.locationName,
26506             draggable: this.draggable,
26507             enableAutocomplete: this.enableAutocomplete,
26508             enableReverseGeocode: this.enableReverseGeocode
26509         });
26510         
26511         var _marker = new google.maps.Marker({
26512             position: position,
26513             map: _map,
26514             title: this.markerTitle,
26515             draggable: this.draggable
26516         });
26517         
26518         return {
26519             map: _map,
26520             marker: _marker,
26521             circle: null,
26522             location: position,
26523             radius: this.radius,
26524             locationName: this.locationName,
26525             addressComponents: {
26526                 formatted_address: null,
26527                 addressLine1: null,
26528                 addressLine2: null,
26529                 streetName: null,
26530                 streetNumber: null,
26531                 city: null,
26532                 district: null,
26533                 state: null,
26534                 stateOrProvince: null
26535             },
26536             settings: this,
26537             domContainer: this.el.dom,
26538             geodecoder: new google.maps.Geocoder()
26539         };
26540     },
26541     
26542     drawCircle: function(center, radius, options) 
26543     {
26544         if (this.gMapContext.circle != null) {
26545             this.gMapContext.circle.setMap(null);
26546         }
26547         if (radius > 0) {
26548             radius *= 1;
26549             options = Roo.apply({}, options, {
26550                 strokeColor: "#0000FF",
26551                 strokeOpacity: .35,
26552                 strokeWeight: 2,
26553                 fillColor: "#0000FF",
26554                 fillOpacity: .2
26555             });
26556             
26557             options.map = this.gMapContext.map;
26558             options.radius = radius;
26559             options.center = center;
26560             this.gMapContext.circle = new google.maps.Circle(options);
26561             return this.gMapContext.circle;
26562         }
26563         
26564         return null;
26565     },
26566     
26567     setPosition: function(location) 
26568     {
26569         this.gMapContext.location = location;
26570         this.gMapContext.marker.setPosition(location);
26571         this.gMapContext.map.panTo(location);
26572         this.drawCircle(location, this.gMapContext.radius, {});
26573         
26574         var _this = this;
26575         
26576         if (this.gMapContext.settings.enableReverseGeocode) {
26577             this.gMapContext.geodecoder.geocode({
26578                 latLng: this.gMapContext.location
26579             }, function(results, status) {
26580                 
26581                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26582                     _this.gMapContext.locationName = results[0].formatted_address;
26583                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26584                     
26585                     _this.fireEvent('positionchanged', this, location);
26586                 }
26587             });
26588             
26589             return;
26590         }
26591         
26592         this.fireEvent('positionchanged', this, location);
26593     },
26594     
26595     resize: function()
26596     {
26597         google.maps.event.trigger(this.gMapContext.map, "resize");
26598         
26599         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26600         
26601         this.fireEvent('resize', this);
26602     },
26603     
26604     setPositionByLatLng: function(latitude, longitude)
26605     {
26606         this.setPosition(new google.maps.LatLng(latitude, longitude));
26607     },
26608     
26609     getCurrentPosition: function() 
26610     {
26611         return {
26612             latitude: this.gMapContext.location.lat(),
26613             longitude: this.gMapContext.location.lng()
26614         };
26615     },
26616     
26617     getAddressName: function() 
26618     {
26619         return this.gMapContext.locationName;
26620     },
26621     
26622     getAddressComponents: function() 
26623     {
26624         return this.gMapContext.addressComponents;
26625     },
26626     
26627     address_component_from_google_geocode: function(address_components) 
26628     {
26629         var result = {};
26630         
26631         for (var i = 0; i < address_components.length; i++) {
26632             var component = address_components[i];
26633             if (component.types.indexOf("postal_code") >= 0) {
26634                 result.postalCode = component.short_name;
26635             } else if (component.types.indexOf("street_number") >= 0) {
26636                 result.streetNumber = component.short_name;
26637             } else if (component.types.indexOf("route") >= 0) {
26638                 result.streetName = component.short_name;
26639             } else if (component.types.indexOf("neighborhood") >= 0) {
26640                 result.city = component.short_name;
26641             } else if (component.types.indexOf("locality") >= 0) {
26642                 result.city = component.short_name;
26643             } else if (component.types.indexOf("sublocality") >= 0) {
26644                 result.district = component.short_name;
26645             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26646                 result.stateOrProvince = component.short_name;
26647             } else if (component.types.indexOf("country") >= 0) {
26648                 result.country = component.short_name;
26649             }
26650         }
26651         
26652         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26653         result.addressLine2 = "";
26654         return result;
26655     },
26656     
26657     setZoomLevel: function(zoom)
26658     {
26659         this.gMapContext.map.setZoom(zoom);
26660     },
26661     
26662     show: function()
26663     {
26664         if(!this.el){
26665             return;
26666         }
26667         
26668         this.el.show();
26669         
26670         this.resize();
26671         
26672         this.fireEvent('show', this);
26673     },
26674     
26675     hide: function()
26676     {
26677         if(!this.el){
26678             return;
26679         }
26680         
26681         this.el.hide();
26682         
26683         this.fireEvent('hide', this);
26684     }
26685     
26686 });
26687
26688 Roo.apply(Roo.bootstrap.LocationPicker, {
26689     
26690     OverlayView : function(map, options)
26691     {
26692         options = options || {};
26693         
26694         this.setMap(map);
26695     }
26696     
26697     
26698 });/*
26699  * - LGPL
26700  *
26701  * Alert
26702  * 
26703  */
26704
26705 /**
26706  * @class Roo.bootstrap.Alert
26707  * @extends Roo.bootstrap.Component
26708  * Bootstrap Alert class
26709  * @cfg {String} title The title of alert
26710  * @cfg {String} html The content of alert
26711  * @cfg {String} weight (  success | info | warning | danger )
26712  * @cfg {String} faicon font-awesomeicon
26713  * 
26714  * @constructor
26715  * Create a new alert
26716  * @param {Object} config The config object
26717  */
26718
26719
26720 Roo.bootstrap.Alert = function(config){
26721     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26722     
26723 };
26724
26725 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26726     
26727     title: '',
26728     html: '',
26729     weight: false,
26730     faicon: false,
26731     
26732     getAutoCreate : function()
26733     {
26734         
26735         var cfg = {
26736             tag : 'div',
26737             cls : 'alert',
26738             cn : [
26739                 {
26740                     tag : 'i',
26741                     cls : 'roo-alert-icon'
26742                     
26743                 },
26744                 {
26745                     tag : 'b',
26746                     cls : 'roo-alert-title',
26747                     html : this.title
26748                 },
26749                 {
26750                     tag : 'span',
26751                     cls : 'roo-alert-text',
26752                     html : this.html
26753                 }
26754             ]
26755         };
26756         
26757         if(this.faicon){
26758             cfg.cn[0].cls += ' fa ' + this.faicon;
26759         }
26760         
26761         if(this.weight){
26762             cfg.cls += ' alert-' + this.weight;
26763         }
26764         
26765         return cfg;
26766     },
26767     
26768     initEvents: function() 
26769     {
26770         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26771     },
26772     
26773     setTitle : function(str)
26774     {
26775         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26776     },
26777     
26778     setText : function(str)
26779     {
26780         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26781     },
26782     
26783     setWeight : function(weight)
26784     {
26785         if(this.weight){
26786             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26787         }
26788         
26789         this.weight = weight;
26790         
26791         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26792     },
26793     
26794     setIcon : function(icon)
26795     {
26796         if(this.faicon){
26797             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26798         }
26799         
26800         this.faicon = icon;
26801         
26802         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26803     },
26804     
26805     hide: function() 
26806     {
26807         this.el.hide();   
26808     },
26809     
26810     show: function() 
26811     {  
26812         this.el.show();   
26813     }
26814     
26815 });
26816
26817  
26818 /*
26819 * Licence: LGPL
26820 */
26821
26822 /**
26823  * @class Roo.bootstrap.UploadCropbox
26824  * @extends Roo.bootstrap.Component
26825  * Bootstrap UploadCropbox class
26826  * @cfg {String} emptyText show when image has been loaded
26827  * @cfg {String} rotateNotify show when image too small to rotate
26828  * @cfg {Number} errorTimeout default 3000
26829  * @cfg {Number} minWidth default 300
26830  * @cfg {Number} minHeight default 300
26831  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26832  * @cfg {Boolean} isDocument (true|false) default false
26833  * @cfg {String} url action url
26834  * @cfg {String} paramName default 'imageUpload'
26835  * @cfg {String} method default POST
26836  * @cfg {Boolean} loadMask (true|false) default true
26837  * @cfg {Boolean} loadingText default 'Loading...'
26838  * 
26839  * @constructor
26840  * Create a new UploadCropbox
26841  * @param {Object} config The config object
26842  */
26843
26844 Roo.bootstrap.UploadCropbox = function(config){
26845     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26846     
26847     this.addEvents({
26848         /**
26849          * @event beforeselectfile
26850          * Fire before select file
26851          * @param {Roo.bootstrap.UploadCropbox} this
26852          */
26853         "beforeselectfile" : true,
26854         /**
26855          * @event initial
26856          * Fire after initEvent
26857          * @param {Roo.bootstrap.UploadCropbox} this
26858          */
26859         "initial" : true,
26860         /**
26861          * @event crop
26862          * Fire after initEvent
26863          * @param {Roo.bootstrap.UploadCropbox} this
26864          * @param {String} data
26865          */
26866         "crop" : true,
26867         /**
26868          * @event prepare
26869          * Fire when preparing the file data
26870          * @param {Roo.bootstrap.UploadCropbox} this
26871          * @param {Object} file
26872          */
26873         "prepare" : true,
26874         /**
26875          * @event exception
26876          * Fire when get exception
26877          * @param {Roo.bootstrap.UploadCropbox} this
26878          * @param {XMLHttpRequest} xhr
26879          */
26880         "exception" : true,
26881         /**
26882          * @event beforeloadcanvas
26883          * Fire before load the canvas
26884          * @param {Roo.bootstrap.UploadCropbox} this
26885          * @param {String} src
26886          */
26887         "beforeloadcanvas" : true,
26888         /**
26889          * @event trash
26890          * Fire when trash image
26891          * @param {Roo.bootstrap.UploadCropbox} this
26892          */
26893         "trash" : true,
26894         /**
26895          * @event download
26896          * Fire when download the image
26897          * @param {Roo.bootstrap.UploadCropbox} this
26898          */
26899         "download" : true,
26900         /**
26901          * @event footerbuttonclick
26902          * Fire when footerbuttonclick
26903          * @param {Roo.bootstrap.UploadCropbox} this
26904          * @param {String} type
26905          */
26906         "footerbuttonclick" : true,
26907         /**
26908          * @event resize
26909          * Fire when resize
26910          * @param {Roo.bootstrap.UploadCropbox} this
26911          */
26912         "resize" : true,
26913         /**
26914          * @event rotate
26915          * Fire when rotate the image
26916          * @param {Roo.bootstrap.UploadCropbox} this
26917          * @param {String} pos
26918          */
26919         "rotate" : true,
26920         /**
26921          * @event inspect
26922          * Fire when inspect the file
26923          * @param {Roo.bootstrap.UploadCropbox} this
26924          * @param {Object} file
26925          */
26926         "inspect" : true,
26927         /**
26928          * @event upload
26929          * Fire when xhr upload the file
26930          * @param {Roo.bootstrap.UploadCropbox} this
26931          * @param {Object} data
26932          */
26933         "upload" : true,
26934         /**
26935          * @event arrange
26936          * Fire when arrange the file data
26937          * @param {Roo.bootstrap.UploadCropbox} this
26938          * @param {Object} formData
26939          */
26940         "arrange" : true
26941     });
26942     
26943     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26944 };
26945
26946 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26947     
26948     emptyText : 'Click to upload image',
26949     rotateNotify : 'Image is too small to rotate',
26950     errorTimeout : 3000,
26951     scale : 0,
26952     baseScale : 1,
26953     rotate : 0,
26954     dragable : false,
26955     pinching : false,
26956     mouseX : 0,
26957     mouseY : 0,
26958     cropData : false,
26959     minWidth : 300,
26960     minHeight : 300,
26961     file : false,
26962     exif : {},
26963     baseRotate : 1,
26964     cropType : 'image/jpeg',
26965     buttons : false,
26966     canvasLoaded : false,
26967     isDocument : false,
26968     method : 'POST',
26969     paramName : 'imageUpload',
26970     loadMask : true,
26971     loadingText : 'Loading...',
26972     maskEl : false,
26973     
26974     getAutoCreate : function()
26975     {
26976         var cfg = {
26977             tag : 'div',
26978             cls : 'roo-upload-cropbox',
26979             cn : [
26980                 {
26981                     tag : 'input',
26982                     cls : 'roo-upload-cropbox-selector',
26983                     type : 'file'
26984                 },
26985                 {
26986                     tag : 'div',
26987                     cls : 'roo-upload-cropbox-body',
26988                     style : 'cursor:pointer',
26989                     cn : [
26990                         {
26991                             tag : 'div',
26992                             cls : 'roo-upload-cropbox-preview'
26993                         },
26994                         {
26995                             tag : 'div',
26996                             cls : 'roo-upload-cropbox-thumb'
26997                         },
26998                         {
26999                             tag : 'div',
27000                             cls : 'roo-upload-cropbox-empty-notify',
27001                             html : this.emptyText
27002                         },
27003                         {
27004                             tag : 'div',
27005                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27006                             html : this.rotateNotify
27007                         }
27008                     ]
27009                 },
27010                 {
27011                     tag : 'div',
27012                     cls : 'roo-upload-cropbox-footer',
27013                     cn : {
27014                         tag : 'div',
27015                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27016                         cn : []
27017                     }
27018                 }
27019             ]
27020         };
27021         
27022         return cfg;
27023     },
27024     
27025     onRender : function(ct, position)
27026     {
27027         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27028         
27029         if (this.buttons.length) {
27030             
27031             Roo.each(this.buttons, function(bb) {
27032                 
27033                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27034                 
27035                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27036                 
27037             }, this);
27038         }
27039         
27040         if(this.loadMask){
27041             this.maskEl = this.el;
27042         }
27043     },
27044     
27045     initEvents : function()
27046     {
27047         this.urlAPI = (window.createObjectURL && window) || 
27048                                 (window.URL && URL.revokeObjectURL && URL) || 
27049                                 (window.webkitURL && webkitURL);
27050                         
27051         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27052         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27053         
27054         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27055         this.selectorEl.hide();
27056         
27057         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27058         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27059         
27060         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27061         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27062         this.thumbEl.hide();
27063         
27064         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27065         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27066         
27067         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27068         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27069         this.errorEl.hide();
27070         
27071         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27072         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27073         this.footerEl.hide();
27074         
27075         this.setThumbBoxSize();
27076         
27077         this.bind();
27078         
27079         this.resize();
27080         
27081         this.fireEvent('initial', this);
27082     },
27083
27084     bind : function()
27085     {
27086         var _this = this;
27087         
27088         window.addEventListener("resize", function() { _this.resize(); } );
27089         
27090         this.bodyEl.on('click', this.beforeSelectFile, this);
27091         
27092         if(Roo.isTouch){
27093             this.bodyEl.on('touchstart', this.onTouchStart, this);
27094             this.bodyEl.on('touchmove', this.onTouchMove, this);
27095             this.bodyEl.on('touchend', this.onTouchEnd, this);
27096         }
27097         
27098         if(!Roo.isTouch){
27099             this.bodyEl.on('mousedown', this.onMouseDown, this);
27100             this.bodyEl.on('mousemove', this.onMouseMove, this);
27101             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27102             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27103             Roo.get(document).on('mouseup', this.onMouseUp, this);
27104         }
27105         
27106         this.selectorEl.on('change', this.onFileSelected, this);
27107     },
27108     
27109     reset : function()
27110     {    
27111         this.scale = 0;
27112         this.baseScale = 1;
27113         this.rotate = 0;
27114         this.baseRotate = 1;
27115         this.dragable = false;
27116         this.pinching = false;
27117         this.mouseX = 0;
27118         this.mouseY = 0;
27119         this.cropData = false;
27120         this.notifyEl.dom.innerHTML = this.emptyText;
27121         
27122         this.selectorEl.dom.value = '';
27123         
27124     },
27125     
27126     resize : function()
27127     {
27128         if(this.fireEvent('resize', this) != false){
27129             this.setThumbBoxPosition();
27130             this.setCanvasPosition();
27131         }
27132     },
27133     
27134     onFooterButtonClick : function(e, el, o, type)
27135     {
27136         switch (type) {
27137             case 'rotate-left' :
27138                 this.onRotateLeft(e);
27139                 break;
27140             case 'rotate-right' :
27141                 this.onRotateRight(e);
27142                 break;
27143             case 'picture' :
27144                 this.beforeSelectFile(e);
27145                 break;
27146             case 'trash' :
27147                 this.trash(e);
27148                 break;
27149             case 'crop' :
27150                 this.crop(e);
27151                 break;
27152             case 'download' :
27153                 this.download(e);
27154                 break;
27155             default :
27156                 break;
27157         }
27158         
27159         this.fireEvent('footerbuttonclick', this, type);
27160     },
27161     
27162     beforeSelectFile : function(e)
27163     {
27164         e.preventDefault();
27165         
27166         if(this.fireEvent('beforeselectfile', this) != false){
27167             this.selectorEl.dom.click();
27168         }
27169     },
27170     
27171     onFileSelected : function(e)
27172     {
27173         e.preventDefault();
27174         
27175         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27176             return;
27177         }
27178         
27179         var file = this.selectorEl.dom.files[0];
27180         
27181         if(this.fireEvent('inspect', this, file) != false){
27182             this.prepare(file);
27183         }
27184         
27185     },
27186     
27187     trash : function(e)
27188     {
27189         this.fireEvent('trash', this);
27190     },
27191     
27192     download : function(e)
27193     {
27194         this.fireEvent('download', this);
27195     },
27196     
27197     loadCanvas : function(src)
27198     {   
27199         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27200             
27201             this.reset();
27202             
27203             this.imageEl = document.createElement('img');
27204             
27205             var _this = this;
27206             
27207             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27208             
27209             this.imageEl.src = src;
27210         }
27211     },
27212     
27213     onLoadCanvas : function()
27214     {   
27215         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27216         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27217         
27218         this.bodyEl.un('click', this.beforeSelectFile, this);
27219         
27220         this.notifyEl.hide();
27221         this.thumbEl.show();
27222         this.footerEl.show();
27223         
27224         this.baseRotateLevel();
27225         
27226         if(this.isDocument){
27227             this.setThumbBoxSize();
27228         }
27229         
27230         this.setThumbBoxPosition();
27231         
27232         this.baseScaleLevel();
27233         
27234         this.draw();
27235         
27236         this.resize();
27237         
27238         this.canvasLoaded = true;
27239         
27240         if(this.loadMask){
27241             this.maskEl.unmask();
27242         }
27243         
27244     },
27245     
27246     setCanvasPosition : function()
27247     {   
27248         if(!this.canvasEl){
27249             return;
27250         }
27251         
27252         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27253         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27254         
27255         this.previewEl.setLeft(pw);
27256         this.previewEl.setTop(ph);
27257         
27258     },
27259     
27260     onMouseDown : function(e)
27261     {   
27262         e.stopEvent();
27263         
27264         this.dragable = true;
27265         this.pinching = false;
27266         
27267         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27268             this.dragable = false;
27269             return;
27270         }
27271         
27272         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27273         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27274         
27275     },
27276     
27277     onMouseMove : function(e)
27278     {   
27279         e.stopEvent();
27280         
27281         if(!this.canvasLoaded){
27282             return;
27283         }
27284         
27285         if (!this.dragable){
27286             return;
27287         }
27288         
27289         var minX = Math.ceil(this.thumbEl.getLeft(true));
27290         var minY = Math.ceil(this.thumbEl.getTop(true));
27291         
27292         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27293         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27294         
27295         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27296         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27297         
27298         x = x - this.mouseX;
27299         y = y - this.mouseY;
27300         
27301         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27302         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27303         
27304         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27305         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27306         
27307         this.previewEl.setLeft(bgX);
27308         this.previewEl.setTop(bgY);
27309         
27310         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27311         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27312     },
27313     
27314     onMouseUp : function(e)
27315     {   
27316         e.stopEvent();
27317         
27318         this.dragable = false;
27319     },
27320     
27321     onMouseWheel : function(e)
27322     {   
27323         e.stopEvent();
27324         
27325         this.startScale = this.scale;
27326         
27327         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27328         
27329         if(!this.zoomable()){
27330             this.scale = this.startScale;
27331             return;
27332         }
27333         
27334         this.draw();
27335         
27336         return;
27337     },
27338     
27339     zoomable : function()
27340     {
27341         var minScale = this.thumbEl.getWidth() / this.minWidth;
27342         
27343         if(this.minWidth < this.minHeight){
27344             minScale = this.thumbEl.getHeight() / this.minHeight;
27345         }
27346         
27347         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27348         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27349         
27350         if(
27351                 this.isDocument &&
27352                 (this.rotate == 0 || this.rotate == 180) && 
27353                 (
27354                     width > this.imageEl.OriginWidth || 
27355                     height > this.imageEl.OriginHeight ||
27356                     (width < this.minWidth && height < this.minHeight)
27357                 )
27358         ){
27359             return false;
27360         }
27361         
27362         if(
27363                 this.isDocument &&
27364                 (this.rotate == 90 || this.rotate == 270) && 
27365                 (
27366                     width > this.imageEl.OriginWidth || 
27367                     height > this.imageEl.OriginHeight ||
27368                     (width < this.minHeight && height < this.minWidth)
27369                 )
27370         ){
27371             return false;
27372         }
27373         
27374         if(
27375                 !this.isDocument &&
27376                 (this.rotate == 0 || this.rotate == 180) && 
27377                 (
27378                     width < this.minWidth || 
27379                     width > this.imageEl.OriginWidth || 
27380                     height < this.minHeight || 
27381                     height > this.imageEl.OriginHeight
27382                 )
27383         ){
27384             return false;
27385         }
27386         
27387         if(
27388                 !this.isDocument &&
27389                 (this.rotate == 90 || this.rotate == 270) && 
27390                 (
27391                     width < this.minHeight || 
27392                     width > this.imageEl.OriginWidth || 
27393                     height < this.minWidth || 
27394                     height > this.imageEl.OriginHeight
27395                 )
27396         ){
27397             return false;
27398         }
27399         
27400         return true;
27401         
27402     },
27403     
27404     onRotateLeft : function(e)
27405     {   
27406         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27407             
27408             var minScale = this.thumbEl.getWidth() / this.minWidth;
27409             
27410             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27411             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27412             
27413             this.startScale = this.scale;
27414             
27415             while (this.getScaleLevel() < minScale){
27416             
27417                 this.scale = this.scale + 1;
27418                 
27419                 if(!this.zoomable()){
27420                     break;
27421                 }
27422                 
27423                 if(
27424                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27425                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27426                 ){
27427                     continue;
27428                 }
27429                 
27430                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27431
27432                 this.draw();
27433                 
27434                 return;
27435             }
27436             
27437             this.scale = this.startScale;
27438             
27439             this.onRotateFail();
27440             
27441             return false;
27442         }
27443         
27444         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27445
27446         if(this.isDocument){
27447             this.setThumbBoxSize();
27448             this.setThumbBoxPosition();
27449             this.setCanvasPosition();
27450         }
27451         
27452         this.draw();
27453         
27454         this.fireEvent('rotate', this, 'left');
27455         
27456     },
27457     
27458     onRotateRight : function(e)
27459     {
27460         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27461             
27462             var minScale = this.thumbEl.getWidth() / this.minWidth;
27463         
27464             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27465             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27466             
27467             this.startScale = this.scale;
27468             
27469             while (this.getScaleLevel() < minScale){
27470             
27471                 this.scale = this.scale + 1;
27472                 
27473                 if(!this.zoomable()){
27474                     break;
27475                 }
27476                 
27477                 if(
27478                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27479                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27480                 ){
27481                     continue;
27482                 }
27483                 
27484                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27485
27486                 this.draw();
27487                 
27488                 return;
27489             }
27490             
27491             this.scale = this.startScale;
27492             
27493             this.onRotateFail();
27494             
27495             return false;
27496         }
27497         
27498         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27499
27500         if(this.isDocument){
27501             this.setThumbBoxSize();
27502             this.setThumbBoxPosition();
27503             this.setCanvasPosition();
27504         }
27505         
27506         this.draw();
27507         
27508         this.fireEvent('rotate', this, 'right');
27509     },
27510     
27511     onRotateFail : function()
27512     {
27513         this.errorEl.show(true);
27514         
27515         var _this = this;
27516         
27517         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27518     },
27519     
27520     draw : function()
27521     {
27522         this.previewEl.dom.innerHTML = '';
27523         
27524         var canvasEl = document.createElement("canvas");
27525         
27526         var contextEl = canvasEl.getContext("2d");
27527         
27528         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27529         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27530         var center = this.imageEl.OriginWidth / 2;
27531         
27532         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27533             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27534             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27535             center = this.imageEl.OriginHeight / 2;
27536         }
27537         
27538         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27539         
27540         contextEl.translate(center, center);
27541         contextEl.rotate(this.rotate * Math.PI / 180);
27542
27543         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27544         
27545         this.canvasEl = document.createElement("canvas");
27546         
27547         this.contextEl = this.canvasEl.getContext("2d");
27548         
27549         switch (this.rotate) {
27550             case 0 :
27551                 
27552                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27553                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27554                 
27555                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27556                 
27557                 break;
27558             case 90 : 
27559                 
27560                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27561                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27562                 
27563                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27564                     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);
27565                     break;
27566                 }
27567                 
27568                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27569                 
27570                 break;
27571             case 180 :
27572                 
27573                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27574                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27575                 
27576                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27577                     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);
27578                     break;
27579                 }
27580                 
27581                 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);
27582                 
27583                 break;
27584             case 270 :
27585                 
27586                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27587                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27588         
27589                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27590                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27591                     break;
27592                 }
27593                 
27594                 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);
27595                 
27596                 break;
27597             default : 
27598                 break;
27599         }
27600         
27601         this.previewEl.appendChild(this.canvasEl);
27602         
27603         this.setCanvasPosition();
27604     },
27605     
27606     crop : function()
27607     {
27608         if(!this.canvasLoaded){
27609             return;
27610         }
27611         
27612         var imageCanvas = document.createElement("canvas");
27613         
27614         var imageContext = imageCanvas.getContext("2d");
27615         
27616         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27617         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27618         
27619         var center = imageCanvas.width / 2;
27620         
27621         imageContext.translate(center, center);
27622         
27623         imageContext.rotate(this.rotate * Math.PI / 180);
27624         
27625         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27626         
27627         var canvas = document.createElement("canvas");
27628         
27629         var context = canvas.getContext("2d");
27630                 
27631         canvas.width = this.minWidth;
27632         canvas.height = this.minHeight;
27633
27634         switch (this.rotate) {
27635             case 0 :
27636                 
27637                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27638                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27639                 
27640                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27641                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27642                 
27643                 var targetWidth = this.minWidth - 2 * x;
27644                 var targetHeight = this.minHeight - 2 * y;
27645                 
27646                 var scale = 1;
27647                 
27648                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27649                     scale = targetWidth / width;
27650                 }
27651                 
27652                 if(x > 0 && y == 0){
27653                     scale = targetHeight / height;
27654                 }
27655                 
27656                 if(x > 0 && y > 0){
27657                     scale = targetWidth / width;
27658                     
27659                     if(width < height){
27660                         scale = targetHeight / height;
27661                     }
27662                 }
27663                 
27664                 context.scale(scale, scale);
27665                 
27666                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27667                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27668
27669                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27670                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27671
27672                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27673                 
27674                 break;
27675             case 90 : 
27676                 
27677                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27678                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27679                 
27680                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27681                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27682                 
27683                 var targetWidth = this.minWidth - 2 * x;
27684                 var targetHeight = this.minHeight - 2 * y;
27685                 
27686                 var scale = 1;
27687                 
27688                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27689                     scale = targetWidth / width;
27690                 }
27691                 
27692                 if(x > 0 && y == 0){
27693                     scale = targetHeight / height;
27694                 }
27695                 
27696                 if(x > 0 && y > 0){
27697                     scale = targetWidth / width;
27698                     
27699                     if(width < height){
27700                         scale = targetHeight / height;
27701                     }
27702                 }
27703                 
27704                 context.scale(scale, scale);
27705                 
27706                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27707                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27708
27709                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27710                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27711                 
27712                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27713                 
27714                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27715                 
27716                 break;
27717             case 180 :
27718                 
27719                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27720                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27721                 
27722                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27723                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27724                 
27725                 var targetWidth = this.minWidth - 2 * x;
27726                 var targetHeight = this.minHeight - 2 * y;
27727                 
27728                 var scale = 1;
27729                 
27730                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27731                     scale = targetWidth / width;
27732                 }
27733                 
27734                 if(x > 0 && y == 0){
27735                     scale = targetHeight / height;
27736                 }
27737                 
27738                 if(x > 0 && y > 0){
27739                     scale = targetWidth / width;
27740                     
27741                     if(width < height){
27742                         scale = targetHeight / height;
27743                     }
27744                 }
27745                 
27746                 context.scale(scale, scale);
27747                 
27748                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27749                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27750
27751                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27752                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27753
27754                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27755                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27756                 
27757                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27758                 
27759                 break;
27760             case 270 :
27761                 
27762                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27763                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27764                 
27765                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27766                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27767                 
27768                 var targetWidth = this.minWidth - 2 * x;
27769                 var targetHeight = this.minHeight - 2 * y;
27770                 
27771                 var scale = 1;
27772                 
27773                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27774                     scale = targetWidth / width;
27775                 }
27776                 
27777                 if(x > 0 && y == 0){
27778                     scale = targetHeight / height;
27779                 }
27780                 
27781                 if(x > 0 && y > 0){
27782                     scale = targetWidth / width;
27783                     
27784                     if(width < height){
27785                         scale = targetHeight / height;
27786                     }
27787                 }
27788                 
27789                 context.scale(scale, scale);
27790                 
27791                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27792                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27793
27794                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27795                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27796                 
27797                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27798                 
27799                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27800                 
27801                 break;
27802             default : 
27803                 break;
27804         }
27805         
27806         this.cropData = canvas.toDataURL(this.cropType);
27807         
27808         if(this.fireEvent('crop', this, this.cropData) !== false){
27809             this.process(this.file, this.cropData);
27810         }
27811         
27812         return;
27813         
27814     },
27815     
27816     setThumbBoxSize : function()
27817     {
27818         var width, height;
27819         
27820         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27821             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27822             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27823             
27824             this.minWidth = width;
27825             this.minHeight = height;
27826             
27827             if(this.rotate == 90 || this.rotate == 270){
27828                 this.minWidth = height;
27829                 this.minHeight = width;
27830             }
27831         }
27832         
27833         height = 300;
27834         width = Math.ceil(this.minWidth * height / this.minHeight);
27835         
27836         if(this.minWidth > this.minHeight){
27837             width = 300;
27838             height = Math.ceil(this.minHeight * width / this.minWidth);
27839         }
27840         
27841         this.thumbEl.setStyle({
27842             width : width + 'px',
27843             height : height + 'px'
27844         });
27845
27846         return;
27847             
27848     },
27849     
27850     setThumbBoxPosition : function()
27851     {
27852         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27853         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27854         
27855         this.thumbEl.setLeft(x);
27856         this.thumbEl.setTop(y);
27857         
27858     },
27859     
27860     baseRotateLevel : function()
27861     {
27862         this.baseRotate = 1;
27863         
27864         if(
27865                 typeof(this.exif) != 'undefined' &&
27866                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27867                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27868         ){
27869             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27870         }
27871         
27872         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27873         
27874     },
27875     
27876     baseScaleLevel : function()
27877     {
27878         var width, height;
27879         
27880         if(this.isDocument){
27881             
27882             if(this.baseRotate == 6 || this.baseRotate == 8){
27883             
27884                 height = this.thumbEl.getHeight();
27885                 this.baseScale = height / this.imageEl.OriginWidth;
27886
27887                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27888                     width = this.thumbEl.getWidth();
27889                     this.baseScale = width / this.imageEl.OriginHeight;
27890                 }
27891
27892                 return;
27893             }
27894
27895             height = this.thumbEl.getHeight();
27896             this.baseScale = height / this.imageEl.OriginHeight;
27897
27898             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27899                 width = this.thumbEl.getWidth();
27900                 this.baseScale = width / this.imageEl.OriginWidth;
27901             }
27902
27903             return;
27904         }
27905         
27906         if(this.baseRotate == 6 || this.baseRotate == 8){
27907             
27908             width = this.thumbEl.getHeight();
27909             this.baseScale = width / this.imageEl.OriginHeight;
27910             
27911             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27912                 height = this.thumbEl.getWidth();
27913                 this.baseScale = height / this.imageEl.OriginHeight;
27914             }
27915             
27916             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27917                 height = this.thumbEl.getWidth();
27918                 this.baseScale = height / this.imageEl.OriginHeight;
27919                 
27920                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27921                     width = this.thumbEl.getHeight();
27922                     this.baseScale = width / this.imageEl.OriginWidth;
27923                 }
27924             }
27925             
27926             return;
27927         }
27928         
27929         width = this.thumbEl.getWidth();
27930         this.baseScale = width / this.imageEl.OriginWidth;
27931         
27932         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27933             height = this.thumbEl.getHeight();
27934             this.baseScale = height / this.imageEl.OriginHeight;
27935         }
27936         
27937         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27938             
27939             height = this.thumbEl.getHeight();
27940             this.baseScale = height / this.imageEl.OriginHeight;
27941             
27942             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27943                 width = this.thumbEl.getWidth();
27944                 this.baseScale = width / this.imageEl.OriginWidth;
27945             }
27946             
27947         }
27948         
27949         return;
27950     },
27951     
27952     getScaleLevel : function()
27953     {
27954         return this.baseScale * Math.pow(1.1, this.scale);
27955     },
27956     
27957     onTouchStart : function(e)
27958     {
27959         if(!this.canvasLoaded){
27960             this.beforeSelectFile(e);
27961             return;
27962         }
27963         
27964         var touches = e.browserEvent.touches;
27965         
27966         if(!touches){
27967             return;
27968         }
27969         
27970         if(touches.length == 1){
27971             this.onMouseDown(e);
27972             return;
27973         }
27974         
27975         if(touches.length != 2){
27976             return;
27977         }
27978         
27979         var coords = [];
27980         
27981         for(var i = 0, finger; finger = touches[i]; i++){
27982             coords.push(finger.pageX, finger.pageY);
27983         }
27984         
27985         var x = Math.pow(coords[0] - coords[2], 2);
27986         var y = Math.pow(coords[1] - coords[3], 2);
27987         
27988         this.startDistance = Math.sqrt(x + y);
27989         
27990         this.startScale = this.scale;
27991         
27992         this.pinching = true;
27993         this.dragable = false;
27994         
27995     },
27996     
27997     onTouchMove : function(e)
27998     {
27999         if(!this.pinching && !this.dragable){
28000             return;
28001         }
28002         
28003         var touches = e.browserEvent.touches;
28004         
28005         if(!touches){
28006             return;
28007         }
28008         
28009         if(this.dragable){
28010             this.onMouseMove(e);
28011             return;
28012         }
28013         
28014         var coords = [];
28015         
28016         for(var i = 0, finger; finger = touches[i]; i++){
28017             coords.push(finger.pageX, finger.pageY);
28018         }
28019         
28020         var x = Math.pow(coords[0] - coords[2], 2);
28021         var y = Math.pow(coords[1] - coords[3], 2);
28022         
28023         this.endDistance = Math.sqrt(x + y);
28024         
28025         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28026         
28027         if(!this.zoomable()){
28028             this.scale = this.startScale;
28029             return;
28030         }
28031         
28032         this.draw();
28033         
28034     },
28035     
28036     onTouchEnd : function(e)
28037     {
28038         this.pinching = false;
28039         this.dragable = false;
28040         
28041     },
28042     
28043     process : function(file, crop)
28044     {
28045         if(this.loadMask){
28046             this.maskEl.mask(this.loadingText);
28047         }
28048         
28049         this.xhr = new XMLHttpRequest();
28050         
28051         file.xhr = this.xhr;
28052
28053         this.xhr.open(this.method, this.url, true);
28054         
28055         var headers = {
28056             "Accept": "application/json",
28057             "Cache-Control": "no-cache",
28058             "X-Requested-With": "XMLHttpRequest"
28059         };
28060         
28061         for (var headerName in headers) {
28062             var headerValue = headers[headerName];
28063             if (headerValue) {
28064                 this.xhr.setRequestHeader(headerName, headerValue);
28065             }
28066         }
28067         
28068         var _this = this;
28069         
28070         this.xhr.onload = function()
28071         {
28072             _this.xhrOnLoad(_this.xhr);
28073         }
28074         
28075         this.xhr.onerror = function()
28076         {
28077             _this.xhrOnError(_this.xhr);
28078         }
28079         
28080         var formData = new FormData();
28081
28082         formData.append('returnHTML', 'NO');
28083         
28084         if(crop){
28085             formData.append('crop', crop);
28086         }
28087         
28088         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28089             formData.append(this.paramName, file, file.name);
28090         }
28091         
28092         if(typeof(file.filename) != 'undefined'){
28093             formData.append('filename', file.filename);
28094         }
28095         
28096         if(typeof(file.mimetype) != 'undefined'){
28097             formData.append('mimetype', file.mimetype);
28098         }
28099         
28100         if(this.fireEvent('arrange', this, formData) != false){
28101             this.xhr.send(formData);
28102         };
28103     },
28104     
28105     xhrOnLoad : function(xhr)
28106     {
28107         if(this.loadMask){
28108             this.maskEl.unmask();
28109         }
28110         
28111         if (xhr.readyState !== 4) {
28112             this.fireEvent('exception', this, xhr);
28113             return;
28114         }
28115
28116         var response = Roo.decode(xhr.responseText);
28117         
28118         if(!response.success){
28119             this.fireEvent('exception', this, xhr);
28120             return;
28121         }
28122         
28123         var response = Roo.decode(xhr.responseText);
28124         
28125         this.fireEvent('upload', this, response);
28126         
28127     },
28128     
28129     xhrOnError : function()
28130     {
28131         if(this.loadMask){
28132             this.maskEl.unmask();
28133         }
28134         
28135         Roo.log('xhr on error');
28136         
28137         var response = Roo.decode(xhr.responseText);
28138           
28139         Roo.log(response);
28140         
28141     },
28142     
28143     prepare : function(file)
28144     {   
28145         if(this.loadMask){
28146             this.maskEl.mask(this.loadingText);
28147         }
28148         
28149         this.file = false;
28150         this.exif = {};
28151         
28152         if(typeof(file) === 'string'){
28153             this.loadCanvas(file);
28154             return;
28155         }
28156         
28157         if(!file || !this.urlAPI){
28158             return;
28159         }
28160         
28161         this.file = file;
28162         this.cropType = file.type;
28163         
28164         var _this = this;
28165         
28166         if(this.fireEvent('prepare', this, this.file) != false){
28167             
28168             var reader = new FileReader();
28169             
28170             reader.onload = function (e) {
28171                 if (e.target.error) {
28172                     Roo.log(e.target.error);
28173                     return;
28174                 }
28175                 
28176                 var buffer = e.target.result,
28177                     dataView = new DataView(buffer),
28178                     offset = 2,
28179                     maxOffset = dataView.byteLength - 4,
28180                     markerBytes,
28181                     markerLength;
28182                 
28183                 if (dataView.getUint16(0) === 0xffd8) {
28184                     while (offset < maxOffset) {
28185                         markerBytes = dataView.getUint16(offset);
28186                         
28187                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28188                             markerLength = dataView.getUint16(offset + 2) + 2;
28189                             if (offset + markerLength > dataView.byteLength) {
28190                                 Roo.log('Invalid meta data: Invalid segment size.');
28191                                 break;
28192                             }
28193                             
28194                             if(markerBytes == 0xffe1){
28195                                 _this.parseExifData(
28196                                     dataView,
28197                                     offset,
28198                                     markerLength
28199                                 );
28200                             }
28201                             
28202                             offset += markerLength;
28203                             
28204                             continue;
28205                         }
28206                         
28207                         break;
28208                     }
28209                     
28210                 }
28211                 
28212                 var url = _this.urlAPI.createObjectURL(_this.file);
28213                 
28214                 _this.loadCanvas(url);
28215                 
28216                 return;
28217             }
28218             
28219             reader.readAsArrayBuffer(this.file);
28220             
28221         }
28222         
28223     },
28224     
28225     parseExifData : function(dataView, offset, length)
28226     {
28227         var tiffOffset = offset + 10,
28228             littleEndian,
28229             dirOffset;
28230     
28231         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28232             // No Exif data, might be XMP data instead
28233             return;
28234         }
28235         
28236         // Check for the ASCII code for "Exif" (0x45786966):
28237         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28238             // No Exif data, might be XMP data instead
28239             return;
28240         }
28241         if (tiffOffset + 8 > dataView.byteLength) {
28242             Roo.log('Invalid Exif data: Invalid segment size.');
28243             return;
28244         }
28245         // Check for the two null bytes:
28246         if (dataView.getUint16(offset + 8) !== 0x0000) {
28247             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28248             return;
28249         }
28250         // Check the byte alignment:
28251         switch (dataView.getUint16(tiffOffset)) {
28252         case 0x4949:
28253             littleEndian = true;
28254             break;
28255         case 0x4D4D:
28256             littleEndian = false;
28257             break;
28258         default:
28259             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28260             return;
28261         }
28262         // Check for the TIFF tag marker (0x002A):
28263         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28264             Roo.log('Invalid Exif data: Missing TIFF marker.');
28265             return;
28266         }
28267         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28268         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28269         
28270         this.parseExifTags(
28271             dataView,
28272             tiffOffset,
28273             tiffOffset + dirOffset,
28274             littleEndian
28275         );
28276     },
28277     
28278     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28279     {
28280         var tagsNumber,
28281             dirEndOffset,
28282             i;
28283         if (dirOffset + 6 > dataView.byteLength) {
28284             Roo.log('Invalid Exif data: Invalid directory offset.');
28285             return;
28286         }
28287         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28288         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28289         if (dirEndOffset + 4 > dataView.byteLength) {
28290             Roo.log('Invalid Exif data: Invalid directory size.');
28291             return;
28292         }
28293         for (i = 0; i < tagsNumber; i += 1) {
28294             this.parseExifTag(
28295                 dataView,
28296                 tiffOffset,
28297                 dirOffset + 2 + 12 * i, // tag offset
28298                 littleEndian
28299             );
28300         }
28301         // Return the offset to the next directory:
28302         return dataView.getUint32(dirEndOffset, littleEndian);
28303     },
28304     
28305     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28306     {
28307         var tag = dataView.getUint16(offset, littleEndian);
28308         
28309         this.exif[tag] = this.getExifValue(
28310             dataView,
28311             tiffOffset,
28312             offset,
28313             dataView.getUint16(offset + 2, littleEndian), // tag type
28314             dataView.getUint32(offset + 4, littleEndian), // tag length
28315             littleEndian
28316         );
28317     },
28318     
28319     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28320     {
28321         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28322             tagSize,
28323             dataOffset,
28324             values,
28325             i,
28326             str,
28327             c;
28328     
28329         if (!tagType) {
28330             Roo.log('Invalid Exif data: Invalid tag type.');
28331             return;
28332         }
28333         
28334         tagSize = tagType.size * length;
28335         // Determine if the value is contained in the dataOffset bytes,
28336         // or if the value at the dataOffset is a pointer to the actual data:
28337         dataOffset = tagSize > 4 ?
28338                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28339         if (dataOffset + tagSize > dataView.byteLength) {
28340             Roo.log('Invalid Exif data: Invalid data offset.');
28341             return;
28342         }
28343         if (length === 1) {
28344             return tagType.getValue(dataView, dataOffset, littleEndian);
28345         }
28346         values = [];
28347         for (i = 0; i < length; i += 1) {
28348             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28349         }
28350         
28351         if (tagType.ascii) {
28352             str = '';
28353             // Concatenate the chars:
28354             for (i = 0; i < values.length; i += 1) {
28355                 c = values[i];
28356                 // Ignore the terminating NULL byte(s):
28357                 if (c === '\u0000') {
28358                     break;
28359                 }
28360                 str += c;
28361             }
28362             return str;
28363         }
28364         return values;
28365     }
28366     
28367 });
28368
28369 Roo.apply(Roo.bootstrap.UploadCropbox, {
28370     tags : {
28371         'Orientation': 0x0112
28372     },
28373     
28374     Orientation: {
28375             1: 0, //'top-left',
28376 //            2: 'top-right',
28377             3: 180, //'bottom-right',
28378 //            4: 'bottom-left',
28379 //            5: 'left-top',
28380             6: 90, //'right-top',
28381 //            7: 'right-bottom',
28382             8: 270 //'left-bottom'
28383     },
28384     
28385     exifTagTypes : {
28386         // byte, 8-bit unsigned int:
28387         1: {
28388             getValue: function (dataView, dataOffset) {
28389                 return dataView.getUint8(dataOffset);
28390             },
28391             size: 1
28392         },
28393         // ascii, 8-bit byte:
28394         2: {
28395             getValue: function (dataView, dataOffset) {
28396                 return String.fromCharCode(dataView.getUint8(dataOffset));
28397             },
28398             size: 1,
28399             ascii: true
28400         },
28401         // short, 16 bit int:
28402         3: {
28403             getValue: function (dataView, dataOffset, littleEndian) {
28404                 return dataView.getUint16(dataOffset, littleEndian);
28405             },
28406             size: 2
28407         },
28408         // long, 32 bit int:
28409         4: {
28410             getValue: function (dataView, dataOffset, littleEndian) {
28411                 return dataView.getUint32(dataOffset, littleEndian);
28412             },
28413             size: 4
28414         },
28415         // rational = two long values, first is numerator, second is denominator:
28416         5: {
28417             getValue: function (dataView, dataOffset, littleEndian) {
28418                 return dataView.getUint32(dataOffset, littleEndian) /
28419                     dataView.getUint32(dataOffset + 4, littleEndian);
28420             },
28421             size: 8
28422         },
28423         // slong, 32 bit signed int:
28424         9: {
28425             getValue: function (dataView, dataOffset, littleEndian) {
28426                 return dataView.getInt32(dataOffset, littleEndian);
28427             },
28428             size: 4
28429         },
28430         // srational, two slongs, first is numerator, second is denominator:
28431         10: {
28432             getValue: function (dataView, dataOffset, littleEndian) {
28433                 return dataView.getInt32(dataOffset, littleEndian) /
28434                     dataView.getInt32(dataOffset + 4, littleEndian);
28435             },
28436             size: 8
28437         }
28438     },
28439     
28440     footer : {
28441         STANDARD : [
28442             {
28443                 tag : 'div',
28444                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28445                 action : 'rotate-left',
28446                 cn : [
28447                     {
28448                         tag : 'button',
28449                         cls : 'btn btn-default',
28450                         html : '<i class="fa fa-undo"></i>'
28451                     }
28452                 ]
28453             },
28454             {
28455                 tag : 'div',
28456                 cls : 'btn-group roo-upload-cropbox-picture',
28457                 action : 'picture',
28458                 cn : [
28459                     {
28460                         tag : 'button',
28461                         cls : 'btn btn-default',
28462                         html : '<i class="fa fa-picture-o"></i>'
28463                     }
28464                 ]
28465             },
28466             {
28467                 tag : 'div',
28468                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28469                 action : 'rotate-right',
28470                 cn : [
28471                     {
28472                         tag : 'button',
28473                         cls : 'btn btn-default',
28474                         html : '<i class="fa fa-repeat"></i>'
28475                     }
28476                 ]
28477             }
28478         ],
28479         DOCUMENT : [
28480             {
28481                 tag : 'div',
28482                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28483                 action : 'rotate-left',
28484                 cn : [
28485                     {
28486                         tag : 'button',
28487                         cls : 'btn btn-default',
28488                         html : '<i class="fa fa-undo"></i>'
28489                     }
28490                 ]
28491             },
28492             {
28493                 tag : 'div',
28494                 cls : 'btn-group roo-upload-cropbox-download',
28495                 action : 'download',
28496                 cn : [
28497                     {
28498                         tag : 'button',
28499                         cls : 'btn btn-default',
28500                         html : '<i class="fa fa-download"></i>'
28501                     }
28502                 ]
28503             },
28504             {
28505                 tag : 'div',
28506                 cls : 'btn-group roo-upload-cropbox-crop',
28507                 action : 'crop',
28508                 cn : [
28509                     {
28510                         tag : 'button',
28511                         cls : 'btn btn-default',
28512                         html : '<i class="fa fa-crop"></i>'
28513                     }
28514                 ]
28515             },
28516             {
28517                 tag : 'div',
28518                 cls : 'btn-group roo-upload-cropbox-trash',
28519                 action : 'trash',
28520                 cn : [
28521                     {
28522                         tag : 'button',
28523                         cls : 'btn btn-default',
28524                         html : '<i class="fa fa-trash"></i>'
28525                     }
28526                 ]
28527             },
28528             {
28529                 tag : 'div',
28530                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28531                 action : 'rotate-right',
28532                 cn : [
28533                     {
28534                         tag : 'button',
28535                         cls : 'btn btn-default',
28536                         html : '<i class="fa fa-repeat"></i>'
28537                     }
28538                 ]
28539             }
28540         ],
28541         ROTATOR : [
28542             {
28543                 tag : 'div',
28544                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28545                 action : 'rotate-left',
28546                 cn : [
28547                     {
28548                         tag : 'button',
28549                         cls : 'btn btn-default',
28550                         html : '<i class="fa fa-undo"></i>'
28551                     }
28552                 ]
28553             },
28554             {
28555                 tag : 'div',
28556                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28557                 action : 'rotate-right',
28558                 cn : [
28559                     {
28560                         tag : 'button',
28561                         cls : 'btn btn-default',
28562                         html : '<i class="fa fa-repeat"></i>'
28563                     }
28564                 ]
28565             }
28566         ]
28567     }
28568 });
28569
28570 /*
28571 * Licence: LGPL
28572 */
28573
28574 /**
28575  * @class Roo.bootstrap.DocumentManager
28576  * @extends Roo.bootstrap.Component
28577  * Bootstrap DocumentManager class
28578  * @cfg {String} paramName default 'imageUpload'
28579  * @cfg {String} toolTipName default 'filename'
28580  * @cfg {String} method default POST
28581  * @cfg {String} url action url
28582  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28583  * @cfg {Boolean} multiple multiple upload default true
28584  * @cfg {Number} thumbSize default 300
28585  * @cfg {String} fieldLabel
28586  * @cfg {Number} labelWidth default 4
28587  * @cfg {String} labelAlign (left|top) default left
28588  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28589 * @cfg {Number} labellg set the width of label (1-12)
28590  * @cfg {Number} labelmd set the width of label (1-12)
28591  * @cfg {Number} labelsm set the width of label (1-12)
28592  * @cfg {Number} labelxs set the width of label (1-12)
28593  * 
28594  * @constructor
28595  * Create a new DocumentManager
28596  * @param {Object} config The config object
28597  */
28598
28599 Roo.bootstrap.DocumentManager = function(config){
28600     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28601     
28602     this.files = [];
28603     this.delegates = [];
28604     
28605     this.addEvents({
28606         /**
28607          * @event initial
28608          * Fire when initial the DocumentManager
28609          * @param {Roo.bootstrap.DocumentManager} this
28610          */
28611         "initial" : true,
28612         /**
28613          * @event inspect
28614          * inspect selected file
28615          * @param {Roo.bootstrap.DocumentManager} this
28616          * @param {File} file
28617          */
28618         "inspect" : true,
28619         /**
28620          * @event exception
28621          * Fire when xhr load exception
28622          * @param {Roo.bootstrap.DocumentManager} this
28623          * @param {XMLHttpRequest} xhr
28624          */
28625         "exception" : true,
28626         /**
28627          * @event afterupload
28628          * Fire when xhr load exception
28629          * @param {Roo.bootstrap.DocumentManager} this
28630          * @param {XMLHttpRequest} xhr
28631          */
28632         "afterupload" : true,
28633         /**
28634          * @event prepare
28635          * prepare the form data
28636          * @param {Roo.bootstrap.DocumentManager} this
28637          * @param {Object} formData
28638          */
28639         "prepare" : true,
28640         /**
28641          * @event remove
28642          * Fire when remove the file
28643          * @param {Roo.bootstrap.DocumentManager} this
28644          * @param {Object} file
28645          */
28646         "remove" : true,
28647         /**
28648          * @event refresh
28649          * Fire after refresh the file
28650          * @param {Roo.bootstrap.DocumentManager} this
28651          */
28652         "refresh" : true,
28653         /**
28654          * @event click
28655          * Fire after click the image
28656          * @param {Roo.bootstrap.DocumentManager} this
28657          * @param {Object} file
28658          */
28659         "click" : true,
28660         /**
28661          * @event edit
28662          * Fire when upload a image and editable set to true
28663          * @param {Roo.bootstrap.DocumentManager} this
28664          * @param {Object} file
28665          */
28666         "edit" : true,
28667         /**
28668          * @event beforeselectfile
28669          * Fire before select file
28670          * @param {Roo.bootstrap.DocumentManager} this
28671          */
28672         "beforeselectfile" : true,
28673         /**
28674          * @event process
28675          * Fire before process file
28676          * @param {Roo.bootstrap.DocumentManager} this
28677          * @param {Object} file
28678          */
28679         "process" : true,
28680         /**
28681          * @event previewrendered
28682          * Fire when preview rendered
28683          * @param {Roo.bootstrap.DocumentManager} this
28684          * @param {Object} file
28685          */
28686         "previewrendered" : true
28687         
28688     });
28689 };
28690
28691 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28692     
28693     boxes : 0,
28694     inputName : '',
28695     thumbSize : 300,
28696     multiple : true,
28697     files : false,
28698     method : 'POST',
28699     url : '',
28700     paramName : 'imageUpload',
28701     toolTipName : 'filename',
28702     fieldLabel : '',
28703     labelWidth : 4,
28704     labelAlign : 'left',
28705     editable : true,
28706     delegates : false,
28707     xhr : false, 
28708     
28709     labellg : 0,
28710     labelmd : 0,
28711     labelsm : 0,
28712     labelxs : 0,
28713     
28714     getAutoCreate : function()
28715     {   
28716         var managerWidget = {
28717             tag : 'div',
28718             cls : 'roo-document-manager',
28719             cn : [
28720                 {
28721                     tag : 'input',
28722                     cls : 'roo-document-manager-selector',
28723                     type : 'file'
28724                 },
28725                 {
28726                     tag : 'div',
28727                     cls : 'roo-document-manager-uploader',
28728                     cn : [
28729                         {
28730                             tag : 'div',
28731                             cls : 'roo-document-manager-upload-btn',
28732                             html : '<i class="fa fa-plus"></i>'
28733                         }
28734                     ]
28735                     
28736                 }
28737             ]
28738         };
28739         
28740         var content = [
28741             {
28742                 tag : 'div',
28743                 cls : 'column col-md-12',
28744                 cn : managerWidget
28745             }
28746         ];
28747         
28748         if(this.fieldLabel.length){
28749             
28750             content = [
28751                 {
28752                     tag : 'div',
28753                     cls : 'column col-md-12',
28754                     html : this.fieldLabel
28755                 },
28756                 {
28757                     tag : 'div',
28758                     cls : 'column col-md-12',
28759                     cn : managerWidget
28760                 }
28761             ];
28762
28763             if(this.labelAlign == 'left'){
28764                 content = [
28765                     {
28766                         tag : 'div',
28767                         cls : 'column',
28768                         html : this.fieldLabel
28769                     },
28770                     {
28771                         tag : 'div',
28772                         cls : 'column',
28773                         cn : managerWidget
28774                     }
28775                 ];
28776                 
28777                 if(this.labelWidth > 12){
28778                     content[0].style = "width: " + this.labelWidth + 'px';
28779                 }
28780
28781                 if(this.labelWidth < 13 && this.labelmd == 0){
28782                     this.labelmd = this.labelWidth;
28783                 }
28784
28785                 if(this.labellg > 0){
28786                     content[0].cls += ' col-lg-' + this.labellg;
28787                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28788                 }
28789
28790                 if(this.labelmd > 0){
28791                     content[0].cls += ' col-md-' + this.labelmd;
28792                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28793                 }
28794
28795                 if(this.labelsm > 0){
28796                     content[0].cls += ' col-sm-' + this.labelsm;
28797                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28798                 }
28799
28800                 if(this.labelxs > 0){
28801                     content[0].cls += ' col-xs-' + this.labelxs;
28802                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28803                 }
28804                 
28805             }
28806         }
28807         
28808         var cfg = {
28809             tag : 'div',
28810             cls : 'row clearfix',
28811             cn : content
28812         };
28813         
28814         return cfg;
28815         
28816     },
28817     
28818     initEvents : function()
28819     {
28820         this.managerEl = this.el.select('.roo-document-manager', true).first();
28821         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28822         
28823         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28824         this.selectorEl.hide();
28825         
28826         if(this.multiple){
28827             this.selectorEl.attr('multiple', 'multiple');
28828         }
28829         
28830         this.selectorEl.on('change', this.onFileSelected, this);
28831         
28832         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28833         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28834         
28835         this.uploader.on('click', this.onUploaderClick, this);
28836         
28837         this.renderProgressDialog();
28838         
28839         var _this = this;
28840         
28841         window.addEventListener("resize", function() { _this.refresh(); } );
28842         
28843         this.fireEvent('initial', this);
28844     },
28845     
28846     renderProgressDialog : function()
28847     {
28848         var _this = this;
28849         
28850         this.progressDialog = new Roo.bootstrap.Modal({
28851             cls : 'roo-document-manager-progress-dialog',
28852             allow_close : false,
28853             title : '',
28854             buttons : [
28855                 {
28856                     name  :'cancel',
28857                     weight : 'danger',
28858                     html : 'Cancel'
28859                 }
28860             ], 
28861             listeners : { 
28862                 btnclick : function() {
28863                     _this.uploadCancel();
28864                     this.hide();
28865                 }
28866             }
28867         });
28868          
28869         this.progressDialog.render(Roo.get(document.body));
28870          
28871         this.progress = new Roo.bootstrap.Progress({
28872             cls : 'roo-document-manager-progress',
28873             active : true,
28874             striped : true
28875         });
28876         
28877         this.progress.render(this.progressDialog.getChildContainer());
28878         
28879         this.progressBar = new Roo.bootstrap.ProgressBar({
28880             cls : 'roo-document-manager-progress-bar',
28881             aria_valuenow : 0,
28882             aria_valuemin : 0,
28883             aria_valuemax : 12,
28884             panel : 'success'
28885         });
28886         
28887         this.progressBar.render(this.progress.getChildContainer());
28888     },
28889     
28890     onUploaderClick : function(e)
28891     {
28892         e.preventDefault();
28893      
28894         if(this.fireEvent('beforeselectfile', this) != false){
28895             this.selectorEl.dom.click();
28896         }
28897         
28898     },
28899     
28900     onFileSelected : function(e)
28901     {
28902         e.preventDefault();
28903         
28904         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28905             return;
28906         }
28907         
28908         Roo.each(this.selectorEl.dom.files, function(file){
28909             if(this.fireEvent('inspect', this, file) != false){
28910                 this.files.push(file);
28911             }
28912         }, this);
28913         
28914         this.queue();
28915         
28916     },
28917     
28918     queue : function()
28919     {
28920         this.selectorEl.dom.value = '';
28921         
28922         if(!this.files || !this.files.length){
28923             return;
28924         }
28925         
28926         if(this.boxes > 0 && this.files.length > this.boxes){
28927             this.files = this.files.slice(0, this.boxes);
28928         }
28929         
28930         this.uploader.show();
28931         
28932         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28933             this.uploader.hide();
28934         }
28935         
28936         var _this = this;
28937         
28938         var files = [];
28939         
28940         var docs = [];
28941         
28942         Roo.each(this.files, function(file){
28943             
28944             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28945                 var f = this.renderPreview(file);
28946                 files.push(f);
28947                 return;
28948             }
28949             
28950             if(file.type.indexOf('image') != -1){
28951                 this.delegates.push(
28952                     (function(){
28953                         _this.process(file);
28954                     }).createDelegate(this)
28955                 );
28956         
28957                 return;
28958             }
28959             
28960             docs.push(
28961                 (function(){
28962                     _this.process(file);
28963                 }).createDelegate(this)
28964             );
28965             
28966         }, this);
28967         
28968         this.files = files;
28969         
28970         this.delegates = this.delegates.concat(docs);
28971         
28972         if(!this.delegates.length){
28973             this.refresh();
28974             return;
28975         }
28976         
28977         this.progressBar.aria_valuemax = this.delegates.length;
28978         
28979         this.arrange();
28980         
28981         return;
28982     },
28983     
28984     arrange : function()
28985     {
28986         if(!this.delegates.length){
28987             this.progressDialog.hide();
28988             this.refresh();
28989             return;
28990         }
28991         
28992         var delegate = this.delegates.shift();
28993         
28994         this.progressDialog.show();
28995         
28996         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28997         
28998         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28999         
29000         delegate();
29001     },
29002     
29003     refresh : function()
29004     {
29005         this.uploader.show();
29006         
29007         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29008             this.uploader.hide();
29009         }
29010         
29011         Roo.isTouch ? this.closable(false) : this.closable(true);
29012         
29013         this.fireEvent('refresh', this);
29014     },
29015     
29016     onRemove : function(e, el, o)
29017     {
29018         e.preventDefault();
29019         
29020         this.fireEvent('remove', this, o);
29021         
29022     },
29023     
29024     remove : function(o)
29025     {
29026         var files = [];
29027         
29028         Roo.each(this.files, function(file){
29029             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29030                 files.push(file);
29031                 return;
29032             }
29033
29034             o.target.remove();
29035
29036         }, this);
29037         
29038         this.files = files;
29039         
29040         this.refresh();
29041     },
29042     
29043     clear : function()
29044     {
29045         Roo.each(this.files, function(file){
29046             if(!file.target){
29047                 return;
29048             }
29049             
29050             file.target.remove();
29051
29052         }, this);
29053         
29054         this.files = [];
29055         
29056         this.refresh();
29057     },
29058     
29059     onClick : function(e, el, o)
29060     {
29061         e.preventDefault();
29062         
29063         this.fireEvent('click', this, o);
29064         
29065     },
29066     
29067     closable : function(closable)
29068     {
29069         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29070             
29071             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29072             
29073             if(closable){
29074                 el.show();
29075                 return;
29076             }
29077             
29078             el.hide();
29079             
29080         }, this);
29081     },
29082     
29083     xhrOnLoad : function(xhr)
29084     {
29085         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29086             el.remove();
29087         }, this);
29088         
29089         if (xhr.readyState !== 4) {
29090             this.arrange();
29091             this.fireEvent('exception', this, xhr);
29092             return;
29093         }
29094
29095         var response = Roo.decode(xhr.responseText);
29096         
29097         if(!response.success){
29098             this.arrange();
29099             this.fireEvent('exception', this, xhr);
29100             return;
29101         }
29102         
29103         var file = this.renderPreview(response.data);
29104         
29105         this.files.push(file);
29106         
29107         this.arrange();
29108         
29109         this.fireEvent('afterupload', this, xhr);
29110         
29111     },
29112     
29113     xhrOnError : function(xhr)
29114     {
29115         Roo.log('xhr on error');
29116         
29117         var response = Roo.decode(xhr.responseText);
29118           
29119         Roo.log(response);
29120         
29121         this.arrange();
29122     },
29123     
29124     process : function(file)
29125     {
29126         if(this.fireEvent('process', this, file) !== false){
29127             if(this.editable && file.type.indexOf('image') != -1){
29128                 this.fireEvent('edit', this, file);
29129                 return;
29130             }
29131
29132             this.uploadStart(file, false);
29133
29134             return;
29135         }
29136         
29137     },
29138     
29139     uploadStart : function(file, crop)
29140     {
29141         this.xhr = new XMLHttpRequest();
29142         
29143         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29144             this.arrange();
29145             return;
29146         }
29147         
29148         file.xhr = this.xhr;
29149             
29150         this.managerEl.createChild({
29151             tag : 'div',
29152             cls : 'roo-document-manager-loading',
29153             cn : [
29154                 {
29155                     tag : 'div',
29156                     tooltip : file.name,
29157                     cls : 'roo-document-manager-thumb',
29158                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29159                 }
29160             ]
29161
29162         });
29163
29164         this.xhr.open(this.method, this.url, true);
29165         
29166         var headers = {
29167             "Accept": "application/json",
29168             "Cache-Control": "no-cache",
29169             "X-Requested-With": "XMLHttpRequest"
29170         };
29171         
29172         for (var headerName in headers) {
29173             var headerValue = headers[headerName];
29174             if (headerValue) {
29175                 this.xhr.setRequestHeader(headerName, headerValue);
29176             }
29177         }
29178         
29179         var _this = this;
29180         
29181         this.xhr.onload = function()
29182         {
29183             _this.xhrOnLoad(_this.xhr);
29184         }
29185         
29186         this.xhr.onerror = function()
29187         {
29188             _this.xhrOnError(_this.xhr);
29189         }
29190         
29191         var formData = new FormData();
29192
29193         formData.append('returnHTML', 'NO');
29194         
29195         if(crop){
29196             formData.append('crop', crop);
29197         }
29198         
29199         formData.append(this.paramName, file, file.name);
29200         
29201         var options = {
29202             file : file, 
29203             manually : false
29204         };
29205         
29206         if(this.fireEvent('prepare', this, formData, options) != false){
29207             
29208             if(options.manually){
29209                 return;
29210             }
29211             
29212             this.xhr.send(formData);
29213             return;
29214         };
29215         
29216         this.uploadCancel();
29217     },
29218     
29219     uploadCancel : function()
29220     {
29221         if (this.xhr) {
29222             this.xhr.abort();
29223         }
29224         
29225         this.delegates = [];
29226         
29227         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29228             el.remove();
29229         }, this);
29230         
29231         this.arrange();
29232     },
29233     
29234     renderPreview : function(file)
29235     {
29236         if(typeof(file.target) != 'undefined' && file.target){
29237             return file;
29238         }
29239         
29240         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29241         
29242         var previewEl = this.managerEl.createChild({
29243             tag : 'div',
29244             cls : 'roo-document-manager-preview',
29245             cn : [
29246                 {
29247                     tag : 'div',
29248                     tooltip : file[this.toolTipName],
29249                     cls : 'roo-document-manager-thumb',
29250                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29251                 },
29252                 {
29253                     tag : 'button',
29254                     cls : 'close',
29255                     html : '<i class="fa fa-times-circle"></i>'
29256                 }
29257             ]
29258         });
29259
29260         var close = previewEl.select('button.close', true).first();
29261
29262         close.on('click', this.onRemove, this, file);
29263
29264         file.target = previewEl;
29265
29266         var image = previewEl.select('img', true).first();
29267         
29268         var _this = this;
29269         
29270         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29271         
29272         image.on('click', this.onClick, this, file);
29273         
29274         this.fireEvent('previewrendered', this, file);
29275         
29276         return file;
29277         
29278     },
29279     
29280     onPreviewLoad : function(file, image)
29281     {
29282         if(typeof(file.target) == 'undefined' || !file.target){
29283             return;
29284         }
29285         
29286         var width = image.dom.naturalWidth || image.dom.width;
29287         var height = image.dom.naturalHeight || image.dom.height;
29288         
29289         if(width > height){
29290             file.target.addClass('wide');
29291             return;
29292         }
29293         
29294         file.target.addClass('tall');
29295         return;
29296         
29297     },
29298     
29299     uploadFromSource : function(file, crop)
29300     {
29301         this.xhr = new XMLHttpRequest();
29302         
29303         this.managerEl.createChild({
29304             tag : 'div',
29305             cls : 'roo-document-manager-loading',
29306             cn : [
29307                 {
29308                     tag : 'div',
29309                     tooltip : file.name,
29310                     cls : 'roo-document-manager-thumb',
29311                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29312                 }
29313             ]
29314
29315         });
29316
29317         this.xhr.open(this.method, this.url, true);
29318         
29319         var headers = {
29320             "Accept": "application/json",
29321             "Cache-Control": "no-cache",
29322             "X-Requested-With": "XMLHttpRequest"
29323         };
29324         
29325         for (var headerName in headers) {
29326             var headerValue = headers[headerName];
29327             if (headerValue) {
29328                 this.xhr.setRequestHeader(headerName, headerValue);
29329             }
29330         }
29331         
29332         var _this = this;
29333         
29334         this.xhr.onload = function()
29335         {
29336             _this.xhrOnLoad(_this.xhr);
29337         }
29338         
29339         this.xhr.onerror = function()
29340         {
29341             _this.xhrOnError(_this.xhr);
29342         }
29343         
29344         var formData = new FormData();
29345
29346         formData.append('returnHTML', 'NO');
29347         
29348         formData.append('crop', crop);
29349         
29350         if(typeof(file.filename) != 'undefined'){
29351             formData.append('filename', file.filename);
29352         }
29353         
29354         if(typeof(file.mimetype) != 'undefined'){
29355             formData.append('mimetype', file.mimetype);
29356         }
29357         
29358         Roo.log(formData);
29359         
29360         if(this.fireEvent('prepare', this, formData) != false){
29361             this.xhr.send(formData);
29362         };
29363     }
29364 });
29365
29366 /*
29367 * Licence: LGPL
29368 */
29369
29370 /**
29371  * @class Roo.bootstrap.DocumentViewer
29372  * @extends Roo.bootstrap.Component
29373  * Bootstrap DocumentViewer class
29374  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29375  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29376  * 
29377  * @constructor
29378  * Create a new DocumentViewer
29379  * @param {Object} config The config object
29380  */
29381
29382 Roo.bootstrap.DocumentViewer = function(config){
29383     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29384     
29385     this.addEvents({
29386         /**
29387          * @event initial
29388          * Fire after initEvent
29389          * @param {Roo.bootstrap.DocumentViewer} this
29390          */
29391         "initial" : true,
29392         /**
29393          * @event click
29394          * Fire after click
29395          * @param {Roo.bootstrap.DocumentViewer} this
29396          */
29397         "click" : true,
29398         /**
29399          * @event download
29400          * Fire after download button
29401          * @param {Roo.bootstrap.DocumentViewer} this
29402          */
29403         "download" : true,
29404         /**
29405          * @event trash
29406          * Fire after trash button
29407          * @param {Roo.bootstrap.DocumentViewer} this
29408          */
29409         "trash" : true
29410         
29411     });
29412 };
29413
29414 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29415     
29416     showDownload : true,
29417     
29418     showTrash : true,
29419     
29420     getAutoCreate : function()
29421     {
29422         var cfg = {
29423             tag : 'div',
29424             cls : 'roo-document-viewer',
29425             cn : [
29426                 {
29427                     tag : 'div',
29428                     cls : 'roo-document-viewer-body',
29429                     cn : [
29430                         {
29431                             tag : 'div',
29432                             cls : 'roo-document-viewer-thumb',
29433                             cn : [
29434                                 {
29435                                     tag : 'img',
29436                                     cls : 'roo-document-viewer-image'
29437                                 }
29438                             ]
29439                         }
29440                     ]
29441                 },
29442                 {
29443                     tag : 'div',
29444                     cls : 'roo-document-viewer-footer',
29445                     cn : {
29446                         tag : 'div',
29447                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29448                         cn : [
29449                             {
29450                                 tag : 'div',
29451                                 cls : 'btn-group roo-document-viewer-download',
29452                                 cn : [
29453                                     {
29454                                         tag : 'button',
29455                                         cls : 'btn btn-default',
29456                                         html : '<i class="fa fa-download"></i>'
29457                                     }
29458                                 ]
29459                             },
29460                             {
29461                                 tag : 'div',
29462                                 cls : 'btn-group roo-document-viewer-trash',
29463                                 cn : [
29464                                     {
29465                                         tag : 'button',
29466                                         cls : 'btn btn-default',
29467                                         html : '<i class="fa fa-trash"></i>'
29468                                     }
29469                                 ]
29470                             }
29471                         ]
29472                     }
29473                 }
29474             ]
29475         };
29476         
29477         return cfg;
29478     },
29479     
29480     initEvents : function()
29481     {
29482         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29483         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29484         
29485         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29486         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29487         
29488         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29489         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29490         
29491         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29492         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29493         
29494         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29495         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29496         
29497         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29498         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29499         
29500         this.bodyEl.on('click', this.onClick, this);
29501         this.downloadBtn.on('click', this.onDownload, this);
29502         this.trashBtn.on('click', this.onTrash, this);
29503         
29504         this.downloadBtn.hide();
29505         this.trashBtn.hide();
29506         
29507         if(this.showDownload){
29508             this.downloadBtn.show();
29509         }
29510         
29511         if(this.showTrash){
29512             this.trashBtn.show();
29513         }
29514         
29515         if(!this.showDownload && !this.showTrash) {
29516             this.footerEl.hide();
29517         }
29518         
29519     },
29520     
29521     initial : function()
29522     {
29523         this.fireEvent('initial', this);
29524         
29525     },
29526     
29527     onClick : function(e)
29528     {
29529         e.preventDefault();
29530         
29531         this.fireEvent('click', this);
29532     },
29533     
29534     onDownload : function(e)
29535     {
29536         e.preventDefault();
29537         
29538         this.fireEvent('download', this);
29539     },
29540     
29541     onTrash : function(e)
29542     {
29543         e.preventDefault();
29544         
29545         this.fireEvent('trash', this);
29546     }
29547     
29548 });
29549 /*
29550  * - LGPL
29551  *
29552  * nav progress bar
29553  * 
29554  */
29555
29556 /**
29557  * @class Roo.bootstrap.NavProgressBar
29558  * @extends Roo.bootstrap.Component
29559  * Bootstrap NavProgressBar class
29560  * 
29561  * @constructor
29562  * Create a new nav progress bar
29563  * @param {Object} config The config object
29564  */
29565
29566 Roo.bootstrap.NavProgressBar = function(config){
29567     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29568
29569     this.bullets = this.bullets || [];
29570    
29571 //    Roo.bootstrap.NavProgressBar.register(this);
29572      this.addEvents({
29573         /**
29574              * @event changed
29575              * Fires when the active item changes
29576              * @param {Roo.bootstrap.NavProgressBar} this
29577              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29578              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29579          */
29580         'changed': true
29581      });
29582     
29583 };
29584
29585 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29586     
29587     bullets : [],
29588     barItems : [],
29589     
29590     getAutoCreate : function()
29591     {
29592         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29593         
29594         cfg = {
29595             tag : 'div',
29596             cls : 'roo-navigation-bar-group',
29597             cn : [
29598                 {
29599                     tag : 'div',
29600                     cls : 'roo-navigation-top-bar'
29601                 },
29602                 {
29603                     tag : 'div',
29604                     cls : 'roo-navigation-bullets-bar',
29605                     cn : [
29606                         {
29607                             tag : 'ul',
29608                             cls : 'roo-navigation-bar'
29609                         }
29610                     ]
29611                 },
29612                 
29613                 {
29614                     tag : 'div',
29615                     cls : 'roo-navigation-bottom-bar'
29616                 }
29617             ]
29618             
29619         };
29620         
29621         return cfg;
29622         
29623     },
29624     
29625     initEvents: function() 
29626     {
29627         
29628     },
29629     
29630     onRender : function(ct, position) 
29631     {
29632         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29633         
29634         if(this.bullets.length){
29635             Roo.each(this.bullets, function(b){
29636                this.addItem(b);
29637             }, this);
29638         }
29639         
29640         this.format();
29641         
29642     },
29643     
29644     addItem : function(cfg)
29645     {
29646         var item = new Roo.bootstrap.NavProgressItem(cfg);
29647         
29648         item.parentId = this.id;
29649         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29650         
29651         if(cfg.html){
29652             var top = new Roo.bootstrap.Element({
29653                 tag : 'div',
29654                 cls : 'roo-navigation-bar-text'
29655             });
29656             
29657             var bottom = new Roo.bootstrap.Element({
29658                 tag : 'div',
29659                 cls : 'roo-navigation-bar-text'
29660             });
29661             
29662             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29663             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29664             
29665             var topText = new Roo.bootstrap.Element({
29666                 tag : 'span',
29667                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29668             });
29669             
29670             var bottomText = new Roo.bootstrap.Element({
29671                 tag : 'span',
29672                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29673             });
29674             
29675             topText.onRender(top.el, null);
29676             bottomText.onRender(bottom.el, null);
29677             
29678             item.topEl = top;
29679             item.bottomEl = bottom;
29680         }
29681         
29682         this.barItems.push(item);
29683         
29684         return item;
29685     },
29686     
29687     getActive : function()
29688     {
29689         var active = false;
29690         
29691         Roo.each(this.barItems, function(v){
29692             
29693             if (!v.isActive()) {
29694                 return;
29695             }
29696             
29697             active = v;
29698             return false;
29699             
29700         });
29701         
29702         return active;
29703     },
29704     
29705     setActiveItem : function(item)
29706     {
29707         var prev = false;
29708         
29709         Roo.each(this.barItems, function(v){
29710             if (v.rid == item.rid) {
29711                 return ;
29712             }
29713             
29714             if (v.isActive()) {
29715                 v.setActive(false);
29716                 prev = v;
29717             }
29718         });
29719
29720         item.setActive(true);
29721         
29722         this.fireEvent('changed', this, item, prev);
29723     },
29724     
29725     getBarItem: function(rid)
29726     {
29727         var ret = false;
29728         
29729         Roo.each(this.barItems, function(e) {
29730             if (e.rid != rid) {
29731                 return;
29732             }
29733             
29734             ret =  e;
29735             return false;
29736         });
29737         
29738         return ret;
29739     },
29740     
29741     indexOfItem : function(item)
29742     {
29743         var index = false;
29744         
29745         Roo.each(this.barItems, function(v, i){
29746             
29747             if (v.rid != item.rid) {
29748                 return;
29749             }
29750             
29751             index = i;
29752             return false
29753         });
29754         
29755         return index;
29756     },
29757     
29758     setActiveNext : function()
29759     {
29760         var i = this.indexOfItem(this.getActive());
29761         
29762         if (i > this.barItems.length) {
29763             return;
29764         }
29765         
29766         this.setActiveItem(this.barItems[i+1]);
29767     },
29768     
29769     setActivePrev : function()
29770     {
29771         var i = this.indexOfItem(this.getActive());
29772         
29773         if (i  < 1) {
29774             return;
29775         }
29776         
29777         this.setActiveItem(this.barItems[i-1]);
29778     },
29779     
29780     format : function()
29781     {
29782         if(!this.barItems.length){
29783             return;
29784         }
29785      
29786         var width = 100 / this.barItems.length;
29787         
29788         Roo.each(this.barItems, function(i){
29789             i.el.setStyle('width', width + '%');
29790             i.topEl.el.setStyle('width', width + '%');
29791             i.bottomEl.el.setStyle('width', width + '%');
29792         }, this);
29793         
29794     }
29795     
29796 });
29797 /*
29798  * - LGPL
29799  *
29800  * Nav Progress Item
29801  * 
29802  */
29803
29804 /**
29805  * @class Roo.bootstrap.NavProgressItem
29806  * @extends Roo.bootstrap.Component
29807  * Bootstrap NavProgressItem class
29808  * @cfg {String} rid the reference id
29809  * @cfg {Boolean} active (true|false) Is item active default false
29810  * @cfg {Boolean} disabled (true|false) Is item active default false
29811  * @cfg {String} html
29812  * @cfg {String} position (top|bottom) text position default bottom
29813  * @cfg {String} icon show icon instead of number
29814  * 
29815  * @constructor
29816  * Create a new NavProgressItem
29817  * @param {Object} config The config object
29818  */
29819 Roo.bootstrap.NavProgressItem = function(config){
29820     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29821     this.addEvents({
29822         // raw events
29823         /**
29824          * @event click
29825          * The raw click event for the entire grid.
29826          * @param {Roo.bootstrap.NavProgressItem} this
29827          * @param {Roo.EventObject} e
29828          */
29829         "click" : true
29830     });
29831    
29832 };
29833
29834 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29835     
29836     rid : '',
29837     active : false,
29838     disabled : false,
29839     html : '',
29840     position : 'bottom',
29841     icon : false,
29842     
29843     getAutoCreate : function()
29844     {
29845         var iconCls = 'roo-navigation-bar-item-icon';
29846         
29847         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29848         
29849         var cfg = {
29850             tag: 'li',
29851             cls: 'roo-navigation-bar-item',
29852             cn : [
29853                 {
29854                     tag : 'i',
29855                     cls : iconCls
29856                 }
29857             ]
29858         };
29859         
29860         if(this.active){
29861             cfg.cls += ' active';
29862         }
29863         if(this.disabled){
29864             cfg.cls += ' disabled';
29865         }
29866         
29867         return cfg;
29868     },
29869     
29870     disable : function()
29871     {
29872         this.setDisabled(true);
29873     },
29874     
29875     enable : function()
29876     {
29877         this.setDisabled(false);
29878     },
29879     
29880     initEvents: function() 
29881     {
29882         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29883         
29884         this.iconEl.on('click', this.onClick, this);
29885     },
29886     
29887     onClick : function(e)
29888     {
29889         e.preventDefault();
29890         
29891         if(this.disabled){
29892             return;
29893         }
29894         
29895         if(this.fireEvent('click', this, e) === false){
29896             return;
29897         };
29898         
29899         this.parent().setActiveItem(this);
29900     },
29901     
29902     isActive: function () 
29903     {
29904         return this.active;
29905     },
29906     
29907     setActive : function(state)
29908     {
29909         if(this.active == state){
29910             return;
29911         }
29912         
29913         this.active = state;
29914         
29915         if (state) {
29916             this.el.addClass('active');
29917             return;
29918         }
29919         
29920         this.el.removeClass('active');
29921         
29922         return;
29923     },
29924     
29925     setDisabled : function(state)
29926     {
29927         if(this.disabled == state){
29928             return;
29929         }
29930         
29931         this.disabled = state;
29932         
29933         if (state) {
29934             this.el.addClass('disabled');
29935             return;
29936         }
29937         
29938         this.el.removeClass('disabled');
29939     },
29940     
29941     tooltipEl : function()
29942     {
29943         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29944     }
29945 });
29946  
29947
29948  /*
29949  * - LGPL
29950  *
29951  * FieldLabel
29952  * 
29953  */
29954
29955 /**
29956  * @class Roo.bootstrap.FieldLabel
29957  * @extends Roo.bootstrap.Component
29958  * Bootstrap FieldLabel class
29959  * @cfg {String} html contents of the element
29960  * @cfg {String} tag tag of the element default label
29961  * @cfg {String} cls class of the element
29962  * @cfg {String} target label target 
29963  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29964  * @cfg {String} invalidClass default "text-warning"
29965  * @cfg {String} validClass default "text-success"
29966  * @cfg {String} iconTooltip default "This field is required"
29967  * @cfg {String} indicatorpos (left|right) default left
29968  * 
29969  * @constructor
29970  * Create a new FieldLabel
29971  * @param {Object} config The config object
29972  */
29973
29974 Roo.bootstrap.FieldLabel = function(config){
29975     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29976     
29977     this.addEvents({
29978             /**
29979              * @event invalid
29980              * Fires after the field has been marked as invalid.
29981              * @param {Roo.form.FieldLabel} this
29982              * @param {String} msg The validation message
29983              */
29984             invalid : true,
29985             /**
29986              * @event valid
29987              * Fires after the field has been validated with no errors.
29988              * @param {Roo.form.FieldLabel} this
29989              */
29990             valid : true
29991         });
29992 };
29993
29994 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29995     
29996     tag: 'label',
29997     cls: '',
29998     html: '',
29999     target: '',
30000     allowBlank : true,
30001     invalidClass : 'has-warning',
30002     validClass : 'has-success',
30003     iconTooltip : 'This field is required',
30004     indicatorpos : 'left',
30005     
30006     getAutoCreate : function(){
30007         
30008         var cfg = {
30009             tag : this.tag,
30010             cls : 'roo-bootstrap-field-label ' + this.cls,
30011             for : this.target,
30012             cn : [
30013                 {
30014                     tag : 'i',
30015                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
30016                     tooltip : this.iconTooltip
30017                 },
30018                 {
30019                     tag : 'span',
30020                     html : this.html
30021                 }
30022             ] 
30023         };
30024         
30025         if(this.indicatorpos == 'right'){
30026             var cfg = {
30027                 tag : this.tag,
30028                 cls : 'roo-bootstrap-field-label ' + this.cls,
30029                 for : this.target,
30030                 cn : [
30031                     {
30032                         tag : 'span',
30033                         html : this.html
30034                     },
30035                     {
30036                         tag : 'i',
30037                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
30038                         tooltip : this.iconTooltip
30039                     }
30040                 ] 
30041             };
30042         }
30043         
30044         return cfg;
30045     },
30046     
30047     initEvents: function() 
30048     {
30049         Roo.bootstrap.Element.superclass.initEvents.call(this);
30050         
30051         this.indicator = this.indicatorEl();
30052         
30053         if(this.indicator){
30054             this.indicator.removeClass('visible');
30055             this.indicator.addClass('invisible');
30056         }
30057         
30058         Roo.bootstrap.FieldLabel.register(this);
30059     },
30060     
30061     indicatorEl : function()
30062     {
30063         var indicator = this.el.select('i.roo-required-indicator',true).first();
30064         
30065         if(!indicator){
30066             return false;
30067         }
30068         
30069         return indicator;
30070         
30071     },
30072     
30073     /**
30074      * Mark this field as valid
30075      */
30076     markValid : function()
30077     {
30078         if(this.indicator){
30079             this.indicator.removeClass('visible');
30080             this.indicator.addClass('invisible');
30081         }
30082         
30083         this.el.removeClass(this.invalidClass);
30084         
30085         this.el.addClass(this.validClass);
30086         
30087         this.fireEvent('valid', this);
30088     },
30089     
30090     /**
30091      * Mark this field as invalid
30092      * @param {String} msg The validation message
30093      */
30094     markInvalid : function(msg)
30095     {
30096         if(this.indicator){
30097             this.indicator.removeClass('invisible');
30098             this.indicator.addClass('visible');
30099         }
30100         
30101         this.el.removeClass(this.validClass);
30102         
30103         this.el.addClass(this.invalidClass);
30104         
30105         this.fireEvent('invalid', this, msg);
30106     }
30107     
30108    
30109 });
30110
30111 Roo.apply(Roo.bootstrap.FieldLabel, {
30112     
30113     groups: {},
30114     
30115      /**
30116     * register a FieldLabel Group
30117     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30118     */
30119     register : function(label)
30120     {
30121         if(this.groups.hasOwnProperty(label.target)){
30122             return;
30123         }
30124      
30125         this.groups[label.target] = label;
30126         
30127     },
30128     /**
30129     * fetch a FieldLabel Group based on the target
30130     * @param {string} target
30131     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30132     */
30133     get: function(target) {
30134         if (typeof(this.groups[target]) == 'undefined') {
30135             return false;
30136         }
30137         
30138         return this.groups[target] ;
30139     }
30140 });
30141
30142  
30143
30144  /*
30145  * - LGPL
30146  *
30147  * page DateSplitField.
30148  * 
30149  */
30150
30151
30152 /**
30153  * @class Roo.bootstrap.DateSplitField
30154  * @extends Roo.bootstrap.Component
30155  * Bootstrap DateSplitField class
30156  * @cfg {string} fieldLabel - the label associated
30157  * @cfg {Number} labelWidth set the width of label (0-12)
30158  * @cfg {String} labelAlign (top|left)
30159  * @cfg {Boolean} dayAllowBlank (true|false) default false
30160  * @cfg {Boolean} monthAllowBlank (true|false) default false
30161  * @cfg {Boolean} yearAllowBlank (true|false) default false
30162  * @cfg {string} dayPlaceholder 
30163  * @cfg {string} monthPlaceholder
30164  * @cfg {string} yearPlaceholder
30165  * @cfg {string} dayFormat default 'd'
30166  * @cfg {string} monthFormat default 'm'
30167  * @cfg {string} yearFormat default 'Y'
30168  * @cfg {Number} labellg set the width of label (1-12)
30169  * @cfg {Number} labelmd set the width of label (1-12)
30170  * @cfg {Number} labelsm set the width of label (1-12)
30171  * @cfg {Number} labelxs set the width of label (1-12)
30172
30173  *     
30174  * @constructor
30175  * Create a new DateSplitField
30176  * @param {Object} config The config object
30177  */
30178
30179 Roo.bootstrap.DateSplitField = function(config){
30180     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30181     
30182     this.addEvents({
30183         // raw events
30184          /**
30185          * @event years
30186          * getting the data of years
30187          * @param {Roo.bootstrap.DateSplitField} this
30188          * @param {Object} years
30189          */
30190         "years" : true,
30191         /**
30192          * @event days
30193          * getting the data of days
30194          * @param {Roo.bootstrap.DateSplitField} this
30195          * @param {Object} days
30196          */
30197         "days" : true,
30198         /**
30199          * @event invalid
30200          * Fires after the field has been marked as invalid.
30201          * @param {Roo.form.Field} this
30202          * @param {String} msg The validation message
30203          */
30204         invalid : true,
30205        /**
30206          * @event valid
30207          * Fires after the field has been validated with no errors.
30208          * @param {Roo.form.Field} this
30209          */
30210         valid : true
30211     });
30212 };
30213
30214 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30215     
30216     fieldLabel : '',
30217     labelAlign : 'top',
30218     labelWidth : 3,
30219     dayAllowBlank : false,
30220     monthAllowBlank : false,
30221     yearAllowBlank : false,
30222     dayPlaceholder : '',
30223     monthPlaceholder : '',
30224     yearPlaceholder : '',
30225     dayFormat : 'd',
30226     monthFormat : 'm',
30227     yearFormat : 'Y',
30228     isFormField : true,
30229     labellg : 0,
30230     labelmd : 0,
30231     labelsm : 0,
30232     labelxs : 0,
30233     
30234     getAutoCreate : function()
30235     {
30236         var cfg = {
30237             tag : 'div',
30238             cls : 'row roo-date-split-field-group',
30239             cn : [
30240                 {
30241                     tag : 'input',
30242                     type : 'hidden',
30243                     cls : 'form-hidden-field roo-date-split-field-group-value',
30244                     name : this.name
30245                 }
30246             ]
30247         };
30248         
30249         var labelCls = 'col-md-12';
30250         var contentCls = 'col-md-4';
30251         
30252         if(this.fieldLabel){
30253             
30254             var label = {
30255                 tag : 'div',
30256                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30257                 cn : [
30258                     {
30259                         tag : 'label',
30260                         html : this.fieldLabel
30261                     }
30262                 ]
30263             };
30264             
30265             if(this.labelAlign == 'left'){
30266             
30267                 if(this.labelWidth > 12){
30268                     label.style = "width: " + this.labelWidth + 'px';
30269                 }
30270
30271                 if(this.labelWidth < 13 && this.labelmd == 0){
30272                     this.labelmd = this.labelWidth;
30273                 }
30274
30275                 if(this.labellg > 0){
30276                     labelCls = ' col-lg-' + this.labellg;
30277                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30278                 }
30279
30280                 if(this.labelmd > 0){
30281                     labelCls = ' col-md-' + this.labelmd;
30282                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30283                 }
30284
30285                 if(this.labelsm > 0){
30286                     labelCls = ' col-sm-' + this.labelsm;
30287                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30288                 }
30289
30290                 if(this.labelxs > 0){
30291                     labelCls = ' col-xs-' + this.labelxs;
30292                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30293                 }
30294             }
30295             
30296             label.cls += ' ' + labelCls;
30297             
30298             cfg.cn.push(label);
30299         }
30300         
30301         Roo.each(['day', 'month', 'year'], function(t){
30302             cfg.cn.push({
30303                 tag : 'div',
30304                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30305             });
30306         }, this);
30307         
30308         return cfg;
30309     },
30310     
30311     inputEl: function ()
30312     {
30313         return this.el.select('.roo-date-split-field-group-value', true).first();
30314     },
30315     
30316     onRender : function(ct, position) 
30317     {
30318         var _this = this;
30319         
30320         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30321         
30322         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30323         
30324         this.dayField = new Roo.bootstrap.ComboBox({
30325             allowBlank : this.dayAllowBlank,
30326             alwaysQuery : true,
30327             displayField : 'value',
30328             editable : false,
30329             fieldLabel : '',
30330             forceSelection : true,
30331             mode : 'local',
30332             placeholder : this.dayPlaceholder,
30333             selectOnFocus : true,
30334             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30335             triggerAction : 'all',
30336             typeAhead : true,
30337             valueField : 'value',
30338             store : new Roo.data.SimpleStore({
30339                 data : (function() {    
30340                     var days = [];
30341                     _this.fireEvent('days', _this, days);
30342                     return days;
30343                 })(),
30344                 fields : [ 'value' ]
30345             }),
30346             listeners : {
30347                 select : function (_self, record, index)
30348                 {
30349                     _this.setValue(_this.getValue());
30350                 }
30351             }
30352         });
30353
30354         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30355         
30356         this.monthField = new Roo.bootstrap.MonthField({
30357             after : '<i class=\"fa fa-calendar\"></i>',
30358             allowBlank : this.monthAllowBlank,
30359             placeholder : this.monthPlaceholder,
30360             readOnly : true,
30361             listeners : {
30362                 render : function (_self)
30363                 {
30364                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30365                         e.preventDefault();
30366                         _self.focus();
30367                     });
30368                 },
30369                 select : function (_self, oldvalue, newvalue)
30370                 {
30371                     _this.setValue(_this.getValue());
30372                 }
30373             }
30374         });
30375         
30376         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30377         
30378         this.yearField = new Roo.bootstrap.ComboBox({
30379             allowBlank : this.yearAllowBlank,
30380             alwaysQuery : true,
30381             displayField : 'value',
30382             editable : false,
30383             fieldLabel : '',
30384             forceSelection : true,
30385             mode : 'local',
30386             placeholder : this.yearPlaceholder,
30387             selectOnFocus : true,
30388             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30389             triggerAction : 'all',
30390             typeAhead : true,
30391             valueField : 'value',
30392             store : new Roo.data.SimpleStore({
30393                 data : (function() {
30394                     var years = [];
30395                     _this.fireEvent('years', _this, years);
30396                     return years;
30397                 })(),
30398                 fields : [ 'value' ]
30399             }),
30400             listeners : {
30401                 select : function (_self, record, index)
30402                 {
30403                     _this.setValue(_this.getValue());
30404                 }
30405             }
30406         });
30407
30408         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30409     },
30410     
30411     setValue : function(v, format)
30412     {
30413         this.inputEl.dom.value = v;
30414         
30415         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30416         
30417         var d = Date.parseDate(v, f);
30418         
30419         if(!d){
30420             this.validate();
30421             return;
30422         }
30423         
30424         this.setDay(d.format(this.dayFormat));
30425         this.setMonth(d.format(this.monthFormat));
30426         this.setYear(d.format(this.yearFormat));
30427         
30428         this.validate();
30429         
30430         return;
30431     },
30432     
30433     setDay : function(v)
30434     {
30435         this.dayField.setValue(v);
30436         this.inputEl.dom.value = this.getValue();
30437         this.validate();
30438         return;
30439     },
30440     
30441     setMonth : function(v)
30442     {
30443         this.monthField.setValue(v, true);
30444         this.inputEl.dom.value = this.getValue();
30445         this.validate();
30446         return;
30447     },
30448     
30449     setYear : function(v)
30450     {
30451         this.yearField.setValue(v);
30452         this.inputEl.dom.value = this.getValue();
30453         this.validate();
30454         return;
30455     },
30456     
30457     getDay : function()
30458     {
30459         return this.dayField.getValue();
30460     },
30461     
30462     getMonth : function()
30463     {
30464         return this.monthField.getValue();
30465     },
30466     
30467     getYear : function()
30468     {
30469         return this.yearField.getValue();
30470     },
30471     
30472     getValue : function()
30473     {
30474         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30475         
30476         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30477         
30478         return date;
30479     },
30480     
30481     reset : function()
30482     {
30483         this.setDay('');
30484         this.setMonth('');
30485         this.setYear('');
30486         this.inputEl.dom.value = '';
30487         this.validate();
30488         return;
30489     },
30490     
30491     validate : function()
30492     {
30493         var d = this.dayField.validate();
30494         var m = this.monthField.validate();
30495         var y = this.yearField.validate();
30496         
30497         var valid = true;
30498         
30499         if(
30500                 (!this.dayAllowBlank && !d) ||
30501                 (!this.monthAllowBlank && !m) ||
30502                 (!this.yearAllowBlank && !y)
30503         ){
30504             valid = false;
30505         }
30506         
30507         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30508             return valid;
30509         }
30510         
30511         if(valid){
30512             this.markValid();
30513             return valid;
30514         }
30515         
30516         this.markInvalid();
30517         
30518         return valid;
30519     },
30520     
30521     markValid : function()
30522     {
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      /**
30535      * Mark this field as invalid
30536      * @param {String} msg The validation message
30537      */
30538     markInvalid : function(msg)
30539     {
30540         
30541         var label = this.el.select('label', true).first();
30542         var icon = this.el.select('i.fa-star', true).first();
30543
30544         if(label && !icon){
30545             this.el.select('.roo-date-split-field-label', true).createChild({
30546                 tag : 'i',
30547                 cls : 'text-danger fa fa-lg fa-star',
30548                 tooltip : 'This field is required',
30549                 style : 'margin-right:5px;'
30550             }, label, true);
30551         }
30552         
30553         this.fireEvent('invalid', this, msg);
30554     },
30555     
30556     clearInvalid : function()
30557     {
30558         var label = this.el.select('label', true).first();
30559         var icon = this.el.select('i.fa-star', true).first();
30560
30561         if(label && icon){
30562             icon.remove();
30563         }
30564         
30565         this.fireEvent('valid', this);
30566     },
30567     
30568     getName: function()
30569     {
30570         return this.name;
30571     }
30572     
30573 });
30574
30575  /**
30576  *
30577  * This is based on 
30578  * http://masonry.desandro.com
30579  *
30580  * The idea is to render all the bricks based on vertical width...
30581  *
30582  * The original code extends 'outlayer' - we might need to use that....
30583  * 
30584  */
30585
30586
30587 /**
30588  * @class Roo.bootstrap.LayoutMasonry
30589  * @extends Roo.bootstrap.Component
30590  * Bootstrap Layout Masonry class
30591  * 
30592  * @constructor
30593  * Create a new Element
30594  * @param {Object} config The config object
30595  */
30596
30597 Roo.bootstrap.LayoutMasonry = function(config){
30598     
30599     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30600     
30601     this.bricks = [];
30602     
30603     Roo.bootstrap.LayoutMasonry.register(this);
30604     
30605     this.addEvents({
30606         // raw events
30607         /**
30608          * @event layout
30609          * Fire after layout the items
30610          * @param {Roo.bootstrap.LayoutMasonry} this
30611          * @param {Roo.EventObject} e
30612          */
30613         "layout" : true
30614     });
30615     
30616 };
30617
30618 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30619     
30620     /**
30621      * @cfg {Boolean} isLayoutInstant = no animation?
30622      */   
30623     isLayoutInstant : false, // needed?
30624    
30625     /**
30626      * @cfg {Number} boxWidth  width of the columns
30627      */   
30628     boxWidth : 450,
30629     
30630       /**
30631      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30632      */   
30633     boxHeight : 0,
30634     
30635     /**
30636      * @cfg {Number} padWidth padding below box..
30637      */   
30638     padWidth : 10, 
30639     
30640     /**
30641      * @cfg {Number} gutter gutter width..
30642      */   
30643     gutter : 10,
30644     
30645      /**
30646      * @cfg {Number} maxCols maximum number of columns
30647      */   
30648     
30649     maxCols: 0,
30650     
30651     /**
30652      * @cfg {Boolean} isAutoInitial defalut true
30653      */   
30654     isAutoInitial : true, 
30655     
30656     containerWidth: 0,
30657     
30658     /**
30659      * @cfg {Boolean} isHorizontal defalut false
30660      */   
30661     isHorizontal : false, 
30662
30663     currentSize : null,
30664     
30665     tag: 'div',
30666     
30667     cls: '',
30668     
30669     bricks: null, //CompositeElement
30670     
30671     cols : 1,
30672     
30673     _isLayoutInited : false,
30674     
30675 //    isAlternative : false, // only use for vertical layout...
30676     
30677     /**
30678      * @cfg {Number} alternativePadWidth padding below box..
30679      */   
30680     alternativePadWidth : 50,
30681     
30682     selectedBrick : [],
30683     
30684     getAutoCreate : function(){
30685         
30686         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30687         
30688         var cfg = {
30689             tag: this.tag,
30690             cls: 'blog-masonary-wrapper ' + this.cls,
30691             cn : {
30692                 cls : 'mas-boxes masonary'
30693             }
30694         };
30695         
30696         return cfg;
30697     },
30698     
30699     getChildContainer: function( )
30700     {
30701         if (this.boxesEl) {
30702             return this.boxesEl;
30703         }
30704         
30705         this.boxesEl = this.el.select('.mas-boxes').first();
30706         
30707         return this.boxesEl;
30708     },
30709     
30710     
30711     initEvents : function()
30712     {
30713         var _this = this;
30714         
30715         if(this.isAutoInitial){
30716             Roo.log('hook children rendered');
30717             this.on('childrenrendered', function() {
30718                 Roo.log('children rendered');
30719                 _this.initial();
30720             } ,this);
30721         }
30722     },
30723     
30724     initial : function()
30725     {
30726         this.selectedBrick = [];
30727         
30728         this.currentSize = this.el.getBox(true);
30729         
30730         Roo.EventManager.onWindowResize(this.resize, this); 
30731
30732         if(!this.isAutoInitial){
30733             this.layout();
30734             return;
30735         }
30736         
30737         this.layout();
30738         
30739         return;
30740         //this.layout.defer(500,this);
30741         
30742     },
30743     
30744     resize : function()
30745     {
30746         var cs = this.el.getBox(true);
30747         
30748         if (
30749                 this.currentSize.width == cs.width && 
30750                 this.currentSize.x == cs.x && 
30751                 this.currentSize.height == cs.height && 
30752                 this.currentSize.y == cs.y 
30753         ) {
30754             Roo.log("no change in with or X or Y");
30755             return;
30756         }
30757         
30758         this.currentSize = cs;
30759         
30760         this.layout();
30761         
30762     },
30763     
30764     layout : function()
30765     {   
30766         this._resetLayout();
30767         
30768         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30769         
30770         this.layoutItems( isInstant );
30771       
30772         this._isLayoutInited = true;
30773         
30774         this.fireEvent('layout', this);
30775         
30776     },
30777     
30778     _resetLayout : function()
30779     {
30780         if(this.isHorizontal){
30781             this.horizontalMeasureColumns();
30782             return;
30783         }
30784         
30785         this.verticalMeasureColumns();
30786         
30787     },
30788     
30789     verticalMeasureColumns : function()
30790     {
30791         this.getContainerWidth();
30792         
30793 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30794 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30795 //            return;
30796 //        }
30797         
30798         var boxWidth = this.boxWidth + this.padWidth;
30799         
30800         if(this.containerWidth < this.boxWidth){
30801             boxWidth = this.containerWidth
30802         }
30803         
30804         var containerWidth = this.containerWidth;
30805         
30806         var cols = Math.floor(containerWidth / boxWidth);
30807         
30808         this.cols = Math.max( cols, 1 );
30809         
30810         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30811         
30812         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30813         
30814         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30815         
30816         this.colWidth = boxWidth + avail - this.padWidth;
30817         
30818         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30819         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30820     },
30821     
30822     horizontalMeasureColumns : function()
30823     {
30824         this.getContainerWidth();
30825         
30826         var boxWidth = this.boxWidth;
30827         
30828         if(this.containerWidth < boxWidth){
30829             boxWidth = this.containerWidth;
30830         }
30831         
30832         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30833         
30834         this.el.setHeight(boxWidth);
30835         
30836     },
30837     
30838     getContainerWidth : function()
30839     {
30840         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30841     },
30842     
30843     layoutItems : function( isInstant )
30844     {
30845         Roo.log(this.bricks);
30846         
30847         var items = Roo.apply([], this.bricks);
30848         
30849         if(this.isHorizontal){
30850             this._horizontalLayoutItems( items , isInstant );
30851             return;
30852         }
30853         
30854 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30855 //            this._verticalAlternativeLayoutItems( items , isInstant );
30856 //            return;
30857 //        }
30858         
30859         this._verticalLayoutItems( items , isInstant );
30860         
30861     },
30862     
30863     _verticalLayoutItems : function ( items , isInstant)
30864     {
30865         if ( !items || !items.length ) {
30866             return;
30867         }
30868         
30869         var standard = [
30870             ['xs', 'xs', 'xs', 'tall'],
30871             ['xs', 'xs', 'tall'],
30872             ['xs', 'xs', 'sm'],
30873             ['xs', 'xs', 'xs'],
30874             ['xs', 'tall'],
30875             ['xs', 'sm'],
30876             ['xs', 'xs'],
30877             ['xs'],
30878             
30879             ['sm', 'xs', 'xs'],
30880             ['sm', 'xs'],
30881             ['sm'],
30882             
30883             ['tall', 'xs', 'xs', 'xs'],
30884             ['tall', 'xs', 'xs'],
30885             ['tall', 'xs'],
30886             ['tall']
30887             
30888         ];
30889         
30890         var queue = [];
30891         
30892         var boxes = [];
30893         
30894         var box = [];
30895         
30896         Roo.each(items, function(item, k){
30897             
30898             switch (item.size) {
30899                 // these layouts take up a full box,
30900                 case 'md' :
30901                 case 'md-left' :
30902                 case 'md-right' :
30903                 case 'wide' :
30904                     
30905                     if(box.length){
30906                         boxes.push(box);
30907                         box = [];
30908                     }
30909                     
30910                     boxes.push([item]);
30911                     
30912                     break;
30913                     
30914                 case 'xs' :
30915                 case 'sm' :
30916                 case 'tall' :
30917                     
30918                     box.push(item);
30919                     
30920                     break;
30921                 default :
30922                     break;
30923                     
30924             }
30925             
30926         }, this);
30927         
30928         if(box.length){
30929             boxes.push(box);
30930             box = [];
30931         }
30932         
30933         var filterPattern = function(box, length)
30934         {
30935             if(!box.length){
30936                 return;
30937             }
30938             
30939             var match = false;
30940             
30941             var pattern = box.slice(0, length);
30942             
30943             var format = [];
30944             
30945             Roo.each(pattern, function(i){
30946                 format.push(i.size);
30947             }, this);
30948             
30949             Roo.each(standard, function(s){
30950                 
30951                 if(String(s) != String(format)){
30952                     return;
30953                 }
30954                 
30955                 match = true;
30956                 return false;
30957                 
30958             }, this);
30959             
30960             if(!match && length == 1){
30961                 return;
30962             }
30963             
30964             if(!match){
30965                 filterPattern(box, length - 1);
30966                 return;
30967             }
30968                 
30969             queue.push(pattern);
30970
30971             box = box.slice(length, box.length);
30972
30973             filterPattern(box, 4);
30974
30975             return;
30976             
30977         }
30978         
30979         Roo.each(boxes, function(box, k){
30980             
30981             if(!box.length){
30982                 return;
30983             }
30984             
30985             if(box.length == 1){
30986                 queue.push(box);
30987                 return;
30988             }
30989             
30990             filterPattern(box, 4);
30991             
30992         }, this);
30993         
30994         this._processVerticalLayoutQueue( queue, isInstant );
30995         
30996     },
30997     
30998 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30999 //    {
31000 //        if ( !items || !items.length ) {
31001 //            return;
31002 //        }
31003 //
31004 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31005 //        
31006 //    },
31007     
31008     _horizontalLayoutItems : function ( items , isInstant)
31009     {
31010         if ( !items || !items.length || items.length < 3) {
31011             return;
31012         }
31013         
31014         items.reverse();
31015         
31016         var eItems = items.slice(0, 3);
31017         
31018         items = items.slice(3, items.length);
31019         
31020         var standard = [
31021             ['xs', 'xs', 'xs', 'wide'],
31022             ['xs', 'xs', 'wide'],
31023             ['xs', 'xs', 'sm'],
31024             ['xs', 'xs', 'xs'],
31025             ['xs', 'wide'],
31026             ['xs', 'sm'],
31027             ['xs', 'xs'],
31028             ['xs'],
31029             
31030             ['sm', 'xs', 'xs'],
31031             ['sm', 'xs'],
31032             ['sm'],
31033             
31034             ['wide', 'xs', 'xs', 'xs'],
31035             ['wide', 'xs', 'xs'],
31036             ['wide', 'xs'],
31037             ['wide'],
31038             
31039             ['wide-thin']
31040         ];
31041         
31042         var queue = [];
31043         
31044         var boxes = [];
31045         
31046         var box = [];
31047         
31048         Roo.each(items, function(item, k){
31049             
31050             switch (item.size) {
31051                 case 'md' :
31052                 case 'md-left' :
31053                 case 'md-right' :
31054                 case 'tall' :
31055                     
31056                     if(box.length){
31057                         boxes.push(box);
31058                         box = [];
31059                     }
31060                     
31061                     boxes.push([item]);
31062                     
31063                     break;
31064                     
31065                 case 'xs' :
31066                 case 'sm' :
31067                 case 'wide' :
31068                 case 'wide-thin' :
31069                     
31070                     box.push(item);
31071                     
31072                     break;
31073                 default :
31074                     break;
31075                     
31076             }
31077             
31078         }, this);
31079         
31080         if(box.length){
31081             boxes.push(box);
31082             box = [];
31083         }
31084         
31085         var filterPattern = function(box, length)
31086         {
31087             if(!box.length){
31088                 return;
31089             }
31090             
31091             var match = false;
31092             
31093             var pattern = box.slice(0, length);
31094             
31095             var format = [];
31096             
31097             Roo.each(pattern, function(i){
31098                 format.push(i.size);
31099             }, this);
31100             
31101             Roo.each(standard, function(s){
31102                 
31103                 if(String(s) != String(format)){
31104                     return;
31105                 }
31106                 
31107                 match = true;
31108                 return false;
31109                 
31110             }, this);
31111             
31112             if(!match && length == 1){
31113                 return;
31114             }
31115             
31116             if(!match){
31117                 filterPattern(box, length - 1);
31118                 return;
31119             }
31120                 
31121             queue.push(pattern);
31122
31123             box = box.slice(length, box.length);
31124
31125             filterPattern(box, 4);
31126
31127             return;
31128             
31129         }
31130         
31131         Roo.each(boxes, function(box, k){
31132             
31133             if(!box.length){
31134                 return;
31135             }
31136             
31137             if(box.length == 1){
31138                 queue.push(box);
31139                 return;
31140             }
31141             
31142             filterPattern(box, 4);
31143             
31144         }, this);
31145         
31146         
31147         var prune = [];
31148         
31149         var pos = this.el.getBox(true);
31150         
31151         var minX = pos.x;
31152         
31153         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31154         
31155         var hit_end = false;
31156         
31157         Roo.each(queue, function(box){
31158             
31159             if(hit_end){
31160                 
31161                 Roo.each(box, function(b){
31162                 
31163                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31164                     b.el.hide();
31165
31166                 }, this);
31167
31168                 return;
31169             }
31170             
31171             var mx = 0;
31172             
31173             Roo.each(box, function(b){
31174                 
31175                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31176                 b.el.show();
31177
31178                 mx = Math.max(mx, b.x);
31179                 
31180             }, this);
31181             
31182             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31183             
31184             if(maxX < minX){
31185                 
31186                 Roo.each(box, function(b){
31187                 
31188                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31189                     b.el.hide();
31190                     
31191                 }, this);
31192                 
31193                 hit_end = true;
31194                 
31195                 return;
31196             }
31197             
31198             prune.push(box);
31199             
31200         }, this);
31201         
31202         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31203     },
31204     
31205     /** Sets position of item in DOM
31206     * @param {Element} item
31207     * @param {Number} x - horizontal position
31208     * @param {Number} y - vertical position
31209     * @param {Boolean} isInstant - disables transitions
31210     */
31211     _processVerticalLayoutQueue : function( queue, isInstant )
31212     {
31213         var pos = this.el.getBox(true);
31214         var x = pos.x;
31215         var y = pos.y;
31216         var maxY = [];
31217         
31218         for (var i = 0; i < this.cols; i++){
31219             maxY[i] = pos.y;
31220         }
31221         
31222         Roo.each(queue, function(box, k){
31223             
31224             var col = k % this.cols;
31225             
31226             Roo.each(box, function(b,kk){
31227                 
31228                 b.el.position('absolute');
31229                 
31230                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31231                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31232                 
31233                 if(b.size == 'md-left' || b.size == 'md-right'){
31234                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31235                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31236                 }
31237                 
31238                 b.el.setWidth(width);
31239                 b.el.setHeight(height);
31240                 // iframe?
31241                 b.el.select('iframe',true).setSize(width,height);
31242                 
31243             }, this);
31244             
31245             for (var i = 0; i < this.cols; i++){
31246                 
31247                 if(maxY[i] < maxY[col]){
31248                     col = i;
31249                     continue;
31250                 }
31251                 
31252                 col = Math.min(col, i);
31253                 
31254             }
31255             
31256             x = pos.x + col * (this.colWidth + this.padWidth);
31257             
31258             y = maxY[col];
31259             
31260             var positions = [];
31261             
31262             switch (box.length){
31263                 case 1 :
31264                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31265                     break;
31266                 case 2 :
31267                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31268                     break;
31269                 case 3 :
31270                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31271                     break;
31272                 case 4 :
31273                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31274                     break;
31275                 default :
31276                     break;
31277             }
31278             
31279             Roo.each(box, function(b,kk){
31280                 
31281                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31282                 
31283                 var sz = b.el.getSize();
31284                 
31285                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31286                 
31287             }, this);
31288             
31289         }, this);
31290         
31291         var mY = 0;
31292         
31293         for (var i = 0; i < this.cols; i++){
31294             mY = Math.max(mY, maxY[i]);
31295         }
31296         
31297         this.el.setHeight(mY - pos.y);
31298         
31299     },
31300     
31301 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31302 //    {
31303 //        var pos = this.el.getBox(true);
31304 //        var x = pos.x;
31305 //        var y = pos.y;
31306 //        var maxX = pos.right;
31307 //        
31308 //        var maxHeight = 0;
31309 //        
31310 //        Roo.each(items, function(item, k){
31311 //            
31312 //            var c = k % 2;
31313 //            
31314 //            item.el.position('absolute');
31315 //                
31316 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31317 //
31318 //            item.el.setWidth(width);
31319 //
31320 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31321 //
31322 //            item.el.setHeight(height);
31323 //            
31324 //            if(c == 0){
31325 //                item.el.setXY([x, y], isInstant ? false : true);
31326 //            } else {
31327 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31328 //            }
31329 //            
31330 //            y = y + height + this.alternativePadWidth;
31331 //            
31332 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31333 //            
31334 //        }, this);
31335 //        
31336 //        this.el.setHeight(maxHeight);
31337 //        
31338 //    },
31339     
31340     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31341     {
31342         var pos = this.el.getBox(true);
31343         
31344         var minX = pos.x;
31345         var minY = pos.y;
31346         
31347         var maxX = pos.right;
31348         
31349         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31350         
31351         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31352         
31353         Roo.each(queue, function(box, k){
31354             
31355             Roo.each(box, function(b, kk){
31356                 
31357                 b.el.position('absolute');
31358                 
31359                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31360                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31361                 
31362                 if(b.size == 'md-left' || b.size == 'md-right'){
31363                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31364                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31365                 }
31366                 
31367                 b.el.setWidth(width);
31368                 b.el.setHeight(height);
31369                 
31370             }, this);
31371             
31372             if(!box.length){
31373                 return;
31374             }
31375             
31376             var positions = [];
31377             
31378             switch (box.length){
31379                 case 1 :
31380                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31381                     break;
31382                 case 2 :
31383                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31384                     break;
31385                 case 3 :
31386                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31387                     break;
31388                 case 4 :
31389                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31390                     break;
31391                 default :
31392                     break;
31393             }
31394             
31395             Roo.each(box, function(b,kk){
31396                 
31397                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31398                 
31399                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31400                 
31401             }, this);
31402             
31403         }, this);
31404         
31405     },
31406     
31407     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31408     {
31409         Roo.each(eItems, function(b,k){
31410             
31411             b.size = (k == 0) ? 'sm' : 'xs';
31412             b.x = (k == 0) ? 2 : 1;
31413             b.y = (k == 0) ? 2 : 1;
31414             
31415             b.el.position('absolute');
31416             
31417             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31418                 
31419             b.el.setWidth(width);
31420             
31421             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31422             
31423             b.el.setHeight(height);
31424             
31425         }, this);
31426
31427         var positions = [];
31428         
31429         positions.push({
31430             x : maxX - this.unitWidth * 2 - this.gutter,
31431             y : minY
31432         });
31433         
31434         positions.push({
31435             x : maxX - this.unitWidth,
31436             y : minY + (this.unitWidth + this.gutter) * 2
31437         });
31438         
31439         positions.push({
31440             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31441             y : minY
31442         });
31443         
31444         Roo.each(eItems, function(b,k){
31445             
31446             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31447
31448         }, this);
31449         
31450     },
31451     
31452     getVerticalOneBoxColPositions : function(x, y, box)
31453     {
31454         var pos = [];
31455         
31456         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31457         
31458         if(box[0].size == 'md-left'){
31459             rand = 0;
31460         }
31461         
31462         if(box[0].size == 'md-right'){
31463             rand = 1;
31464         }
31465         
31466         pos.push({
31467             x : x + (this.unitWidth + this.gutter) * rand,
31468             y : y
31469         });
31470         
31471         return pos;
31472     },
31473     
31474     getVerticalTwoBoxColPositions : function(x, y, box)
31475     {
31476         var pos = [];
31477         
31478         if(box[0].size == 'xs'){
31479             
31480             pos.push({
31481                 x : x,
31482                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31483             });
31484
31485             pos.push({
31486                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31487                 y : y
31488             });
31489             
31490             return pos;
31491             
31492         }
31493         
31494         pos.push({
31495             x : x,
31496             y : y
31497         });
31498
31499         pos.push({
31500             x : x + (this.unitWidth + this.gutter) * 2,
31501             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31502         });
31503         
31504         return pos;
31505         
31506     },
31507     
31508     getVerticalThreeBoxColPositions : function(x, y, box)
31509     {
31510         var pos = [];
31511         
31512         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31513             
31514             pos.push({
31515                 x : x,
31516                 y : y
31517             });
31518
31519             pos.push({
31520                 x : x + (this.unitWidth + this.gutter) * 1,
31521                 y : y
31522             });
31523             
31524             pos.push({
31525                 x : x + (this.unitWidth + this.gutter) * 2,
31526                 y : y
31527             });
31528             
31529             return pos;
31530             
31531         }
31532         
31533         if(box[0].size == 'xs' && box[1].size == 'xs'){
31534             
31535             pos.push({
31536                 x : x,
31537                 y : y
31538             });
31539
31540             pos.push({
31541                 x : x,
31542                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31543             });
31544             
31545             pos.push({
31546                 x : x + (this.unitWidth + this.gutter) * 1,
31547                 y : y
31548             });
31549             
31550             return pos;
31551             
31552         }
31553         
31554         pos.push({
31555             x : x,
31556             y : y
31557         });
31558
31559         pos.push({
31560             x : x + (this.unitWidth + this.gutter) * 2,
31561             y : y
31562         });
31563
31564         pos.push({
31565             x : x + (this.unitWidth + this.gutter) * 2,
31566             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31567         });
31568             
31569         return pos;
31570         
31571     },
31572     
31573     getVerticalFourBoxColPositions : function(x, y, box)
31574     {
31575         var pos = [];
31576         
31577         if(box[0].size == 'xs'){
31578             
31579             pos.push({
31580                 x : x,
31581                 y : y
31582             });
31583
31584             pos.push({
31585                 x : x,
31586                 y : y + (this.unitHeight + this.gutter) * 1
31587             });
31588             
31589             pos.push({
31590                 x : x,
31591                 y : y + (this.unitHeight + this.gutter) * 2
31592             });
31593             
31594             pos.push({
31595                 x : x + (this.unitWidth + this.gutter) * 1,
31596                 y : y
31597             });
31598             
31599             return pos;
31600             
31601         }
31602         
31603         pos.push({
31604             x : x,
31605             y : y
31606         });
31607
31608         pos.push({
31609             x : x + (this.unitWidth + this.gutter) * 2,
31610             y : y
31611         });
31612
31613         pos.push({
31614             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31615             y : y + (this.unitHeight + this.gutter) * 1
31616         });
31617
31618         pos.push({
31619             x : x + (this.unitWidth + this.gutter) * 2,
31620             y : y + (this.unitWidth + this.gutter) * 2
31621         });
31622
31623         return pos;
31624         
31625     },
31626     
31627     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31628     {
31629         var pos = [];
31630         
31631         if(box[0].size == 'md-left'){
31632             pos.push({
31633                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31634                 y : minY
31635             });
31636             
31637             return pos;
31638         }
31639         
31640         if(box[0].size == 'md-right'){
31641             pos.push({
31642                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31643                 y : minY + (this.unitWidth + this.gutter) * 1
31644             });
31645             
31646             return pos;
31647         }
31648         
31649         var rand = Math.floor(Math.random() * (4 - box[0].y));
31650         
31651         pos.push({
31652             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31653             y : minY + (this.unitWidth + this.gutter) * rand
31654         });
31655         
31656         return pos;
31657         
31658     },
31659     
31660     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31661     {
31662         var pos = [];
31663         
31664         if(box[0].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) * (3 - box[1].y)
31674             });
31675             
31676             return pos;
31677             
31678         }
31679         
31680         pos.push({
31681             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31682             y : minY
31683         });
31684
31685         pos.push({
31686             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31687             y : minY + (this.unitWidth + this.gutter) * 2
31688         });
31689         
31690         return pos;
31691         
31692     },
31693     
31694     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31695     {
31696         var pos = [];
31697         
31698         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31699             
31700             pos.push({
31701                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31702                 y : minY
31703             });
31704
31705             pos.push({
31706                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31707                 y : minY + (this.unitWidth + this.gutter) * 1
31708             });
31709             
31710             pos.push({
31711                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31712                 y : minY + (this.unitWidth + this.gutter) * 2
31713             });
31714             
31715             return pos;
31716             
31717         }
31718         
31719         if(box[0].size == 'xs' && box[1].size == 'xs'){
31720             
31721             pos.push({
31722                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31723                 y : minY
31724             });
31725
31726             pos.push({
31727                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31728                 y : minY
31729             });
31730             
31731             pos.push({
31732                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31733                 y : minY + (this.unitWidth + this.gutter) * 1
31734             });
31735             
31736             return pos;
31737             
31738         }
31739         
31740         pos.push({
31741             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31742             y : minY
31743         });
31744
31745         pos.push({
31746             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31747             y : minY + (this.unitWidth + this.gutter) * 2
31748         });
31749
31750         pos.push({
31751             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31752             y : minY + (this.unitWidth + this.gutter) * 2
31753         });
31754             
31755         return pos;
31756         
31757     },
31758     
31759     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31760     {
31761         var pos = [];
31762         
31763         if(box[0].size == 'xs'){
31764             
31765             pos.push({
31766                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31767                 y : minY
31768             });
31769
31770             pos.push({
31771                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31772                 y : minY
31773             });
31774             
31775             pos.push({
31776                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31777                 y : minY
31778             });
31779             
31780             pos.push({
31781                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31782                 y : minY + (this.unitWidth + this.gutter) * 1
31783             });
31784             
31785             return pos;
31786             
31787         }
31788         
31789         pos.push({
31790             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31791             y : minY
31792         });
31793         
31794         pos.push({
31795             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31796             y : minY + (this.unitWidth + this.gutter) * 2
31797         });
31798         
31799         pos.push({
31800             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31801             y : minY + (this.unitWidth + this.gutter) * 2
31802         });
31803         
31804         pos.push({
31805             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31806             y : minY + (this.unitWidth + this.gutter) * 2
31807         });
31808
31809         return pos;
31810         
31811     },
31812     
31813     /**
31814     * remove a Masonry Brick
31815     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31816     */
31817     removeBrick : function(brick_id)
31818     {
31819         if (!brick_id) {
31820             return;
31821         }
31822         
31823         for (var i = 0; i<this.bricks.length; i++) {
31824             if (this.bricks[i].id == brick_id) {
31825                 this.bricks.splice(i,1);
31826                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31827                 this.initial();
31828             }
31829         }
31830     },
31831     
31832     /**
31833     * adds a Masonry Brick
31834     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31835     */
31836     addBrick : function(cfg)
31837     {
31838         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31839         //this.register(cn);
31840         cn.parentId = this.id;
31841         cn.onRender(this.el, null);
31842         return cn;
31843     },
31844     
31845     /**
31846     * register a Masonry Brick
31847     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31848     */
31849     
31850     register : function(brick)
31851     {
31852         this.bricks.push(brick);
31853         brick.masonryId = this.id;
31854     },
31855     
31856     /**
31857     * clear all the Masonry Brick
31858     */
31859     clearAll : function()
31860     {
31861         this.bricks = [];
31862         //this.getChildContainer().dom.innerHTML = "";
31863         this.el.dom.innerHTML = '';
31864     },
31865     
31866     getSelected : function()
31867     {
31868         if (!this.selectedBrick) {
31869             return false;
31870         }
31871         
31872         return this.selectedBrick;
31873     }
31874 });
31875
31876 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31877     
31878     groups: {},
31879      /**
31880     * register a Masonry Layout
31881     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31882     */
31883     
31884     register : function(layout)
31885     {
31886         this.groups[layout.id] = layout;
31887     },
31888     /**
31889     * fetch a  Masonry Layout based on the masonry layout ID
31890     * @param {string} the masonry layout to add
31891     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31892     */
31893     
31894     get: function(layout_id) {
31895         if (typeof(this.groups[layout_id]) == 'undefined') {
31896             return false;
31897         }
31898         return this.groups[layout_id] ;
31899     }
31900     
31901     
31902     
31903 });
31904
31905  
31906
31907  /**
31908  *
31909  * This is based on 
31910  * http://masonry.desandro.com
31911  *
31912  * The idea is to render all the bricks based on vertical width...
31913  *
31914  * The original code extends 'outlayer' - we might need to use that....
31915  * 
31916  */
31917
31918
31919 /**
31920  * @class Roo.bootstrap.LayoutMasonryAuto
31921  * @extends Roo.bootstrap.Component
31922  * Bootstrap Layout Masonry class
31923  * 
31924  * @constructor
31925  * Create a new Element
31926  * @param {Object} config The config object
31927  */
31928
31929 Roo.bootstrap.LayoutMasonryAuto = function(config){
31930     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31931 };
31932
31933 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31934     
31935       /**
31936      * @cfg {Boolean} isFitWidth  - resize the width..
31937      */   
31938     isFitWidth : false,  // options..
31939     /**
31940      * @cfg {Boolean} isOriginLeft = left align?
31941      */   
31942     isOriginLeft : true,
31943     /**
31944      * @cfg {Boolean} isOriginTop = top align?
31945      */   
31946     isOriginTop : false,
31947     /**
31948      * @cfg {Boolean} isLayoutInstant = no animation?
31949      */   
31950     isLayoutInstant : false, // needed?
31951     /**
31952      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31953      */   
31954     isResizingContainer : true,
31955     /**
31956      * @cfg {Number} columnWidth  width of the columns 
31957      */   
31958     
31959     columnWidth : 0,
31960     
31961     /**
31962      * @cfg {Number} maxCols maximum number of columns
31963      */   
31964     
31965     maxCols: 0,
31966     /**
31967      * @cfg {Number} padHeight padding below box..
31968      */   
31969     
31970     padHeight : 10, 
31971     
31972     /**
31973      * @cfg {Boolean} isAutoInitial defalut true
31974      */   
31975     
31976     isAutoInitial : true, 
31977     
31978     // private?
31979     gutter : 0,
31980     
31981     containerWidth: 0,
31982     initialColumnWidth : 0,
31983     currentSize : null,
31984     
31985     colYs : null, // array.
31986     maxY : 0,
31987     padWidth: 10,
31988     
31989     
31990     tag: 'div',
31991     cls: '',
31992     bricks: null, //CompositeElement
31993     cols : 0, // array?
31994     // element : null, // wrapped now this.el
31995     _isLayoutInited : null, 
31996     
31997     
31998     getAutoCreate : function(){
31999         
32000         var cfg = {
32001             tag: this.tag,
32002             cls: 'blog-masonary-wrapper ' + this.cls,
32003             cn : {
32004                 cls : 'mas-boxes masonary'
32005             }
32006         };
32007         
32008         return cfg;
32009     },
32010     
32011     getChildContainer: function( )
32012     {
32013         if (this.boxesEl) {
32014             return this.boxesEl;
32015         }
32016         
32017         this.boxesEl = this.el.select('.mas-boxes').first();
32018         
32019         return this.boxesEl;
32020     },
32021     
32022     
32023     initEvents : function()
32024     {
32025         var _this = this;
32026         
32027         if(this.isAutoInitial){
32028             Roo.log('hook children rendered');
32029             this.on('childrenrendered', function() {
32030                 Roo.log('children rendered');
32031                 _this.initial();
32032             } ,this);
32033         }
32034         
32035     },
32036     
32037     initial : function()
32038     {
32039         this.reloadItems();
32040
32041         this.currentSize = this.el.getBox(true);
32042
32043         /// was window resize... - let's see if this works..
32044         Roo.EventManager.onWindowResize(this.resize, this); 
32045
32046         if(!this.isAutoInitial){
32047             this.layout();
32048             return;
32049         }
32050         
32051         this.layout.defer(500,this);
32052     },
32053     
32054     reloadItems: function()
32055     {
32056         this.bricks = this.el.select('.masonry-brick', true);
32057         
32058         this.bricks.each(function(b) {
32059             //Roo.log(b.getSize());
32060             if (!b.attr('originalwidth')) {
32061                 b.attr('originalwidth',  b.getSize().width);
32062             }
32063             
32064         });
32065         
32066         Roo.log(this.bricks.elements.length);
32067     },
32068     
32069     resize : function()
32070     {
32071         Roo.log('resize');
32072         var cs = this.el.getBox(true);
32073         
32074         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32075             Roo.log("no change in with or X");
32076             return;
32077         }
32078         this.currentSize = cs;
32079         this.layout();
32080     },
32081     
32082     layout : function()
32083     {
32084          Roo.log('layout');
32085         this._resetLayout();
32086         //this._manageStamps();
32087       
32088         // don't animate first layout
32089         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32090         this.layoutItems( isInstant );
32091       
32092         // flag for initalized
32093         this._isLayoutInited = true;
32094     },
32095     
32096     layoutItems : function( isInstant )
32097     {
32098         //var items = this._getItemsForLayout( this.items );
32099         // original code supports filtering layout items.. we just ignore it..
32100         
32101         this._layoutItems( this.bricks , isInstant );
32102       
32103         this._postLayout();
32104     },
32105     _layoutItems : function ( items , isInstant)
32106     {
32107        //this.fireEvent( 'layout', this, items );
32108     
32109
32110         if ( !items || !items.elements.length ) {
32111           // no items, emit event with empty array
32112             return;
32113         }
32114
32115         var queue = [];
32116         items.each(function(item) {
32117             Roo.log("layout item");
32118             Roo.log(item);
32119             // get x/y object from method
32120             var position = this._getItemLayoutPosition( item );
32121             // enqueue
32122             position.item = item;
32123             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32124             queue.push( position );
32125         }, this);
32126       
32127         this._processLayoutQueue( queue );
32128     },
32129     /** Sets position of item in DOM
32130     * @param {Element} item
32131     * @param {Number} x - horizontal position
32132     * @param {Number} y - vertical position
32133     * @param {Boolean} isInstant - disables transitions
32134     */
32135     _processLayoutQueue : function( queue )
32136     {
32137         for ( var i=0, len = queue.length; i < len; i++ ) {
32138             var obj = queue[i];
32139             obj.item.position('absolute');
32140             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32141         }
32142     },
32143       
32144     
32145     /**
32146     * Any logic you want to do after each layout,
32147     * i.e. size the container
32148     */
32149     _postLayout : function()
32150     {
32151         this.resizeContainer();
32152     },
32153     
32154     resizeContainer : function()
32155     {
32156         if ( !this.isResizingContainer ) {
32157             return;
32158         }
32159         var size = this._getContainerSize();
32160         if ( size ) {
32161             this.el.setSize(size.width,size.height);
32162             this.boxesEl.setSize(size.width,size.height);
32163         }
32164     },
32165     
32166     
32167     
32168     _resetLayout : function()
32169     {
32170         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32171         this.colWidth = this.el.getWidth();
32172         //this.gutter = this.el.getWidth(); 
32173         
32174         this.measureColumns();
32175
32176         // reset column Y
32177         var i = this.cols;
32178         this.colYs = [];
32179         while (i--) {
32180             this.colYs.push( 0 );
32181         }
32182     
32183         this.maxY = 0;
32184     },
32185
32186     measureColumns : function()
32187     {
32188         this.getContainerWidth();
32189       // if columnWidth is 0, default to outerWidth of first item
32190         if ( !this.columnWidth ) {
32191             var firstItem = this.bricks.first();
32192             Roo.log(firstItem);
32193             this.columnWidth  = this.containerWidth;
32194             if (firstItem && firstItem.attr('originalwidth') ) {
32195                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32196             }
32197             // columnWidth fall back to item of first element
32198             Roo.log("set column width?");
32199                         this.initialColumnWidth = this.columnWidth  ;
32200
32201             // if first elem has no width, default to size of container
32202             
32203         }
32204         
32205         
32206         if (this.initialColumnWidth) {
32207             this.columnWidth = this.initialColumnWidth;
32208         }
32209         
32210         
32211             
32212         // column width is fixed at the top - however if container width get's smaller we should
32213         // reduce it...
32214         
32215         // this bit calcs how man columns..
32216             
32217         var columnWidth = this.columnWidth += this.gutter;
32218       
32219         // calculate columns
32220         var containerWidth = this.containerWidth + this.gutter;
32221         
32222         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32223         // fix rounding errors, typically with gutters
32224         var excess = columnWidth - containerWidth % columnWidth;
32225         
32226         
32227         // if overshoot is less than a pixel, round up, otherwise floor it
32228         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32229         cols = Math[ mathMethod ]( cols );
32230         this.cols = Math.max( cols, 1 );
32231         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32232         
32233          // padding positioning..
32234         var totalColWidth = this.cols * this.columnWidth;
32235         var padavail = this.containerWidth - totalColWidth;
32236         // so for 2 columns - we need 3 'pads'
32237         
32238         var padNeeded = (1+this.cols) * this.padWidth;
32239         
32240         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32241         
32242         this.columnWidth += padExtra
32243         //this.padWidth = Math.floor(padavail /  ( this.cols));
32244         
32245         // adjust colum width so that padding is fixed??
32246         
32247         // we have 3 columns ... total = width * 3
32248         // we have X left over... that should be used by 
32249         
32250         //if (this.expandC) {
32251             
32252         //}
32253         
32254         
32255         
32256     },
32257     
32258     getContainerWidth : function()
32259     {
32260        /* // container is parent if fit width
32261         var container = this.isFitWidth ? this.element.parentNode : this.element;
32262         // check that this.size and size are there
32263         // IE8 triggers resize on body size change, so they might not be
32264         
32265         var size = getSize( container );  //FIXME
32266         this.containerWidth = size && size.innerWidth; //FIXME
32267         */
32268          
32269         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32270         
32271     },
32272     
32273     _getItemLayoutPosition : function( item )  // what is item?
32274     {
32275         // we resize the item to our columnWidth..
32276       
32277         item.setWidth(this.columnWidth);
32278         item.autoBoxAdjust  = false;
32279         
32280         var sz = item.getSize();
32281  
32282         // how many columns does this brick span
32283         var remainder = this.containerWidth % this.columnWidth;
32284         
32285         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32286         // round if off by 1 pixel, otherwise use ceil
32287         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32288         colSpan = Math.min( colSpan, this.cols );
32289         
32290         // normally this should be '1' as we dont' currently allow multi width columns..
32291         
32292         var colGroup = this._getColGroup( colSpan );
32293         // get the minimum Y value from the columns
32294         var minimumY = Math.min.apply( Math, colGroup );
32295         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32296         
32297         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32298          
32299         // position the brick
32300         var position = {
32301             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32302             y: this.currentSize.y + minimumY + this.padHeight
32303         };
32304         
32305         Roo.log(position);
32306         // apply setHeight to necessary columns
32307         var setHeight = minimumY + sz.height + this.padHeight;
32308         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32309         
32310         var setSpan = this.cols + 1 - colGroup.length;
32311         for ( var i = 0; i < setSpan; i++ ) {
32312           this.colYs[ shortColIndex + i ] = setHeight ;
32313         }
32314       
32315         return position;
32316     },
32317     
32318     /**
32319      * @param {Number} colSpan - number of columns the element spans
32320      * @returns {Array} colGroup
32321      */
32322     _getColGroup : function( colSpan )
32323     {
32324         if ( colSpan < 2 ) {
32325           // if brick spans only one column, use all the column Ys
32326           return this.colYs;
32327         }
32328       
32329         var colGroup = [];
32330         // how many different places could this brick fit horizontally
32331         var groupCount = this.cols + 1 - colSpan;
32332         // for each group potential horizontal position
32333         for ( var i = 0; i < groupCount; i++ ) {
32334           // make an array of colY values for that one group
32335           var groupColYs = this.colYs.slice( i, i + colSpan );
32336           // and get the max value of the array
32337           colGroup[i] = Math.max.apply( Math, groupColYs );
32338         }
32339         return colGroup;
32340     },
32341     /*
32342     _manageStamp : function( stamp )
32343     {
32344         var stampSize =  stamp.getSize();
32345         var offset = stamp.getBox();
32346         // get the columns that this stamp affects
32347         var firstX = this.isOriginLeft ? offset.x : offset.right;
32348         var lastX = firstX + stampSize.width;
32349         var firstCol = Math.floor( firstX / this.columnWidth );
32350         firstCol = Math.max( 0, firstCol );
32351         
32352         var lastCol = Math.floor( lastX / this.columnWidth );
32353         // lastCol should not go over if multiple of columnWidth #425
32354         lastCol -= lastX % this.columnWidth ? 0 : 1;
32355         lastCol = Math.min( this.cols - 1, lastCol );
32356         
32357         // set colYs to bottom of the stamp
32358         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32359             stampSize.height;
32360             
32361         for ( var i = firstCol; i <= lastCol; i++ ) {
32362           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32363         }
32364     },
32365     */
32366     
32367     _getContainerSize : function()
32368     {
32369         this.maxY = Math.max.apply( Math, this.colYs );
32370         var size = {
32371             height: this.maxY
32372         };
32373       
32374         if ( this.isFitWidth ) {
32375             size.width = this._getContainerFitWidth();
32376         }
32377       
32378         return size;
32379     },
32380     
32381     _getContainerFitWidth : function()
32382     {
32383         var unusedCols = 0;
32384         // count unused columns
32385         var i = this.cols;
32386         while ( --i ) {
32387           if ( this.colYs[i] !== 0 ) {
32388             break;
32389           }
32390           unusedCols++;
32391         }
32392         // fit container to columns that have been used
32393         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32394     },
32395     
32396     needsResizeLayout : function()
32397     {
32398         var previousWidth = this.containerWidth;
32399         this.getContainerWidth();
32400         return previousWidth !== this.containerWidth;
32401     }
32402  
32403 });
32404
32405  
32406
32407  /*
32408  * - LGPL
32409  *
32410  * element
32411  * 
32412  */
32413
32414 /**
32415  * @class Roo.bootstrap.MasonryBrick
32416  * @extends Roo.bootstrap.Component
32417  * Bootstrap MasonryBrick class
32418  * 
32419  * @constructor
32420  * Create a new MasonryBrick
32421  * @param {Object} config The config object
32422  */
32423
32424 Roo.bootstrap.MasonryBrick = function(config){
32425     
32426     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32427     
32428     Roo.bootstrap.MasonryBrick.register(this);
32429     
32430     this.addEvents({
32431         // raw events
32432         /**
32433          * @event click
32434          * When a MasonryBrick is clcik
32435          * @param {Roo.bootstrap.MasonryBrick} this
32436          * @param {Roo.EventObject} e
32437          */
32438         "click" : true
32439     });
32440 };
32441
32442 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32443     
32444     /**
32445      * @cfg {String} title
32446      */   
32447     title : '',
32448     /**
32449      * @cfg {String} html
32450      */   
32451     html : '',
32452     /**
32453      * @cfg {String} bgimage
32454      */   
32455     bgimage : '',
32456     /**
32457      * @cfg {String} videourl
32458      */   
32459     videourl : '',
32460     /**
32461      * @cfg {String} cls
32462      */   
32463     cls : '',
32464     /**
32465      * @cfg {String} href
32466      */   
32467     href : '',
32468     /**
32469      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32470      */   
32471     size : 'xs',
32472     
32473     /**
32474      * @cfg {String} placetitle (center|bottom)
32475      */   
32476     placetitle : '',
32477     
32478     /**
32479      * @cfg {Boolean} isFitContainer defalut true
32480      */   
32481     isFitContainer : true, 
32482     
32483     /**
32484      * @cfg {Boolean} preventDefault defalut false
32485      */   
32486     preventDefault : false, 
32487     
32488     /**
32489      * @cfg {Boolean} inverse defalut false
32490      */   
32491     maskInverse : false, 
32492     
32493     getAutoCreate : function()
32494     {
32495         if(!this.isFitContainer){
32496             return this.getSplitAutoCreate();
32497         }
32498         
32499         var cls = 'masonry-brick masonry-brick-full';
32500         
32501         if(this.href.length){
32502             cls += ' masonry-brick-link';
32503         }
32504         
32505         if(this.bgimage.length){
32506             cls += ' masonry-brick-image';
32507         }
32508         
32509         if(this.maskInverse){
32510             cls += ' mask-inverse';
32511         }
32512         
32513         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32514             cls += ' enable-mask';
32515         }
32516         
32517         if(this.size){
32518             cls += ' masonry-' + this.size + '-brick';
32519         }
32520         
32521         if(this.placetitle.length){
32522             
32523             switch (this.placetitle) {
32524                 case 'center' :
32525                     cls += ' masonry-center-title';
32526                     break;
32527                 case 'bottom' :
32528                     cls += ' masonry-bottom-title';
32529                     break;
32530                 default:
32531                     break;
32532             }
32533             
32534         } else {
32535             if(!this.html.length && !this.bgimage.length){
32536                 cls += ' masonry-center-title';
32537             }
32538
32539             if(!this.html.length && this.bgimage.length){
32540                 cls += ' masonry-bottom-title';
32541             }
32542         }
32543         
32544         if(this.cls){
32545             cls += ' ' + this.cls;
32546         }
32547         
32548         var cfg = {
32549             tag: (this.href.length) ? 'a' : 'div',
32550             cls: cls,
32551             cn: [
32552                 {
32553                     tag: 'div',
32554                     cls: 'masonry-brick-mask'
32555                 },
32556                 {
32557                     tag: 'div',
32558                     cls: 'masonry-brick-paragraph',
32559                     cn: []
32560                 }
32561             ]
32562         };
32563         
32564         if(this.href.length){
32565             cfg.href = this.href;
32566         }
32567         
32568         var cn = cfg.cn[1].cn;
32569         
32570         if(this.title.length){
32571             cn.push({
32572                 tag: 'h4',
32573                 cls: 'masonry-brick-title',
32574                 html: this.title
32575             });
32576         }
32577         
32578         if(this.html.length){
32579             cn.push({
32580                 tag: 'p',
32581                 cls: 'masonry-brick-text',
32582                 html: this.html
32583             });
32584         }
32585         
32586         if (!this.title.length && !this.html.length) {
32587             cfg.cn[1].cls += ' hide';
32588         }
32589         
32590         if(this.bgimage.length){
32591             cfg.cn.push({
32592                 tag: 'img',
32593                 cls: 'masonry-brick-image-view',
32594                 src: this.bgimage
32595             });
32596         }
32597         
32598         if(this.videourl.length){
32599             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32600             // youtube support only?
32601             cfg.cn.push({
32602                 tag: 'iframe',
32603                 cls: 'masonry-brick-image-view',
32604                 src: vurl,
32605                 frameborder : 0,
32606                 allowfullscreen : true
32607             });
32608         }
32609         
32610         return cfg;
32611         
32612     },
32613     
32614     getSplitAutoCreate : function()
32615     {
32616         var cls = 'masonry-brick masonry-brick-split';
32617         
32618         if(this.href.length){
32619             cls += ' masonry-brick-link';
32620         }
32621         
32622         if(this.bgimage.length){
32623             cls += ' masonry-brick-image';
32624         }
32625         
32626         if(this.size){
32627             cls += ' masonry-' + this.size + '-brick';
32628         }
32629         
32630         switch (this.placetitle) {
32631             case 'center' :
32632                 cls += ' masonry-center-title';
32633                 break;
32634             case 'bottom' :
32635                 cls += ' masonry-bottom-title';
32636                 break;
32637             default:
32638                 if(!this.bgimage.length){
32639                     cls += ' masonry-center-title';
32640                 }
32641
32642                 if(this.bgimage.length){
32643                     cls += ' masonry-bottom-title';
32644                 }
32645                 break;
32646         }
32647         
32648         if(this.cls){
32649             cls += ' ' + this.cls;
32650         }
32651         
32652         var cfg = {
32653             tag: (this.href.length) ? 'a' : 'div',
32654             cls: cls,
32655             cn: [
32656                 {
32657                     tag: 'div',
32658                     cls: 'masonry-brick-split-head',
32659                     cn: [
32660                         {
32661                             tag: 'div',
32662                             cls: 'masonry-brick-paragraph',
32663                             cn: []
32664                         }
32665                     ]
32666                 },
32667                 {
32668                     tag: 'div',
32669                     cls: 'masonry-brick-split-body',
32670                     cn: []
32671                 }
32672             ]
32673         };
32674         
32675         if(this.href.length){
32676             cfg.href = this.href;
32677         }
32678         
32679         if(this.title.length){
32680             cfg.cn[0].cn[0].cn.push({
32681                 tag: 'h4',
32682                 cls: 'masonry-brick-title',
32683                 html: this.title
32684             });
32685         }
32686         
32687         if(this.html.length){
32688             cfg.cn[1].cn.push({
32689                 tag: 'p',
32690                 cls: 'masonry-brick-text',
32691                 html: this.html
32692             });
32693         }
32694
32695         if(this.bgimage.length){
32696             cfg.cn[0].cn.push({
32697                 tag: 'img',
32698                 cls: 'masonry-brick-image-view',
32699                 src: this.bgimage
32700             });
32701         }
32702         
32703         if(this.videourl.length){
32704             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32705             // youtube support only?
32706             cfg.cn[0].cn.cn.push({
32707                 tag: 'iframe',
32708                 cls: 'masonry-brick-image-view',
32709                 src: vurl,
32710                 frameborder : 0,
32711                 allowfullscreen : true
32712             });
32713         }
32714         
32715         return cfg;
32716     },
32717     
32718     initEvents: function() 
32719     {
32720         switch (this.size) {
32721             case 'xs' :
32722                 this.x = 1;
32723                 this.y = 1;
32724                 break;
32725             case 'sm' :
32726                 this.x = 2;
32727                 this.y = 2;
32728                 break;
32729             case 'md' :
32730             case 'md-left' :
32731             case 'md-right' :
32732                 this.x = 3;
32733                 this.y = 3;
32734                 break;
32735             case 'tall' :
32736                 this.x = 2;
32737                 this.y = 3;
32738                 break;
32739             case 'wide' :
32740                 this.x = 3;
32741                 this.y = 2;
32742                 break;
32743             case 'wide-thin' :
32744                 this.x = 3;
32745                 this.y = 1;
32746                 break;
32747                         
32748             default :
32749                 break;
32750         }
32751         
32752         if(Roo.isTouch){
32753             this.el.on('touchstart', this.onTouchStart, this);
32754             this.el.on('touchmove', this.onTouchMove, this);
32755             this.el.on('touchend', this.onTouchEnd, this);
32756             this.el.on('contextmenu', this.onContextMenu, this);
32757         } else {
32758             this.el.on('mouseenter'  ,this.enter, this);
32759             this.el.on('mouseleave', this.leave, this);
32760             this.el.on('click', this.onClick, this);
32761         }
32762         
32763         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32764             this.parent().bricks.push(this);   
32765         }
32766         
32767     },
32768     
32769     onClick: function(e, el)
32770     {
32771         var time = this.endTimer - this.startTimer;
32772         // Roo.log(e.preventDefault());
32773         if(Roo.isTouch){
32774             if(time > 1000){
32775                 e.preventDefault();
32776                 return;
32777             }
32778         }
32779         
32780         if(!this.preventDefault){
32781             return;
32782         }
32783         
32784         e.preventDefault();
32785         
32786         if (this.activeClass != '') {
32787             this.selectBrick();
32788         }
32789         
32790         this.fireEvent('click', this, e);
32791     },
32792     
32793     enter: function(e, el)
32794     {
32795         e.preventDefault();
32796         
32797         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32798             return;
32799         }
32800         
32801         if(this.bgimage.length && this.html.length){
32802             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32803         }
32804     },
32805     
32806     leave: function(e, el)
32807     {
32808         e.preventDefault();
32809         
32810         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32811             return;
32812         }
32813         
32814         if(this.bgimage.length && this.html.length){
32815             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32816         }
32817     },
32818     
32819     onTouchStart: function(e, el)
32820     {
32821 //        e.preventDefault();
32822         
32823         this.touchmoved = false;
32824         
32825         if(!this.isFitContainer){
32826             return;
32827         }
32828         
32829         if(!this.bgimage.length || !this.html.length){
32830             return;
32831         }
32832         
32833         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32834         
32835         this.timer = new Date().getTime();
32836         
32837     },
32838     
32839     onTouchMove: function(e, el)
32840     {
32841         this.touchmoved = true;
32842     },
32843     
32844     onContextMenu : function(e,el)
32845     {
32846         e.preventDefault();
32847         e.stopPropagation();
32848         return false;
32849     },
32850     
32851     onTouchEnd: function(e, el)
32852     {
32853 //        e.preventDefault();
32854         
32855         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32856         
32857             this.leave(e,el);
32858             
32859             return;
32860         }
32861         
32862         if(!this.bgimage.length || !this.html.length){
32863             
32864             if(this.href.length){
32865                 window.location.href = this.href;
32866             }
32867             
32868             return;
32869         }
32870         
32871         if(!this.isFitContainer){
32872             return;
32873         }
32874         
32875         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32876         
32877         window.location.href = this.href;
32878     },
32879     
32880     //selection on single brick only
32881     selectBrick : function() {
32882         
32883         if (!this.parentId) {
32884             return;
32885         }
32886         
32887         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32888         var index = m.selectedBrick.indexOf(this.id);
32889         
32890         if ( index > -1) {
32891             m.selectedBrick.splice(index,1);
32892             this.el.removeClass(this.activeClass);
32893             return;
32894         }
32895         
32896         for(var i = 0; i < m.selectedBrick.length; i++) {
32897             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32898             b.el.removeClass(b.activeClass);
32899         }
32900         
32901         m.selectedBrick = [];
32902         
32903         m.selectedBrick.push(this.id);
32904         this.el.addClass(this.activeClass);
32905         return;
32906     },
32907     
32908     isSelected : function(){
32909         return this.el.hasClass(this.activeClass);
32910         
32911     }
32912 });
32913
32914 Roo.apply(Roo.bootstrap.MasonryBrick, {
32915     
32916     //groups: {},
32917     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32918      /**
32919     * register a Masonry Brick
32920     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32921     */
32922     
32923     register : function(brick)
32924     {
32925         //this.groups[brick.id] = brick;
32926         this.groups.add(brick.id, brick);
32927     },
32928     /**
32929     * fetch a  masonry brick based on the masonry brick ID
32930     * @param {string} the masonry brick to add
32931     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32932     */
32933     
32934     get: function(brick_id) 
32935     {
32936         // if (typeof(this.groups[brick_id]) == 'undefined') {
32937         //     return false;
32938         // }
32939         // return this.groups[brick_id] ;
32940         
32941         if(this.groups.key(brick_id)) {
32942             return this.groups.key(brick_id);
32943         }
32944         
32945         return false;
32946     }
32947     
32948     
32949     
32950 });
32951
32952  /*
32953  * - LGPL
32954  *
32955  * element
32956  * 
32957  */
32958
32959 /**
32960  * @class Roo.bootstrap.Brick
32961  * @extends Roo.bootstrap.Component
32962  * Bootstrap Brick class
32963  * 
32964  * @constructor
32965  * Create a new Brick
32966  * @param {Object} config The config object
32967  */
32968
32969 Roo.bootstrap.Brick = function(config){
32970     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32971     
32972     this.addEvents({
32973         // raw events
32974         /**
32975          * @event click
32976          * When a Brick is click
32977          * @param {Roo.bootstrap.Brick} this
32978          * @param {Roo.EventObject} e
32979          */
32980         "click" : true
32981     });
32982 };
32983
32984 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32985     
32986     /**
32987      * @cfg {String} title
32988      */   
32989     title : '',
32990     /**
32991      * @cfg {String} html
32992      */   
32993     html : '',
32994     /**
32995      * @cfg {String} bgimage
32996      */   
32997     bgimage : '',
32998     /**
32999      * @cfg {String} cls
33000      */   
33001     cls : '',
33002     /**
33003      * @cfg {String} href
33004      */   
33005     href : '',
33006     /**
33007      * @cfg {String} video
33008      */   
33009     video : '',
33010     /**
33011      * @cfg {Boolean} square
33012      */   
33013     square : true,
33014     
33015     getAutoCreate : function()
33016     {
33017         var cls = 'roo-brick';
33018         
33019         if(this.href.length){
33020             cls += ' roo-brick-link';
33021         }
33022         
33023         if(this.bgimage.length){
33024             cls += ' roo-brick-image';
33025         }
33026         
33027         if(!this.html.length && !this.bgimage.length){
33028             cls += ' roo-brick-center-title';
33029         }
33030         
33031         if(!this.html.length && this.bgimage.length){
33032             cls += ' roo-brick-bottom-title';
33033         }
33034         
33035         if(this.cls){
33036             cls += ' ' + this.cls;
33037         }
33038         
33039         var cfg = {
33040             tag: (this.href.length) ? 'a' : 'div',
33041             cls: cls,
33042             cn: [
33043                 {
33044                     tag: 'div',
33045                     cls: 'roo-brick-paragraph',
33046                     cn: []
33047                 }
33048             ]
33049         };
33050         
33051         if(this.href.length){
33052             cfg.href = this.href;
33053         }
33054         
33055         var cn = cfg.cn[0].cn;
33056         
33057         if(this.title.length){
33058             cn.push({
33059                 tag: 'h4',
33060                 cls: 'roo-brick-title',
33061                 html: this.title
33062             });
33063         }
33064         
33065         if(this.html.length){
33066             cn.push({
33067                 tag: 'p',
33068                 cls: 'roo-brick-text',
33069                 html: this.html
33070             });
33071         } else {
33072             cn.cls += ' hide';
33073         }
33074         
33075         if(this.bgimage.length){
33076             cfg.cn.push({
33077                 tag: 'img',
33078                 cls: 'roo-brick-image-view',
33079                 src: this.bgimage
33080             });
33081         }
33082         
33083         return cfg;
33084     },
33085     
33086     initEvents: function() 
33087     {
33088         if(this.title.length || this.html.length){
33089             this.el.on('mouseenter'  ,this.enter, this);
33090             this.el.on('mouseleave', this.leave, this);
33091         }
33092         
33093         Roo.EventManager.onWindowResize(this.resize, this); 
33094         
33095         if(this.bgimage.length){
33096             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33097             this.imageEl.on('load', this.onImageLoad, this);
33098             return;
33099         }
33100         
33101         this.resize();
33102     },
33103     
33104     onImageLoad : function()
33105     {
33106         this.resize();
33107     },
33108     
33109     resize : function()
33110     {
33111         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33112         
33113         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33114         
33115         if(this.bgimage.length){
33116             var image = this.el.select('.roo-brick-image-view', true).first();
33117             
33118             image.setWidth(paragraph.getWidth());
33119             
33120             if(this.square){
33121                 image.setHeight(paragraph.getWidth());
33122             }
33123             
33124             this.el.setHeight(image.getHeight());
33125             paragraph.setHeight(image.getHeight());
33126             
33127         }
33128         
33129     },
33130     
33131     enter: function(e, el)
33132     {
33133         e.preventDefault();
33134         
33135         if(this.bgimage.length){
33136             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33137             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33138         }
33139     },
33140     
33141     leave: function(e, el)
33142     {
33143         e.preventDefault();
33144         
33145         if(this.bgimage.length){
33146             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33147             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33148         }
33149     }
33150     
33151 });
33152
33153  
33154
33155  /*
33156  * - LGPL
33157  *
33158  * Number field 
33159  */
33160
33161 /**
33162  * @class Roo.bootstrap.NumberField
33163  * @extends Roo.bootstrap.Input
33164  * Bootstrap NumberField class
33165  * 
33166  * 
33167  * 
33168  * 
33169  * @constructor
33170  * Create a new NumberField
33171  * @param {Object} config The config object
33172  */
33173
33174 Roo.bootstrap.NumberField = function(config){
33175     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33176 };
33177
33178 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33179     
33180     /**
33181      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33182      */
33183     allowDecimals : true,
33184     /**
33185      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33186      */
33187     decimalSeparator : ".",
33188     /**
33189      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33190      */
33191     decimalPrecision : 2,
33192     /**
33193      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33194      */
33195     allowNegative : true,
33196     
33197     /**
33198      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33199      */
33200     allowZero: true,
33201     /**
33202      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33203      */
33204     minValue : Number.NEGATIVE_INFINITY,
33205     /**
33206      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33207      */
33208     maxValue : Number.MAX_VALUE,
33209     /**
33210      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33211      */
33212     minText : "The minimum value for this field is {0}",
33213     /**
33214      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33215      */
33216     maxText : "The maximum value for this field is {0}",
33217     /**
33218      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33219      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33220      */
33221     nanText : "{0} is not a valid number",
33222     /**
33223      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33224      */
33225     castInt : true,
33226     /**
33227      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33228      */
33229     thousandsDelimiter : false,
33230     /**
33231      * @cfg {String} valueAlign alignment of value
33232      */
33233     valueAlign : "left",
33234
33235     getAutoCreate : function()
33236     {
33237         var hiddenInput = {
33238             tag: 'input',
33239             type: 'hidden',
33240             id: Roo.id(),
33241             cls: 'hidden-number-input'
33242         };
33243         
33244         if (this.name) {
33245             hiddenInput.name = this.name;
33246         }
33247         
33248         this.name = '';
33249         
33250         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33251         
33252         this.name = hiddenInput.name;
33253         
33254         if(cfg.cn.length > 0) {
33255             cfg.cn.push(hiddenInput);
33256         }
33257         
33258         return cfg;
33259     },
33260
33261     // private
33262     initEvents : function()
33263     {   
33264         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33265         
33266         var allowed = "0123456789";
33267         
33268         if(this.allowDecimals){
33269             allowed += this.decimalSeparator;
33270         }
33271         
33272         if(this.allowNegative){
33273             allowed += "-";
33274         }
33275         
33276         if(this.thousandsDelimiter) {
33277             allowed += ",";
33278         }
33279         
33280         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33281         
33282         var keyPress = function(e){
33283             
33284             var k = e.getKey();
33285             
33286             var c = e.getCharCode();
33287             
33288             if(
33289                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33290                     allowed.indexOf(String.fromCharCode(c)) === -1
33291             ){
33292                 e.stopEvent();
33293                 return;
33294             }
33295             
33296             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33297                 return;
33298             }
33299             
33300             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33301                 e.stopEvent();
33302             }
33303         };
33304         
33305         this.el.on("keypress", keyPress, this);
33306     },
33307     
33308     validateValue : function(value)
33309     {
33310         
33311         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33312             return false;
33313         }
33314         
33315         var num = this.parseValue(value);
33316         
33317         if(isNaN(num)){
33318             this.markInvalid(String.format(this.nanText, value));
33319             return false;
33320         }
33321         
33322         if(num < this.minValue){
33323             this.markInvalid(String.format(this.minText, this.minValue));
33324             return false;
33325         }
33326         
33327         if(num > this.maxValue){
33328             this.markInvalid(String.format(this.maxText, this.maxValue));
33329             return false;
33330         }
33331         
33332         return true;
33333     },
33334
33335     getValue : function()
33336     {
33337         var v = this.hiddenEl().getValue();
33338         
33339         return this.fixPrecision(this.parseValue(v));
33340     },
33341
33342     parseValue : function(value)
33343     {
33344         if(this.thousandsDelimiter) {
33345             value += "";
33346             r = new RegExp(",", "g");
33347             value = value.replace(r, "");
33348         }
33349         
33350         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33351         return isNaN(value) ? '' : value;
33352     },
33353
33354     fixPrecision : function(value)
33355     {
33356         if(this.thousandsDelimiter) {
33357             value += "";
33358             r = new RegExp(",", "g");
33359             value = value.replace(r, "");
33360         }
33361         
33362         var nan = isNaN(value);
33363         
33364         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33365             return nan ? '' : value;
33366         }
33367         return parseFloat(value).toFixed(this.decimalPrecision);
33368     },
33369
33370     setValue : function(v)
33371     {
33372         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33373         
33374         this.value = v;
33375         
33376         if(this.rendered){
33377             
33378             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33379             
33380             this.inputEl().dom.value = (v == '') ? '' :
33381                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33382             
33383             if(!this.allowZero && v === '0') {
33384                 this.hiddenEl().dom.value = '';
33385                 this.inputEl().dom.value = '';
33386             }
33387             
33388             this.validate();
33389         }
33390     },
33391
33392     decimalPrecisionFcn : function(v)
33393     {
33394         return Math.floor(v);
33395     },
33396
33397     beforeBlur : function()
33398     {
33399         if(!this.castInt){
33400             return;
33401         }
33402         
33403         var v = this.parseValue(this.getRawValue());
33404         
33405         if(v || v === 0){
33406             this.setValue(v);
33407         }
33408     },
33409     
33410     hiddenEl : function()
33411     {
33412         return this.el.select('input.hidden-number-input',true).first();
33413     }
33414     
33415 });
33416
33417  
33418
33419 /*
33420 * Licence: LGPL
33421 */
33422
33423 /**
33424  * @class Roo.bootstrap.DocumentSlider
33425  * @extends Roo.bootstrap.Component
33426  * Bootstrap DocumentSlider class
33427  * 
33428  * @constructor
33429  * Create a new DocumentViewer
33430  * @param {Object} config The config object
33431  */
33432
33433 Roo.bootstrap.DocumentSlider = function(config){
33434     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33435     
33436     this.files = [];
33437     
33438     this.addEvents({
33439         /**
33440          * @event initial
33441          * Fire after initEvent
33442          * @param {Roo.bootstrap.DocumentSlider} this
33443          */
33444         "initial" : true,
33445         /**
33446          * @event update
33447          * Fire after update
33448          * @param {Roo.bootstrap.DocumentSlider} this
33449          */
33450         "update" : true,
33451         /**
33452          * @event click
33453          * Fire after click
33454          * @param {Roo.bootstrap.DocumentSlider} this
33455          */
33456         "click" : true
33457     });
33458 };
33459
33460 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33461     
33462     files : false,
33463     
33464     indicator : 0,
33465     
33466     getAutoCreate : function()
33467     {
33468         var cfg = {
33469             tag : 'div',
33470             cls : 'roo-document-slider',
33471             cn : [
33472                 {
33473                     tag : 'div',
33474                     cls : 'roo-document-slider-header',
33475                     cn : [
33476                         {
33477                             tag : 'div',
33478                             cls : 'roo-document-slider-header-title'
33479                         }
33480                     ]
33481                 },
33482                 {
33483                     tag : 'div',
33484                     cls : 'roo-document-slider-body',
33485                     cn : [
33486                         {
33487                             tag : 'div',
33488                             cls : 'roo-document-slider-prev',
33489                             cn : [
33490                                 {
33491                                     tag : 'i',
33492                                     cls : 'fa fa-chevron-left'
33493                                 }
33494                             ]
33495                         },
33496                         {
33497                             tag : 'div',
33498                             cls : 'roo-document-slider-thumb',
33499                             cn : [
33500                                 {
33501                                     tag : 'img',
33502                                     cls : 'roo-document-slider-image'
33503                                 }
33504                             ]
33505                         },
33506                         {
33507                             tag : 'div',
33508                             cls : 'roo-document-slider-next',
33509                             cn : [
33510                                 {
33511                                     tag : 'i',
33512                                     cls : 'fa fa-chevron-right'
33513                                 }
33514                             ]
33515                         }
33516                     ]
33517                 }
33518             ]
33519         };
33520         
33521         return cfg;
33522     },
33523     
33524     initEvents : function()
33525     {
33526         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33527         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33528         
33529         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33530         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33531         
33532         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33533         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33534         
33535         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33536         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33537         
33538         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33539         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33540         
33541         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33542         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33543         
33544         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33545         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33546         
33547         this.thumbEl.on('click', this.onClick, this);
33548         
33549         this.prevIndicator.on('click', this.prev, this);
33550         
33551         this.nextIndicator.on('click', this.next, this);
33552         
33553     },
33554     
33555     initial : function()
33556     {
33557         if(this.files.length){
33558             this.indicator = 1;
33559             this.update()
33560         }
33561         
33562         this.fireEvent('initial', this);
33563     },
33564     
33565     update : function()
33566     {
33567         this.imageEl.attr('src', this.files[this.indicator - 1]);
33568         
33569         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33570         
33571         this.prevIndicator.show();
33572         
33573         if(this.indicator == 1){
33574             this.prevIndicator.hide();
33575         }
33576         
33577         this.nextIndicator.show();
33578         
33579         if(this.indicator == this.files.length){
33580             this.nextIndicator.hide();
33581         }
33582         
33583         this.thumbEl.scrollTo('top');
33584         
33585         this.fireEvent('update', this);
33586     },
33587     
33588     onClick : function(e)
33589     {
33590         e.preventDefault();
33591         
33592         this.fireEvent('click', this);
33593     },
33594     
33595     prev : function(e)
33596     {
33597         e.preventDefault();
33598         
33599         this.indicator = Math.max(1, this.indicator - 1);
33600         
33601         this.update();
33602     },
33603     
33604     next : function(e)
33605     {
33606         e.preventDefault();
33607         
33608         this.indicator = Math.min(this.files.length, this.indicator + 1);
33609         
33610         this.update();
33611     }
33612 });
33613 /*
33614  * - LGPL
33615  *
33616  * RadioSet
33617  *
33618  *
33619  */
33620
33621 /**
33622  * @class Roo.bootstrap.RadioSet
33623  * @extends Roo.bootstrap.Input
33624  * Bootstrap RadioSet class
33625  * @cfg {String} indicatorpos (left|right) default left
33626  * @cfg {Boolean} inline (true|false) inline the element (default true)
33627  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33628  * @constructor
33629  * Create a new RadioSet
33630  * @param {Object} config The config object
33631  */
33632
33633 Roo.bootstrap.RadioSet = function(config){
33634     
33635     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33636     
33637     this.radioes = [];
33638     
33639     Roo.bootstrap.RadioSet.register(this);
33640     
33641     this.addEvents({
33642         /**
33643         * @event check
33644         * Fires when the element is checked or unchecked.
33645         * @param {Roo.bootstrap.RadioSet} this This radio
33646         * @param {Roo.bootstrap.Radio} item The checked item
33647         */
33648        check : true,
33649        /**
33650         * @event click
33651         * Fires when the element is click.
33652         * @param {Roo.bootstrap.RadioSet} this This radio set
33653         * @param {Roo.bootstrap.Radio} item The checked item
33654         * @param {Roo.EventObject} e The event object
33655         */
33656        click : true
33657     });
33658     
33659 };
33660
33661 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33662
33663     radioes : false,
33664     
33665     inline : true,
33666     
33667     weight : '',
33668     
33669     indicatorpos : 'left',
33670     
33671     getAutoCreate : function()
33672     {
33673         var label = {
33674             tag : 'label',
33675             cls : 'roo-radio-set-label',
33676             cn : [
33677                 {
33678                     tag : 'span',
33679                     html : this.fieldLabel
33680                 }
33681             ]
33682         };
33683         
33684         if(this.indicatorpos == 'left'){
33685             label.cn.unshift({
33686                 tag : 'i',
33687                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33688                 tooltip : 'This field is required'
33689             });
33690         } else {
33691             label.cn.push({
33692                 tag : 'i',
33693                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33694                 tooltip : 'This field is required'
33695             });
33696         }
33697         
33698         var items = {
33699             tag : 'div',
33700             cls : 'roo-radio-set-items'
33701         };
33702         
33703         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33704         
33705         if (align === 'left' && this.fieldLabel.length) {
33706             
33707             items = {
33708                 cls : "roo-radio-set-right", 
33709                 cn: [
33710                     items
33711                 ]
33712             };
33713             
33714             if(this.labelWidth > 12){
33715                 label.style = "width: " + this.labelWidth + 'px';
33716             }
33717             
33718             if(this.labelWidth < 13 && this.labelmd == 0){
33719                 this.labelmd = this.labelWidth;
33720             }
33721             
33722             if(this.labellg > 0){
33723                 label.cls += ' col-lg-' + this.labellg;
33724                 items.cls += ' col-lg-' + (12 - this.labellg);
33725             }
33726             
33727             if(this.labelmd > 0){
33728                 label.cls += ' col-md-' + this.labelmd;
33729                 items.cls += ' col-md-' + (12 - this.labelmd);
33730             }
33731             
33732             if(this.labelsm > 0){
33733                 label.cls += ' col-sm-' + this.labelsm;
33734                 items.cls += ' col-sm-' + (12 - this.labelsm);
33735             }
33736             
33737             if(this.labelxs > 0){
33738                 label.cls += ' col-xs-' + this.labelxs;
33739                 items.cls += ' col-xs-' + (12 - this.labelxs);
33740             }
33741         }
33742         
33743         var cfg = {
33744             tag : 'div',
33745             cls : 'roo-radio-set',
33746             cn : [
33747                 {
33748                     tag : 'input',
33749                     cls : 'roo-radio-set-input',
33750                     type : 'hidden',
33751                     name : this.name,
33752                     value : this.value ? this.value :  ''
33753                 },
33754                 label,
33755                 items
33756             ]
33757         };
33758         
33759         if(this.weight.length){
33760             cfg.cls += ' roo-radio-' + this.weight;
33761         }
33762         
33763         if(this.inline) {
33764             cfg.cls += ' roo-radio-set-inline';
33765         }
33766         
33767         var settings=this;
33768         ['xs','sm','md','lg'].map(function(size){
33769             if (settings[size]) {
33770                 cfg.cls += ' col-' + size + '-' + settings[size];
33771             }
33772         });
33773         
33774         return cfg;
33775         
33776     },
33777
33778     initEvents : function()
33779     {
33780         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33781         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33782         
33783         if(!this.fieldLabel.length){
33784             this.labelEl.hide();
33785         }
33786         
33787         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33788         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33789         
33790         this.indicator = this.indicatorEl();
33791         
33792         if(this.indicator){
33793             this.indicator.addClass('invisible');
33794         }
33795         
33796         this.originalValue = this.getValue();
33797         
33798     },
33799     
33800     inputEl: function ()
33801     {
33802         return this.el.select('.roo-radio-set-input', true).first();
33803     },
33804     
33805     getChildContainer : function()
33806     {
33807         return this.itemsEl;
33808     },
33809     
33810     register : function(item)
33811     {
33812         this.radioes.push(item);
33813         
33814     },
33815     
33816     validate : function()
33817     {   
33818         if(this.getVisibilityEl().hasClass('hidden')){
33819             return true;
33820         }
33821         
33822         var valid = false;
33823         
33824         Roo.each(this.radioes, function(i){
33825             if(!i.checked){
33826                 return;
33827             }
33828             
33829             valid = true;
33830             return false;
33831         });
33832         
33833         if(this.allowBlank) {
33834             return true;
33835         }
33836         
33837         if(this.disabled || valid){
33838             this.markValid();
33839             return true;
33840         }
33841         
33842         this.markInvalid();
33843         return false;
33844         
33845     },
33846     
33847     markValid : function()
33848     {
33849         if(this.labelEl.isVisible(true)){
33850             this.indicatorEl().removeClass('visible');
33851             this.indicatorEl().addClass('invisible');
33852         }
33853         
33854         this.el.removeClass([this.invalidClass, this.validClass]);
33855         this.el.addClass(this.validClass);
33856         
33857         this.fireEvent('valid', this);
33858     },
33859     
33860     markInvalid : function(msg)
33861     {
33862         if(this.allowBlank || this.disabled){
33863             return;
33864         }
33865         
33866         if(this.labelEl.isVisible(true)){
33867             this.indicatorEl().removeClass('invisible');
33868             this.indicatorEl().addClass('visible');
33869         }
33870         
33871         this.el.removeClass([this.invalidClass, this.validClass]);
33872         this.el.addClass(this.invalidClass);
33873         
33874         this.fireEvent('invalid', this, msg);
33875         
33876     },
33877     
33878     setValue : function(v, suppressEvent)
33879     {   
33880         if(this.value === v){
33881             return;
33882         }
33883         
33884         this.value = v;
33885         
33886         if(this.rendered){
33887             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33888         }
33889         
33890         Roo.each(this.radioes, function(i){
33891             i.checked = false;
33892             i.el.removeClass('checked');
33893         });
33894         
33895         Roo.each(this.radioes, function(i){
33896             
33897             if(i.value === v || i.value.toString() === v.toString()){
33898                 i.checked = true;
33899                 i.el.addClass('checked');
33900                 
33901                 if(suppressEvent !== true){
33902                     this.fireEvent('check', this, i);
33903                 }
33904                 
33905                 return false;
33906             }
33907             
33908         }, this);
33909         
33910         this.validate();
33911     },
33912     
33913     clearInvalid : function(){
33914         
33915         if(!this.el || this.preventMark){
33916             return;
33917         }
33918         
33919         this.el.removeClass([this.invalidClass]);
33920         
33921         this.fireEvent('valid', this);
33922     }
33923     
33924 });
33925
33926 Roo.apply(Roo.bootstrap.RadioSet, {
33927     
33928     groups: {},
33929     
33930     register : function(set)
33931     {
33932         this.groups[set.name] = set;
33933     },
33934     
33935     get: function(name) 
33936     {
33937         if (typeof(this.groups[name]) == 'undefined') {
33938             return false;
33939         }
33940         
33941         return this.groups[name] ;
33942     }
33943     
33944 });
33945 /*
33946  * Based on:
33947  * Ext JS Library 1.1.1
33948  * Copyright(c) 2006-2007, Ext JS, LLC.
33949  *
33950  * Originally Released Under LGPL - original licence link has changed is not relivant.
33951  *
33952  * Fork - LGPL
33953  * <script type="text/javascript">
33954  */
33955
33956
33957 /**
33958  * @class Roo.bootstrap.SplitBar
33959  * @extends Roo.util.Observable
33960  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33961  * <br><br>
33962  * Usage:
33963  * <pre><code>
33964 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33965                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33966 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33967 split.minSize = 100;
33968 split.maxSize = 600;
33969 split.animate = true;
33970 split.on('moved', splitterMoved);
33971 </code></pre>
33972  * @constructor
33973  * Create a new SplitBar
33974  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33975  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33976  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33977  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33978                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33979                         position of the SplitBar).
33980  */
33981 Roo.bootstrap.SplitBar = function(cfg){
33982     
33983     /** @private */
33984     
33985     //{
33986     //  dragElement : elm
33987     //  resizingElement: el,
33988         // optional..
33989     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33990     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33991         // existingProxy ???
33992     //}
33993     
33994     this.el = Roo.get(cfg.dragElement, true);
33995     this.el.dom.unselectable = "on";
33996     /** @private */
33997     this.resizingEl = Roo.get(cfg.resizingElement, true);
33998
33999     /**
34000      * @private
34001      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34002      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34003      * @type Number
34004      */
34005     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34006     
34007     /**
34008      * The minimum size of the resizing element. (Defaults to 0)
34009      * @type Number
34010      */
34011     this.minSize = 0;
34012     
34013     /**
34014      * The maximum size of the resizing element. (Defaults to 2000)
34015      * @type Number
34016      */
34017     this.maxSize = 2000;
34018     
34019     /**
34020      * Whether to animate the transition to the new size
34021      * @type Boolean
34022      */
34023     this.animate = false;
34024     
34025     /**
34026      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34027      * @type Boolean
34028      */
34029     this.useShim = false;
34030     
34031     /** @private */
34032     this.shim = null;
34033     
34034     if(!cfg.existingProxy){
34035         /** @private */
34036         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34037     }else{
34038         this.proxy = Roo.get(cfg.existingProxy).dom;
34039     }
34040     /** @private */
34041     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34042     
34043     /** @private */
34044     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34045     
34046     /** @private */
34047     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34048     
34049     /** @private */
34050     this.dragSpecs = {};
34051     
34052     /**
34053      * @private The adapter to use to positon and resize elements
34054      */
34055     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34056     this.adapter.init(this);
34057     
34058     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34059         /** @private */
34060         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34061         this.el.addClass("roo-splitbar-h");
34062     }else{
34063         /** @private */
34064         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34065         this.el.addClass("roo-splitbar-v");
34066     }
34067     
34068     this.addEvents({
34069         /**
34070          * @event resize
34071          * Fires when the splitter is moved (alias for {@link #event-moved})
34072          * @param {Roo.bootstrap.SplitBar} this
34073          * @param {Number} newSize the new width or height
34074          */
34075         "resize" : true,
34076         /**
34077          * @event moved
34078          * Fires when the splitter is moved
34079          * @param {Roo.bootstrap.SplitBar} this
34080          * @param {Number} newSize the new width or height
34081          */
34082         "moved" : true,
34083         /**
34084          * @event beforeresize
34085          * Fires before the splitter is dragged
34086          * @param {Roo.bootstrap.SplitBar} this
34087          */
34088         "beforeresize" : true,
34089
34090         "beforeapply" : true
34091     });
34092
34093     Roo.util.Observable.call(this);
34094 };
34095
34096 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34097     onStartProxyDrag : function(x, y){
34098         this.fireEvent("beforeresize", this);
34099         if(!this.overlay){
34100             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34101             o.unselectable();
34102             o.enableDisplayMode("block");
34103             // all splitbars share the same overlay
34104             Roo.bootstrap.SplitBar.prototype.overlay = o;
34105         }
34106         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34107         this.overlay.show();
34108         Roo.get(this.proxy).setDisplayed("block");
34109         var size = this.adapter.getElementSize(this);
34110         this.activeMinSize = this.getMinimumSize();;
34111         this.activeMaxSize = this.getMaximumSize();;
34112         var c1 = size - this.activeMinSize;
34113         var c2 = Math.max(this.activeMaxSize - size, 0);
34114         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34115             this.dd.resetConstraints();
34116             this.dd.setXConstraint(
34117                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34118                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34119             );
34120             this.dd.setYConstraint(0, 0);
34121         }else{
34122             this.dd.resetConstraints();
34123             this.dd.setXConstraint(0, 0);
34124             this.dd.setYConstraint(
34125                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34126                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34127             );
34128          }
34129         this.dragSpecs.startSize = size;
34130         this.dragSpecs.startPoint = [x, y];
34131         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34132     },
34133     
34134     /** 
34135      * @private Called after the drag operation by the DDProxy
34136      */
34137     onEndProxyDrag : function(e){
34138         Roo.get(this.proxy).setDisplayed(false);
34139         var endPoint = Roo.lib.Event.getXY(e);
34140         if(this.overlay){
34141             this.overlay.hide();
34142         }
34143         var newSize;
34144         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34145             newSize = this.dragSpecs.startSize + 
34146                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34147                     endPoint[0] - this.dragSpecs.startPoint[0] :
34148                     this.dragSpecs.startPoint[0] - endPoint[0]
34149                 );
34150         }else{
34151             newSize = this.dragSpecs.startSize + 
34152                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34153                     endPoint[1] - this.dragSpecs.startPoint[1] :
34154                     this.dragSpecs.startPoint[1] - endPoint[1]
34155                 );
34156         }
34157         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34158         if(newSize != this.dragSpecs.startSize){
34159             if(this.fireEvent('beforeapply', this, newSize) !== false){
34160                 this.adapter.setElementSize(this, newSize);
34161                 this.fireEvent("moved", this, newSize);
34162                 this.fireEvent("resize", this, newSize);
34163             }
34164         }
34165     },
34166     
34167     /**
34168      * Get the adapter this SplitBar uses
34169      * @return The adapter object
34170      */
34171     getAdapter : function(){
34172         return this.adapter;
34173     },
34174     
34175     /**
34176      * Set the adapter this SplitBar uses
34177      * @param {Object} adapter A SplitBar adapter object
34178      */
34179     setAdapter : function(adapter){
34180         this.adapter = adapter;
34181         this.adapter.init(this);
34182     },
34183     
34184     /**
34185      * Gets the minimum size for the resizing element
34186      * @return {Number} The minimum size
34187      */
34188     getMinimumSize : function(){
34189         return this.minSize;
34190     },
34191     
34192     /**
34193      * Sets the minimum size for the resizing element
34194      * @param {Number} minSize The minimum size
34195      */
34196     setMinimumSize : function(minSize){
34197         this.minSize = minSize;
34198     },
34199     
34200     /**
34201      * Gets the maximum size for the resizing element
34202      * @return {Number} The maximum size
34203      */
34204     getMaximumSize : function(){
34205         return this.maxSize;
34206     },
34207     
34208     /**
34209      * Sets the maximum size for the resizing element
34210      * @param {Number} maxSize The maximum size
34211      */
34212     setMaximumSize : function(maxSize){
34213         this.maxSize = maxSize;
34214     },
34215     
34216     /**
34217      * Sets the initialize size for the resizing element
34218      * @param {Number} size The initial size
34219      */
34220     setCurrentSize : function(size){
34221         var oldAnimate = this.animate;
34222         this.animate = false;
34223         this.adapter.setElementSize(this, size);
34224         this.animate = oldAnimate;
34225     },
34226     
34227     /**
34228      * Destroy this splitbar. 
34229      * @param {Boolean} removeEl True to remove the element
34230      */
34231     destroy : function(removeEl){
34232         if(this.shim){
34233             this.shim.remove();
34234         }
34235         this.dd.unreg();
34236         this.proxy.parentNode.removeChild(this.proxy);
34237         if(removeEl){
34238             this.el.remove();
34239         }
34240     }
34241 });
34242
34243 /**
34244  * @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.
34245  */
34246 Roo.bootstrap.SplitBar.createProxy = function(dir){
34247     var proxy = new Roo.Element(document.createElement("div"));
34248     proxy.unselectable();
34249     var cls = 'roo-splitbar-proxy';
34250     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34251     document.body.appendChild(proxy.dom);
34252     return proxy.dom;
34253 };
34254
34255 /** 
34256  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34257  * Default Adapter. It assumes the splitter and resizing element are not positioned
34258  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34259  */
34260 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34261 };
34262
34263 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34264     // do nothing for now
34265     init : function(s){
34266     
34267     },
34268     /**
34269      * Called before drag operations to get the current size of the resizing element. 
34270      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34271      */
34272      getElementSize : function(s){
34273         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34274             return s.resizingEl.getWidth();
34275         }else{
34276             return s.resizingEl.getHeight();
34277         }
34278     },
34279     
34280     /**
34281      * Called after drag operations to set the size of the resizing element.
34282      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34283      * @param {Number} newSize The new size to set
34284      * @param {Function} onComplete A function to be invoked when resizing is complete
34285      */
34286     setElementSize : function(s, newSize, onComplete){
34287         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34288             if(!s.animate){
34289                 s.resizingEl.setWidth(newSize);
34290                 if(onComplete){
34291                     onComplete(s, newSize);
34292                 }
34293             }else{
34294                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34295             }
34296         }else{
34297             
34298             if(!s.animate){
34299                 s.resizingEl.setHeight(newSize);
34300                 if(onComplete){
34301                     onComplete(s, newSize);
34302                 }
34303             }else{
34304                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34305             }
34306         }
34307     }
34308 };
34309
34310 /** 
34311  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34312  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34313  * Adapter that  moves the splitter element to align with the resized sizing element. 
34314  * Used with an absolute positioned SplitBar.
34315  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34316  * document.body, make sure you assign an id to the body element.
34317  */
34318 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34319     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34320     this.container = Roo.get(container);
34321 };
34322
34323 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34324     init : function(s){
34325         this.basic.init(s);
34326     },
34327     
34328     getElementSize : function(s){
34329         return this.basic.getElementSize(s);
34330     },
34331     
34332     setElementSize : function(s, newSize, onComplete){
34333         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34334     },
34335     
34336     moveSplitter : function(s){
34337         var yes = Roo.bootstrap.SplitBar;
34338         switch(s.placement){
34339             case yes.LEFT:
34340                 s.el.setX(s.resizingEl.getRight());
34341                 break;
34342             case yes.RIGHT:
34343                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34344                 break;
34345             case yes.TOP:
34346                 s.el.setY(s.resizingEl.getBottom());
34347                 break;
34348             case yes.BOTTOM:
34349                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34350                 break;
34351         }
34352     }
34353 };
34354
34355 /**
34356  * Orientation constant - Create a vertical SplitBar
34357  * @static
34358  * @type Number
34359  */
34360 Roo.bootstrap.SplitBar.VERTICAL = 1;
34361
34362 /**
34363  * Orientation constant - Create a horizontal SplitBar
34364  * @static
34365  * @type Number
34366  */
34367 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34368
34369 /**
34370  * Placement constant - The resizing element is to the left of the splitter element
34371  * @static
34372  * @type Number
34373  */
34374 Roo.bootstrap.SplitBar.LEFT = 1;
34375
34376 /**
34377  * Placement constant - The resizing element is to the right of the splitter element
34378  * @static
34379  * @type Number
34380  */
34381 Roo.bootstrap.SplitBar.RIGHT = 2;
34382
34383 /**
34384  * Placement constant - The resizing element is positioned above the splitter element
34385  * @static
34386  * @type Number
34387  */
34388 Roo.bootstrap.SplitBar.TOP = 3;
34389
34390 /**
34391  * Placement constant - The resizing element is positioned under splitter element
34392  * @static
34393  * @type Number
34394  */
34395 Roo.bootstrap.SplitBar.BOTTOM = 4;
34396 Roo.namespace("Roo.bootstrap.layout");/*
34397  * Based on:
34398  * Ext JS Library 1.1.1
34399  * Copyright(c) 2006-2007, Ext JS, LLC.
34400  *
34401  * Originally Released Under LGPL - original licence link has changed is not relivant.
34402  *
34403  * Fork - LGPL
34404  * <script type="text/javascript">
34405  */
34406
34407 /**
34408  * @class Roo.bootstrap.layout.Manager
34409  * @extends Roo.bootstrap.Component
34410  * Base class for layout managers.
34411  */
34412 Roo.bootstrap.layout.Manager = function(config)
34413 {
34414     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34415
34416
34417
34418
34419
34420     /** false to disable window resize monitoring @type Boolean */
34421     this.monitorWindowResize = true;
34422     this.regions = {};
34423     this.addEvents({
34424         /**
34425          * @event layout
34426          * Fires when a layout is performed.
34427          * @param {Roo.LayoutManager} this
34428          */
34429         "layout" : true,
34430         /**
34431          * @event regionresized
34432          * Fires when the user resizes a region.
34433          * @param {Roo.LayoutRegion} region The resized region
34434          * @param {Number} newSize The new size (width for east/west, height for north/south)
34435          */
34436         "regionresized" : true,
34437         /**
34438          * @event regioncollapsed
34439          * Fires when a region is collapsed.
34440          * @param {Roo.LayoutRegion} region The collapsed region
34441          */
34442         "regioncollapsed" : true,
34443         /**
34444          * @event regionexpanded
34445          * Fires when a region is expanded.
34446          * @param {Roo.LayoutRegion} region The expanded region
34447          */
34448         "regionexpanded" : true
34449     });
34450     this.updating = false;
34451
34452     if (config.el) {
34453         this.el = Roo.get(config.el);
34454         this.initEvents();
34455     }
34456
34457 };
34458
34459 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34460
34461
34462     regions : null,
34463
34464     monitorWindowResize : true,
34465
34466
34467     updating : false,
34468
34469
34470     onRender : function(ct, position)
34471     {
34472         if(!this.el){
34473             this.el = Roo.get(ct);
34474             this.initEvents();
34475         }
34476         //this.fireEvent('render',this);
34477     },
34478
34479
34480     initEvents: function()
34481     {
34482
34483
34484         // ie scrollbar fix
34485         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34486             document.body.scroll = "no";
34487         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34488             this.el.position('relative');
34489         }
34490         this.id = this.el.id;
34491         this.el.addClass("roo-layout-container");
34492         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34493         if(this.el.dom != document.body ) {
34494             this.el.on('resize', this.layout,this);
34495             this.el.on('show', this.layout,this);
34496         }
34497
34498     },
34499
34500     /**
34501      * Returns true if this layout is currently being updated
34502      * @return {Boolean}
34503      */
34504     isUpdating : function(){
34505         return this.updating;
34506     },
34507
34508     /**
34509      * Suspend the LayoutManager from doing auto-layouts while
34510      * making multiple add or remove calls
34511      */
34512     beginUpdate : function(){
34513         this.updating = true;
34514     },
34515
34516     /**
34517      * Restore auto-layouts and optionally disable the manager from performing a layout
34518      * @param {Boolean} noLayout true to disable a layout update
34519      */
34520     endUpdate : function(noLayout){
34521         this.updating = false;
34522         if(!noLayout){
34523             this.layout();
34524         }
34525     },
34526
34527     layout: function(){
34528         // abstract...
34529     },
34530
34531     onRegionResized : function(region, newSize){
34532         this.fireEvent("regionresized", region, newSize);
34533         this.layout();
34534     },
34535
34536     onRegionCollapsed : function(region){
34537         this.fireEvent("regioncollapsed", region);
34538     },
34539
34540     onRegionExpanded : function(region){
34541         this.fireEvent("regionexpanded", region);
34542     },
34543
34544     /**
34545      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34546      * performs box-model adjustments.
34547      * @return {Object} The size as an object {width: (the width), height: (the height)}
34548      */
34549     getViewSize : function()
34550     {
34551         var size;
34552         if(this.el.dom != document.body){
34553             size = this.el.getSize();
34554         }else{
34555             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34556         }
34557         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34558         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34559         return size;
34560     },
34561
34562     /**
34563      * Returns the Element this layout is bound to.
34564      * @return {Roo.Element}
34565      */
34566     getEl : function(){
34567         return this.el;
34568     },
34569
34570     /**
34571      * Returns the specified region.
34572      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34573      * @return {Roo.LayoutRegion}
34574      */
34575     getRegion : function(target){
34576         return this.regions[target.toLowerCase()];
34577     },
34578
34579     onWindowResize : function(){
34580         if(this.monitorWindowResize){
34581             this.layout();
34582         }
34583     }
34584 });
34585 /*
34586  * Based on:
34587  * Ext JS Library 1.1.1
34588  * Copyright(c) 2006-2007, Ext JS, LLC.
34589  *
34590  * Originally Released Under LGPL - original licence link has changed is not relivant.
34591  *
34592  * Fork - LGPL
34593  * <script type="text/javascript">
34594  */
34595 /**
34596  * @class Roo.bootstrap.layout.Border
34597  * @extends Roo.bootstrap.layout.Manager
34598  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34599  * please see: examples/bootstrap/nested.html<br><br>
34600  
34601 <b>The container the layout is rendered into can be either the body element or any other element.
34602 If it is not the body element, the container needs to either be an absolute positioned element,
34603 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34604 the container size if it is not the body element.</b>
34605
34606 * @constructor
34607 * Create a new Border
34608 * @param {Object} config Configuration options
34609  */
34610 Roo.bootstrap.layout.Border = function(config){
34611     config = config || {};
34612     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34613     
34614     
34615     
34616     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34617         if(config[region]){
34618             config[region].region = region;
34619             this.addRegion(config[region]);
34620         }
34621     },this);
34622     
34623 };
34624
34625 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34626
34627 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34628     /**
34629      * Creates and adds a new region if it doesn't already exist.
34630      * @param {String} target The target region key (north, south, east, west or center).
34631      * @param {Object} config The regions config object
34632      * @return {BorderLayoutRegion} The new region
34633      */
34634     addRegion : function(config)
34635     {
34636         if(!this.regions[config.region]){
34637             var r = this.factory(config);
34638             this.bindRegion(r);
34639         }
34640         return this.regions[config.region];
34641     },
34642
34643     // private (kinda)
34644     bindRegion : function(r){
34645         this.regions[r.config.region] = r;
34646         
34647         r.on("visibilitychange",    this.layout, this);
34648         r.on("paneladded",          this.layout, this);
34649         r.on("panelremoved",        this.layout, this);
34650         r.on("invalidated",         this.layout, this);
34651         r.on("resized",             this.onRegionResized, this);
34652         r.on("collapsed",           this.onRegionCollapsed, this);
34653         r.on("expanded",            this.onRegionExpanded, this);
34654     },
34655
34656     /**
34657      * Performs a layout update.
34658      */
34659     layout : function()
34660     {
34661         if(this.updating) {
34662             return;
34663         }
34664         
34665         // render all the rebions if they have not been done alreayd?
34666         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34667             if(this.regions[region] && !this.regions[region].bodyEl){
34668                 this.regions[region].onRender(this.el)
34669             }
34670         },this);
34671         
34672         var size = this.getViewSize();
34673         var w = size.width;
34674         var h = size.height;
34675         var centerW = w;
34676         var centerH = h;
34677         var centerY = 0;
34678         var centerX = 0;
34679         //var x = 0, y = 0;
34680
34681         var rs = this.regions;
34682         var north = rs["north"];
34683         var south = rs["south"]; 
34684         var west = rs["west"];
34685         var east = rs["east"];
34686         var center = rs["center"];
34687         //if(this.hideOnLayout){ // not supported anymore
34688             //c.el.setStyle("display", "none");
34689         //}
34690         if(north && north.isVisible()){
34691             var b = north.getBox();
34692             var m = north.getMargins();
34693             b.width = w - (m.left+m.right);
34694             b.x = m.left;
34695             b.y = m.top;
34696             centerY = b.height + b.y + m.bottom;
34697             centerH -= centerY;
34698             north.updateBox(this.safeBox(b));
34699         }
34700         if(south && south.isVisible()){
34701             var b = south.getBox();
34702             var m = south.getMargins();
34703             b.width = w - (m.left+m.right);
34704             b.x = m.left;
34705             var totalHeight = (b.height + m.top + m.bottom);
34706             b.y = h - totalHeight + m.top;
34707             centerH -= totalHeight;
34708             south.updateBox(this.safeBox(b));
34709         }
34710         if(west && west.isVisible()){
34711             var b = west.getBox();
34712             var m = west.getMargins();
34713             b.height = centerH - (m.top+m.bottom);
34714             b.x = m.left;
34715             b.y = centerY + m.top;
34716             var totalWidth = (b.width + m.left + m.right);
34717             centerX += totalWidth;
34718             centerW -= totalWidth;
34719             west.updateBox(this.safeBox(b));
34720         }
34721         if(east && east.isVisible()){
34722             var b = east.getBox();
34723             var m = east.getMargins();
34724             b.height = centerH - (m.top+m.bottom);
34725             var totalWidth = (b.width + m.left + m.right);
34726             b.x = w - totalWidth + m.left;
34727             b.y = centerY + m.top;
34728             centerW -= totalWidth;
34729             east.updateBox(this.safeBox(b));
34730         }
34731         if(center){
34732             var m = center.getMargins();
34733             var centerBox = {
34734                 x: centerX + m.left,
34735                 y: centerY + m.top,
34736                 width: centerW - (m.left+m.right),
34737                 height: centerH - (m.top+m.bottom)
34738             };
34739             //if(this.hideOnLayout){
34740                 //center.el.setStyle("display", "block");
34741             //}
34742             center.updateBox(this.safeBox(centerBox));
34743         }
34744         this.el.repaint();
34745         this.fireEvent("layout", this);
34746     },
34747
34748     // private
34749     safeBox : function(box){
34750         box.width = Math.max(0, box.width);
34751         box.height = Math.max(0, box.height);
34752         return box;
34753     },
34754
34755     /**
34756      * Adds a ContentPanel (or subclass) to this layout.
34757      * @param {String} target The target region key (north, south, east, west or center).
34758      * @param {Roo.ContentPanel} panel The panel to add
34759      * @return {Roo.ContentPanel} The added panel
34760      */
34761     add : function(target, panel){
34762          
34763         target = target.toLowerCase();
34764         return this.regions[target].add(panel);
34765     },
34766
34767     /**
34768      * Remove a ContentPanel (or subclass) to this layout.
34769      * @param {String} target The target region key (north, south, east, west or center).
34770      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34771      * @return {Roo.ContentPanel} The removed panel
34772      */
34773     remove : function(target, panel){
34774         target = target.toLowerCase();
34775         return this.regions[target].remove(panel);
34776     },
34777
34778     /**
34779      * Searches all regions for a panel with the specified id
34780      * @param {String} panelId
34781      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34782      */
34783     findPanel : function(panelId){
34784         var rs = this.regions;
34785         for(var target in rs){
34786             if(typeof rs[target] != "function"){
34787                 var p = rs[target].getPanel(panelId);
34788                 if(p){
34789                     return p;
34790                 }
34791             }
34792         }
34793         return null;
34794     },
34795
34796     /**
34797      * Searches all regions for a panel with the specified id and activates (shows) it.
34798      * @param {String/ContentPanel} panelId The panels id or the panel itself
34799      * @return {Roo.ContentPanel} The shown panel or null
34800      */
34801     showPanel : function(panelId) {
34802       var rs = this.regions;
34803       for(var target in rs){
34804          var r = rs[target];
34805          if(typeof r != "function"){
34806             if(r.hasPanel(panelId)){
34807                return r.showPanel(panelId);
34808             }
34809          }
34810       }
34811       return null;
34812    },
34813
34814    /**
34815      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34816      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34817      */
34818    /*
34819     restoreState : function(provider){
34820         if(!provider){
34821             provider = Roo.state.Manager;
34822         }
34823         var sm = new Roo.LayoutStateManager();
34824         sm.init(this, provider);
34825     },
34826 */
34827  
34828  
34829     /**
34830      * Adds a xtype elements to the layout.
34831      * <pre><code>
34832
34833 layout.addxtype({
34834        xtype : 'ContentPanel',
34835        region: 'west',
34836        items: [ .... ]
34837    }
34838 );
34839
34840 layout.addxtype({
34841         xtype : 'NestedLayoutPanel',
34842         region: 'west',
34843         layout: {
34844            center: { },
34845            west: { }   
34846         },
34847         items : [ ... list of content panels or nested layout panels.. ]
34848    }
34849 );
34850 </code></pre>
34851      * @param {Object} cfg Xtype definition of item to add.
34852      */
34853     addxtype : function(cfg)
34854     {
34855         // basically accepts a pannel...
34856         // can accept a layout region..!?!?
34857         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34858         
34859         
34860         // theory?  children can only be panels??
34861         
34862         //if (!cfg.xtype.match(/Panel$/)) {
34863         //    return false;
34864         //}
34865         var ret = false;
34866         
34867         if (typeof(cfg.region) == 'undefined') {
34868             Roo.log("Failed to add Panel, region was not set");
34869             Roo.log(cfg);
34870             return false;
34871         }
34872         var region = cfg.region;
34873         delete cfg.region;
34874         
34875           
34876         var xitems = [];
34877         if (cfg.items) {
34878             xitems = cfg.items;
34879             delete cfg.items;
34880         }
34881         var nb = false;
34882         
34883         switch(cfg.xtype) 
34884         {
34885             case 'Content':  // ContentPanel (el, cfg)
34886             case 'Scroll':  // ContentPanel (el, cfg)
34887             case 'View': 
34888                 cfg.autoCreate = true;
34889                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34890                 //} else {
34891                 //    var el = this.el.createChild();
34892                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34893                 //}
34894                 
34895                 this.add(region, ret);
34896                 break;
34897             
34898             /*
34899             case 'TreePanel': // our new panel!
34900                 cfg.el = this.el.createChild();
34901                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34902                 this.add(region, ret);
34903                 break;
34904             */
34905             
34906             case 'Nest': 
34907                 // create a new Layout (which is  a Border Layout...
34908                 
34909                 var clayout = cfg.layout;
34910                 clayout.el  = this.el.createChild();
34911                 clayout.items   = clayout.items  || [];
34912                 
34913                 delete cfg.layout;
34914                 
34915                 // replace this exitems with the clayout ones..
34916                 xitems = clayout.items;
34917                  
34918                 // force background off if it's in center...
34919                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34920                     cfg.background = false;
34921                 }
34922                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34923                 
34924                 
34925                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34926                 //console.log('adding nested layout panel '  + cfg.toSource());
34927                 this.add(region, ret);
34928                 nb = {}; /// find first...
34929                 break;
34930             
34931             case 'Grid':
34932                 
34933                 // needs grid and region
34934                 
34935                 //var el = this.getRegion(region).el.createChild();
34936                 /*
34937                  *var el = this.el.createChild();
34938                 // create the grid first...
34939                 cfg.grid.container = el;
34940                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34941                 */
34942                 
34943                 if (region == 'center' && this.active ) {
34944                     cfg.background = false;
34945                 }
34946                 
34947                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34948                 
34949                 this.add(region, ret);
34950                 /*
34951                 if (cfg.background) {
34952                     // render grid on panel activation (if panel background)
34953                     ret.on('activate', function(gp) {
34954                         if (!gp.grid.rendered) {
34955                     //        gp.grid.render(el);
34956                         }
34957                     });
34958                 } else {
34959                   //  cfg.grid.render(el);
34960                 }
34961                 */
34962                 break;
34963            
34964            
34965             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34966                 // it was the old xcomponent building that caused this before.
34967                 // espeically if border is the top element in the tree.
34968                 ret = this;
34969                 break; 
34970                 
34971                     
34972                 
34973                 
34974                 
34975             default:
34976                 /*
34977                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34978                     
34979                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34980                     this.add(region, ret);
34981                 } else {
34982                 */
34983                     Roo.log(cfg);
34984                     throw "Can not add '" + cfg.xtype + "' to Border";
34985                     return null;
34986              
34987                                 
34988              
34989         }
34990         this.beginUpdate();
34991         // add children..
34992         var region = '';
34993         var abn = {};
34994         Roo.each(xitems, function(i)  {
34995             region = nb && i.region ? i.region : false;
34996             
34997             var add = ret.addxtype(i);
34998            
34999             if (region) {
35000                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35001                 if (!i.background) {
35002                     abn[region] = nb[region] ;
35003                 }
35004             }
35005             
35006         });
35007         this.endUpdate();
35008
35009         // make the last non-background panel active..
35010         //if (nb) { Roo.log(abn); }
35011         if (nb) {
35012             
35013             for(var r in abn) {
35014                 region = this.getRegion(r);
35015                 if (region) {
35016                     // tried using nb[r], but it does not work..
35017                      
35018                     region.showPanel(abn[r]);
35019                    
35020                 }
35021             }
35022         }
35023         return ret;
35024         
35025     },
35026     
35027     
35028 // private
35029     factory : function(cfg)
35030     {
35031         
35032         var validRegions = Roo.bootstrap.layout.Border.regions;
35033
35034         var target = cfg.region;
35035         cfg.mgr = this;
35036         
35037         var r = Roo.bootstrap.layout;
35038         Roo.log(target);
35039         switch(target){
35040             case "north":
35041                 return new r.North(cfg);
35042             case "south":
35043                 return new r.South(cfg);
35044             case "east":
35045                 return new r.East(cfg);
35046             case "west":
35047                 return new r.West(cfg);
35048             case "center":
35049                 return new r.Center(cfg);
35050         }
35051         throw 'Layout region "'+target+'" not supported.';
35052     }
35053     
35054     
35055 });
35056  /*
35057  * Based on:
35058  * Ext JS Library 1.1.1
35059  * Copyright(c) 2006-2007, Ext JS, LLC.
35060  *
35061  * Originally Released Under LGPL - original licence link has changed is not relivant.
35062  *
35063  * Fork - LGPL
35064  * <script type="text/javascript">
35065  */
35066  
35067 /**
35068  * @class Roo.bootstrap.layout.Basic
35069  * @extends Roo.util.Observable
35070  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35071  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35072  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35073  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35074  * @cfg {string}   region  the region that it inhabits..
35075  * @cfg {bool}   skipConfig skip config?
35076  * 
35077
35078  */
35079 Roo.bootstrap.layout.Basic = function(config){
35080     
35081     this.mgr = config.mgr;
35082     
35083     this.position = config.region;
35084     
35085     var skipConfig = config.skipConfig;
35086     
35087     this.events = {
35088         /**
35089          * @scope Roo.BasicLayoutRegion
35090          */
35091         
35092         /**
35093          * @event beforeremove
35094          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35095          * @param {Roo.LayoutRegion} this
35096          * @param {Roo.ContentPanel} panel The panel
35097          * @param {Object} e The cancel event object
35098          */
35099         "beforeremove" : true,
35100         /**
35101          * @event invalidated
35102          * Fires when the layout for this region is changed.
35103          * @param {Roo.LayoutRegion} this
35104          */
35105         "invalidated" : true,
35106         /**
35107          * @event visibilitychange
35108          * Fires when this region is shown or hidden 
35109          * @param {Roo.LayoutRegion} this
35110          * @param {Boolean} visibility true or false
35111          */
35112         "visibilitychange" : true,
35113         /**
35114          * @event paneladded
35115          * Fires when a panel is added. 
35116          * @param {Roo.LayoutRegion} this
35117          * @param {Roo.ContentPanel} panel The panel
35118          */
35119         "paneladded" : true,
35120         /**
35121          * @event panelremoved
35122          * Fires when a panel is removed. 
35123          * @param {Roo.LayoutRegion} this
35124          * @param {Roo.ContentPanel} panel The panel
35125          */
35126         "panelremoved" : true,
35127         /**
35128          * @event beforecollapse
35129          * Fires when this region before collapse.
35130          * @param {Roo.LayoutRegion} this
35131          */
35132         "beforecollapse" : true,
35133         /**
35134          * @event collapsed
35135          * Fires when this region is collapsed.
35136          * @param {Roo.LayoutRegion} this
35137          */
35138         "collapsed" : true,
35139         /**
35140          * @event expanded
35141          * Fires when this region is expanded.
35142          * @param {Roo.LayoutRegion} this
35143          */
35144         "expanded" : true,
35145         /**
35146          * @event slideshow
35147          * Fires when this region is slid into view.
35148          * @param {Roo.LayoutRegion} this
35149          */
35150         "slideshow" : true,
35151         /**
35152          * @event slidehide
35153          * Fires when this region slides out of view. 
35154          * @param {Roo.LayoutRegion} this
35155          */
35156         "slidehide" : true,
35157         /**
35158          * @event panelactivated
35159          * Fires when a panel is activated. 
35160          * @param {Roo.LayoutRegion} this
35161          * @param {Roo.ContentPanel} panel The activated panel
35162          */
35163         "panelactivated" : true,
35164         /**
35165          * @event resized
35166          * Fires when the user resizes this region. 
35167          * @param {Roo.LayoutRegion} this
35168          * @param {Number} newSize The new size (width for east/west, height for north/south)
35169          */
35170         "resized" : true
35171     };
35172     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35173     this.panels = new Roo.util.MixedCollection();
35174     this.panels.getKey = this.getPanelId.createDelegate(this);
35175     this.box = null;
35176     this.activePanel = null;
35177     // ensure listeners are added...
35178     
35179     if (config.listeners || config.events) {
35180         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35181             listeners : config.listeners || {},
35182             events : config.events || {}
35183         });
35184     }
35185     
35186     if(skipConfig !== true){
35187         this.applyConfig(config);
35188     }
35189 };
35190
35191 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35192 {
35193     getPanelId : function(p){
35194         return p.getId();
35195     },
35196     
35197     applyConfig : function(config){
35198         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35199         this.config = config;
35200         
35201     },
35202     
35203     /**
35204      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35205      * the width, for horizontal (north, south) the height.
35206      * @param {Number} newSize The new width or height
35207      */
35208     resizeTo : function(newSize){
35209         var el = this.el ? this.el :
35210                  (this.activePanel ? this.activePanel.getEl() : null);
35211         if(el){
35212             switch(this.position){
35213                 case "east":
35214                 case "west":
35215                     el.setWidth(newSize);
35216                     this.fireEvent("resized", this, newSize);
35217                 break;
35218                 case "north":
35219                 case "south":
35220                     el.setHeight(newSize);
35221                     this.fireEvent("resized", this, newSize);
35222                 break;                
35223             }
35224         }
35225     },
35226     
35227     getBox : function(){
35228         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35229     },
35230     
35231     getMargins : function(){
35232         return this.margins;
35233     },
35234     
35235     updateBox : function(box){
35236         this.box = box;
35237         var el = this.activePanel.getEl();
35238         el.dom.style.left = box.x + "px";
35239         el.dom.style.top = box.y + "px";
35240         this.activePanel.setSize(box.width, box.height);
35241     },
35242     
35243     /**
35244      * Returns the container element for this region.
35245      * @return {Roo.Element}
35246      */
35247     getEl : function(){
35248         return this.activePanel;
35249     },
35250     
35251     /**
35252      * Returns true if this region is currently visible.
35253      * @return {Boolean}
35254      */
35255     isVisible : function(){
35256         return this.activePanel ? true : false;
35257     },
35258     
35259     setActivePanel : function(panel){
35260         panel = this.getPanel(panel);
35261         if(this.activePanel && this.activePanel != panel){
35262             this.activePanel.setActiveState(false);
35263             this.activePanel.getEl().setLeftTop(-10000,-10000);
35264         }
35265         this.activePanel = panel;
35266         panel.setActiveState(true);
35267         if(this.box){
35268             panel.setSize(this.box.width, this.box.height);
35269         }
35270         this.fireEvent("panelactivated", this, panel);
35271         this.fireEvent("invalidated");
35272     },
35273     
35274     /**
35275      * Show the specified panel.
35276      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35277      * @return {Roo.ContentPanel} The shown panel or null
35278      */
35279     showPanel : function(panel){
35280         panel = this.getPanel(panel);
35281         if(panel){
35282             this.setActivePanel(panel);
35283         }
35284         return panel;
35285     },
35286     
35287     /**
35288      * Get the active panel for this region.
35289      * @return {Roo.ContentPanel} The active panel or null
35290      */
35291     getActivePanel : function(){
35292         return this.activePanel;
35293     },
35294     
35295     /**
35296      * Add the passed ContentPanel(s)
35297      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35298      * @return {Roo.ContentPanel} The panel added (if only one was added)
35299      */
35300     add : function(panel){
35301         if(arguments.length > 1){
35302             for(var i = 0, len = arguments.length; i < len; i++) {
35303                 this.add(arguments[i]);
35304             }
35305             return null;
35306         }
35307         if(this.hasPanel(panel)){
35308             this.showPanel(panel);
35309             return panel;
35310         }
35311         var el = panel.getEl();
35312         if(el.dom.parentNode != this.mgr.el.dom){
35313             this.mgr.el.dom.appendChild(el.dom);
35314         }
35315         if(panel.setRegion){
35316             panel.setRegion(this);
35317         }
35318         this.panels.add(panel);
35319         el.setStyle("position", "absolute");
35320         if(!panel.background){
35321             this.setActivePanel(panel);
35322             if(this.config.initialSize && this.panels.getCount()==1){
35323                 this.resizeTo(this.config.initialSize);
35324             }
35325         }
35326         this.fireEvent("paneladded", this, panel);
35327         return panel;
35328     },
35329     
35330     /**
35331      * Returns true if the panel is in this region.
35332      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35333      * @return {Boolean}
35334      */
35335     hasPanel : function(panel){
35336         if(typeof panel == "object"){ // must be panel obj
35337             panel = panel.getId();
35338         }
35339         return this.getPanel(panel) ? true : false;
35340     },
35341     
35342     /**
35343      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35344      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35345      * @param {Boolean} preservePanel Overrides the config preservePanel option
35346      * @return {Roo.ContentPanel} The panel that was removed
35347      */
35348     remove : function(panel, preservePanel){
35349         panel = this.getPanel(panel);
35350         if(!panel){
35351             return null;
35352         }
35353         var e = {};
35354         this.fireEvent("beforeremove", this, panel, e);
35355         if(e.cancel === true){
35356             return null;
35357         }
35358         var panelId = panel.getId();
35359         this.panels.removeKey(panelId);
35360         return panel;
35361     },
35362     
35363     /**
35364      * Returns the panel specified or null if it's not in this region.
35365      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35366      * @return {Roo.ContentPanel}
35367      */
35368     getPanel : function(id){
35369         if(typeof id == "object"){ // must be panel obj
35370             return id;
35371         }
35372         return this.panels.get(id);
35373     },
35374     
35375     /**
35376      * Returns this regions position (north/south/east/west/center).
35377      * @return {String} 
35378      */
35379     getPosition: function(){
35380         return this.position;    
35381     }
35382 });/*
35383  * Based on:
35384  * Ext JS Library 1.1.1
35385  * Copyright(c) 2006-2007, Ext JS, LLC.
35386  *
35387  * Originally Released Under LGPL - original licence link has changed is not relivant.
35388  *
35389  * Fork - LGPL
35390  * <script type="text/javascript">
35391  */
35392  
35393 /**
35394  * @class Roo.bootstrap.layout.Region
35395  * @extends Roo.bootstrap.layout.Basic
35396  * This class represents a region in a layout manager.
35397  
35398  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35399  * @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})
35400  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35401  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35402  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35403  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35404  * @cfg {String}    title           The title for the region (overrides panel titles)
35405  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35406  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35407  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35408  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35409  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35410  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35411  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35412  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35413  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35414  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35415
35416  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35417  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35418  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35419  * @cfg {Number}    width           For East/West panels
35420  * @cfg {Number}    height          For North/South panels
35421  * @cfg {Boolean}   split           To show the splitter
35422  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35423  * 
35424  * @cfg {string}   cls             Extra CSS classes to add to region
35425  * 
35426  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35427  * @cfg {string}   region  the region that it inhabits..
35428  *
35429
35430  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35431  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35432
35433  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35434  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35435  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35436  */
35437 Roo.bootstrap.layout.Region = function(config)
35438 {
35439     this.applyConfig(config);
35440
35441     var mgr = config.mgr;
35442     var pos = config.region;
35443     config.skipConfig = true;
35444     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35445     
35446     if (mgr.el) {
35447         this.onRender(mgr.el);   
35448     }
35449      
35450     this.visible = true;
35451     this.collapsed = false;
35452     this.unrendered_panels = [];
35453 };
35454
35455 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35456
35457     position: '', // set by wrapper (eg. north/south etc..)
35458     unrendered_panels : null,  // unrendered panels.
35459     createBody : function(){
35460         /** This region's body element 
35461         * @type Roo.Element */
35462         this.bodyEl = this.el.createChild({
35463                 tag: "div",
35464                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35465         });
35466     },
35467
35468     onRender: function(ctr, pos)
35469     {
35470         var dh = Roo.DomHelper;
35471         /** This region's container element 
35472         * @type Roo.Element */
35473         this.el = dh.append(ctr.dom, {
35474                 tag: "div",
35475                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35476             }, true);
35477         /** This region's title element 
35478         * @type Roo.Element */
35479     
35480         this.titleEl = dh.append(this.el.dom,
35481             {
35482                     tag: "div",
35483                     unselectable: "on",
35484                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35485                     children:[
35486                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35487                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35488                     ]}, true);
35489         
35490         this.titleEl.enableDisplayMode();
35491         /** This region's title text element 
35492         * @type HTMLElement */
35493         this.titleTextEl = this.titleEl.dom.firstChild;
35494         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35495         /*
35496         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35497         this.closeBtn.enableDisplayMode();
35498         this.closeBtn.on("click", this.closeClicked, this);
35499         this.closeBtn.hide();
35500     */
35501         this.createBody(this.config);
35502         if(this.config.hideWhenEmpty){
35503             this.hide();
35504             this.on("paneladded", this.validateVisibility, this);
35505             this.on("panelremoved", this.validateVisibility, this);
35506         }
35507         if(this.autoScroll){
35508             this.bodyEl.setStyle("overflow", "auto");
35509         }else{
35510             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35511         }
35512         //if(c.titlebar !== false){
35513             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35514                 this.titleEl.hide();
35515             }else{
35516                 this.titleEl.show();
35517                 if(this.config.title){
35518                     this.titleTextEl.innerHTML = this.config.title;
35519                 }
35520             }
35521         //}
35522         if(this.config.collapsed){
35523             this.collapse(true);
35524         }
35525         if(this.config.hidden){
35526             this.hide();
35527         }
35528         
35529         if (this.unrendered_panels && this.unrendered_panels.length) {
35530             for (var i =0;i< this.unrendered_panels.length; i++) {
35531                 this.add(this.unrendered_panels[i]);
35532             }
35533             this.unrendered_panels = null;
35534             
35535         }
35536         
35537     },
35538     
35539     applyConfig : function(c)
35540     {
35541         /*
35542          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35543             var dh = Roo.DomHelper;
35544             if(c.titlebar !== false){
35545                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35546                 this.collapseBtn.on("click", this.collapse, this);
35547                 this.collapseBtn.enableDisplayMode();
35548                 /*
35549                 if(c.showPin === true || this.showPin){
35550                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35551                     this.stickBtn.enableDisplayMode();
35552                     this.stickBtn.on("click", this.expand, this);
35553                     this.stickBtn.hide();
35554                 }
35555                 
35556             }
35557             */
35558             /** This region's collapsed element
35559             * @type Roo.Element */
35560             /*
35561              *
35562             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35563                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35564             ]}, true);
35565             
35566             if(c.floatable !== false){
35567                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35568                this.collapsedEl.on("click", this.collapseClick, this);
35569             }
35570
35571             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35572                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35573                    id: "message", unselectable: "on", style:{"float":"left"}});
35574                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35575              }
35576             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35577             this.expandBtn.on("click", this.expand, this);
35578             
35579         }
35580         
35581         if(this.collapseBtn){
35582             this.collapseBtn.setVisible(c.collapsible == true);
35583         }
35584         
35585         this.cmargins = c.cmargins || this.cmargins ||
35586                          (this.position == "west" || this.position == "east" ?
35587                              {top: 0, left: 2, right:2, bottom: 0} :
35588                              {top: 2, left: 0, right:0, bottom: 2});
35589         */
35590         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35591         
35592         
35593         this.bottomTabs = c.tabPosition != "top";
35594         
35595         this.autoScroll = c.autoScroll || false;
35596         
35597         
35598        
35599         
35600         this.duration = c.duration || .30;
35601         this.slideDuration = c.slideDuration || .45;
35602         this.config = c;
35603        
35604     },
35605     /**
35606      * Returns true if this region is currently visible.
35607      * @return {Boolean}
35608      */
35609     isVisible : function(){
35610         return this.visible;
35611     },
35612
35613     /**
35614      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35615      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35616      */
35617     //setCollapsedTitle : function(title){
35618     //    title = title || "&#160;";
35619      //   if(this.collapsedTitleTextEl){
35620       //      this.collapsedTitleTextEl.innerHTML = title;
35621        // }
35622     //},
35623
35624     getBox : function(){
35625         var b;
35626       //  if(!this.collapsed){
35627             b = this.el.getBox(false, true);
35628        // }else{
35629           //  b = this.collapsedEl.getBox(false, true);
35630         //}
35631         return b;
35632     },
35633
35634     getMargins : function(){
35635         return this.margins;
35636         //return this.collapsed ? this.cmargins : this.margins;
35637     },
35638 /*
35639     highlight : function(){
35640         this.el.addClass("x-layout-panel-dragover");
35641     },
35642
35643     unhighlight : function(){
35644         this.el.removeClass("x-layout-panel-dragover");
35645     },
35646 */
35647     updateBox : function(box)
35648     {
35649         if (!this.bodyEl) {
35650             return; // not rendered yet..
35651         }
35652         
35653         this.box = box;
35654         if(!this.collapsed){
35655             this.el.dom.style.left = box.x + "px";
35656             this.el.dom.style.top = box.y + "px";
35657             this.updateBody(box.width, box.height);
35658         }else{
35659             this.collapsedEl.dom.style.left = box.x + "px";
35660             this.collapsedEl.dom.style.top = box.y + "px";
35661             this.collapsedEl.setSize(box.width, box.height);
35662         }
35663         if(this.tabs){
35664             this.tabs.autoSizeTabs();
35665         }
35666     },
35667
35668     updateBody : function(w, h)
35669     {
35670         if(w !== null){
35671             this.el.setWidth(w);
35672             w -= this.el.getBorderWidth("rl");
35673             if(this.config.adjustments){
35674                 w += this.config.adjustments[0];
35675             }
35676         }
35677         if(h !== null && h > 0){
35678             this.el.setHeight(h);
35679             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35680             h -= this.el.getBorderWidth("tb");
35681             if(this.config.adjustments){
35682                 h += this.config.adjustments[1];
35683             }
35684             this.bodyEl.setHeight(h);
35685             if(this.tabs){
35686                 h = this.tabs.syncHeight(h);
35687             }
35688         }
35689         if(this.panelSize){
35690             w = w !== null ? w : this.panelSize.width;
35691             h = h !== null ? h : this.panelSize.height;
35692         }
35693         if(this.activePanel){
35694             var el = this.activePanel.getEl();
35695             w = w !== null ? w : el.getWidth();
35696             h = h !== null ? h : el.getHeight();
35697             this.panelSize = {width: w, height: h};
35698             this.activePanel.setSize(w, h);
35699         }
35700         if(Roo.isIE && this.tabs){
35701             this.tabs.el.repaint();
35702         }
35703     },
35704
35705     /**
35706      * Returns the container element for this region.
35707      * @return {Roo.Element}
35708      */
35709     getEl : function(){
35710         return this.el;
35711     },
35712
35713     /**
35714      * Hides this region.
35715      */
35716     hide : function(){
35717         //if(!this.collapsed){
35718             this.el.dom.style.left = "-2000px";
35719             this.el.hide();
35720         //}else{
35721          //   this.collapsedEl.dom.style.left = "-2000px";
35722          //   this.collapsedEl.hide();
35723        // }
35724         this.visible = false;
35725         this.fireEvent("visibilitychange", this, false);
35726     },
35727
35728     /**
35729      * Shows this region if it was previously hidden.
35730      */
35731     show : function(){
35732         //if(!this.collapsed){
35733             this.el.show();
35734         //}else{
35735         //    this.collapsedEl.show();
35736        // }
35737         this.visible = true;
35738         this.fireEvent("visibilitychange", this, true);
35739     },
35740 /*
35741     closeClicked : function(){
35742         if(this.activePanel){
35743             this.remove(this.activePanel);
35744         }
35745     },
35746
35747     collapseClick : function(e){
35748         if(this.isSlid){
35749            e.stopPropagation();
35750            this.slideIn();
35751         }else{
35752            e.stopPropagation();
35753            this.slideOut();
35754         }
35755     },
35756 */
35757     /**
35758      * Collapses this region.
35759      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35760      */
35761     /*
35762     collapse : function(skipAnim, skipCheck = false){
35763         if(this.collapsed) {
35764             return;
35765         }
35766         
35767         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35768             
35769             this.collapsed = true;
35770             if(this.split){
35771                 this.split.el.hide();
35772             }
35773             if(this.config.animate && skipAnim !== true){
35774                 this.fireEvent("invalidated", this);
35775                 this.animateCollapse();
35776             }else{
35777                 this.el.setLocation(-20000,-20000);
35778                 this.el.hide();
35779                 this.collapsedEl.show();
35780                 this.fireEvent("collapsed", this);
35781                 this.fireEvent("invalidated", this);
35782             }
35783         }
35784         
35785     },
35786 */
35787     animateCollapse : function(){
35788         // overridden
35789     },
35790
35791     /**
35792      * Expands this region if it was previously collapsed.
35793      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35794      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35795      */
35796     /*
35797     expand : function(e, skipAnim){
35798         if(e) {
35799             e.stopPropagation();
35800         }
35801         if(!this.collapsed || this.el.hasActiveFx()) {
35802             return;
35803         }
35804         if(this.isSlid){
35805             this.afterSlideIn();
35806             skipAnim = true;
35807         }
35808         this.collapsed = false;
35809         if(this.config.animate && skipAnim !== true){
35810             this.animateExpand();
35811         }else{
35812             this.el.show();
35813             if(this.split){
35814                 this.split.el.show();
35815             }
35816             this.collapsedEl.setLocation(-2000,-2000);
35817             this.collapsedEl.hide();
35818             this.fireEvent("invalidated", this);
35819             this.fireEvent("expanded", this);
35820         }
35821     },
35822 */
35823     animateExpand : function(){
35824         // overridden
35825     },
35826
35827     initTabs : function()
35828     {
35829         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35830         
35831         var ts = new Roo.bootstrap.panel.Tabs({
35832                 el: this.bodyEl.dom,
35833                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35834                 disableTooltips: this.config.disableTabTips,
35835                 toolbar : this.config.toolbar
35836             });
35837         
35838         if(this.config.hideTabs){
35839             ts.stripWrap.setDisplayed(false);
35840         }
35841         this.tabs = ts;
35842         ts.resizeTabs = this.config.resizeTabs === true;
35843         ts.minTabWidth = this.config.minTabWidth || 40;
35844         ts.maxTabWidth = this.config.maxTabWidth || 250;
35845         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35846         ts.monitorResize = false;
35847         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35848         ts.bodyEl.addClass('roo-layout-tabs-body');
35849         this.panels.each(this.initPanelAsTab, this);
35850     },
35851
35852     initPanelAsTab : function(panel){
35853         var ti = this.tabs.addTab(
35854             panel.getEl().id,
35855             panel.getTitle(),
35856             null,
35857             this.config.closeOnTab && panel.isClosable(),
35858             panel.tpl
35859         );
35860         if(panel.tabTip !== undefined){
35861             ti.setTooltip(panel.tabTip);
35862         }
35863         ti.on("activate", function(){
35864               this.setActivePanel(panel);
35865         }, this);
35866         
35867         if(this.config.closeOnTab){
35868             ti.on("beforeclose", function(t, e){
35869                 e.cancel = true;
35870                 this.remove(panel);
35871             }, this);
35872         }
35873         
35874         panel.tabItem = ti;
35875         
35876         return ti;
35877     },
35878
35879     updatePanelTitle : function(panel, title)
35880     {
35881         if(this.activePanel == panel){
35882             this.updateTitle(title);
35883         }
35884         if(this.tabs){
35885             var ti = this.tabs.getTab(panel.getEl().id);
35886             ti.setText(title);
35887             if(panel.tabTip !== undefined){
35888                 ti.setTooltip(panel.tabTip);
35889             }
35890         }
35891     },
35892
35893     updateTitle : function(title){
35894         if(this.titleTextEl && !this.config.title){
35895             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35896         }
35897     },
35898
35899     setActivePanel : function(panel)
35900     {
35901         panel = this.getPanel(panel);
35902         if(this.activePanel && this.activePanel != panel){
35903             if(this.activePanel.setActiveState(false) === false){
35904                 return;
35905             }
35906         }
35907         this.activePanel = panel;
35908         panel.setActiveState(true);
35909         if(this.panelSize){
35910             panel.setSize(this.panelSize.width, this.panelSize.height);
35911         }
35912         if(this.closeBtn){
35913             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35914         }
35915         this.updateTitle(panel.getTitle());
35916         if(this.tabs){
35917             this.fireEvent("invalidated", this);
35918         }
35919         this.fireEvent("panelactivated", this, panel);
35920     },
35921
35922     /**
35923      * Shows the specified panel.
35924      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35925      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35926      */
35927     showPanel : function(panel)
35928     {
35929         panel = this.getPanel(panel);
35930         if(panel){
35931             if(this.tabs){
35932                 var tab = this.tabs.getTab(panel.getEl().id);
35933                 if(tab.isHidden()){
35934                     this.tabs.unhideTab(tab.id);
35935                 }
35936                 tab.activate();
35937             }else{
35938                 this.setActivePanel(panel);
35939             }
35940         }
35941         return panel;
35942     },
35943
35944     /**
35945      * Get the active panel for this region.
35946      * @return {Roo.ContentPanel} The active panel or null
35947      */
35948     getActivePanel : function(){
35949         return this.activePanel;
35950     },
35951
35952     validateVisibility : function(){
35953         if(this.panels.getCount() < 1){
35954             this.updateTitle("&#160;");
35955             this.closeBtn.hide();
35956             this.hide();
35957         }else{
35958             if(!this.isVisible()){
35959                 this.show();
35960             }
35961         }
35962     },
35963
35964     /**
35965      * Adds the passed ContentPanel(s) to this region.
35966      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35967      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35968      */
35969     add : function(panel)
35970     {
35971         if(arguments.length > 1){
35972             for(var i = 0, len = arguments.length; i < len; i++) {
35973                 this.add(arguments[i]);
35974             }
35975             return null;
35976         }
35977         
35978         // if we have not been rendered yet, then we can not really do much of this..
35979         if (!this.bodyEl) {
35980             this.unrendered_panels.push(panel);
35981             return panel;
35982         }
35983         
35984         
35985         
35986         
35987         if(this.hasPanel(panel)){
35988             this.showPanel(panel);
35989             return panel;
35990         }
35991         panel.setRegion(this);
35992         this.panels.add(panel);
35993        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35994             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35995             // and hide them... ???
35996             this.bodyEl.dom.appendChild(panel.getEl().dom);
35997             if(panel.background !== true){
35998                 this.setActivePanel(panel);
35999             }
36000             this.fireEvent("paneladded", this, panel);
36001             return panel;
36002         }
36003         */
36004         if(!this.tabs){
36005             this.initTabs();
36006         }else{
36007             this.initPanelAsTab(panel);
36008         }
36009         
36010         
36011         if(panel.background !== true){
36012             this.tabs.activate(panel.getEl().id);
36013         }
36014         this.fireEvent("paneladded", this, panel);
36015         return panel;
36016     },
36017
36018     /**
36019      * Hides the tab for the specified panel.
36020      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36021      */
36022     hidePanel : function(panel){
36023         if(this.tabs && (panel = this.getPanel(panel))){
36024             this.tabs.hideTab(panel.getEl().id);
36025         }
36026     },
36027
36028     /**
36029      * Unhides the tab for a previously hidden panel.
36030      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36031      */
36032     unhidePanel : function(panel){
36033         if(this.tabs && (panel = this.getPanel(panel))){
36034             this.tabs.unhideTab(panel.getEl().id);
36035         }
36036     },
36037
36038     clearPanels : function(){
36039         while(this.panels.getCount() > 0){
36040              this.remove(this.panels.first());
36041         }
36042     },
36043
36044     /**
36045      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36046      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36047      * @param {Boolean} preservePanel Overrides the config preservePanel option
36048      * @return {Roo.ContentPanel} The panel that was removed
36049      */
36050     remove : function(panel, preservePanel)
36051     {
36052         panel = this.getPanel(panel);
36053         if(!panel){
36054             return null;
36055         }
36056         var e = {};
36057         this.fireEvent("beforeremove", this, panel, e);
36058         if(e.cancel === true){
36059             return null;
36060         }
36061         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36062         var panelId = panel.getId();
36063         this.panels.removeKey(panelId);
36064         if(preservePanel){
36065             document.body.appendChild(panel.getEl().dom);
36066         }
36067         if(this.tabs){
36068             this.tabs.removeTab(panel.getEl().id);
36069         }else if (!preservePanel){
36070             this.bodyEl.dom.removeChild(panel.getEl().dom);
36071         }
36072         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36073             var p = this.panels.first();
36074             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36075             tempEl.appendChild(p.getEl().dom);
36076             this.bodyEl.update("");
36077             this.bodyEl.dom.appendChild(p.getEl().dom);
36078             tempEl = null;
36079             this.updateTitle(p.getTitle());
36080             this.tabs = null;
36081             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36082             this.setActivePanel(p);
36083         }
36084         panel.setRegion(null);
36085         if(this.activePanel == panel){
36086             this.activePanel = null;
36087         }
36088         if(this.config.autoDestroy !== false && preservePanel !== true){
36089             try{panel.destroy();}catch(e){}
36090         }
36091         this.fireEvent("panelremoved", this, panel);
36092         return panel;
36093     },
36094
36095     /**
36096      * Returns the TabPanel component used by this region
36097      * @return {Roo.TabPanel}
36098      */
36099     getTabs : function(){
36100         return this.tabs;
36101     },
36102
36103     createTool : function(parentEl, className){
36104         var btn = Roo.DomHelper.append(parentEl, {
36105             tag: "div",
36106             cls: "x-layout-tools-button",
36107             children: [ {
36108                 tag: "div",
36109                 cls: "roo-layout-tools-button-inner " + className,
36110                 html: "&#160;"
36111             }]
36112         }, true);
36113         btn.addClassOnOver("roo-layout-tools-button-over");
36114         return btn;
36115     }
36116 });/*
36117  * Based on:
36118  * Ext JS Library 1.1.1
36119  * Copyright(c) 2006-2007, Ext JS, LLC.
36120  *
36121  * Originally Released Under LGPL - original licence link has changed is not relivant.
36122  *
36123  * Fork - LGPL
36124  * <script type="text/javascript">
36125  */
36126  
36127
36128
36129 /**
36130  * @class Roo.SplitLayoutRegion
36131  * @extends Roo.LayoutRegion
36132  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36133  */
36134 Roo.bootstrap.layout.Split = function(config){
36135     this.cursor = config.cursor;
36136     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36137 };
36138
36139 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36140 {
36141     splitTip : "Drag to resize.",
36142     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36143     useSplitTips : false,
36144
36145     applyConfig : function(config){
36146         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36147     },
36148     
36149     onRender : function(ctr,pos) {
36150         
36151         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36152         if(!this.config.split){
36153             return;
36154         }
36155         if(!this.split){
36156             
36157             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36158                             tag: "div",
36159                             id: this.el.id + "-split",
36160                             cls: "roo-layout-split roo-layout-split-"+this.position,
36161                             html: "&#160;"
36162             });
36163             /** The SplitBar for this region 
36164             * @type Roo.SplitBar */
36165             // does not exist yet...
36166             Roo.log([this.position, this.orientation]);
36167             
36168             this.split = new Roo.bootstrap.SplitBar({
36169                 dragElement : splitEl,
36170                 resizingElement: this.el,
36171                 orientation : this.orientation
36172             });
36173             
36174             this.split.on("moved", this.onSplitMove, this);
36175             this.split.useShim = this.config.useShim === true;
36176             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36177             if(this.useSplitTips){
36178                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36179             }
36180             //if(config.collapsible){
36181             //    this.split.el.on("dblclick", this.collapse,  this);
36182             //}
36183         }
36184         if(typeof this.config.minSize != "undefined"){
36185             this.split.minSize = this.config.minSize;
36186         }
36187         if(typeof this.config.maxSize != "undefined"){
36188             this.split.maxSize = this.config.maxSize;
36189         }
36190         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36191             this.hideSplitter();
36192         }
36193         
36194     },
36195
36196     getHMaxSize : function(){
36197          var cmax = this.config.maxSize || 10000;
36198          var center = this.mgr.getRegion("center");
36199          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36200     },
36201
36202     getVMaxSize : function(){
36203          var cmax = this.config.maxSize || 10000;
36204          var center = this.mgr.getRegion("center");
36205          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36206     },
36207
36208     onSplitMove : function(split, newSize){
36209         this.fireEvent("resized", this, newSize);
36210     },
36211     
36212     /** 
36213      * Returns the {@link Roo.SplitBar} for this region.
36214      * @return {Roo.SplitBar}
36215      */
36216     getSplitBar : function(){
36217         return this.split;
36218     },
36219     
36220     hide : function(){
36221         this.hideSplitter();
36222         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36223     },
36224
36225     hideSplitter : function(){
36226         if(this.split){
36227             this.split.el.setLocation(-2000,-2000);
36228             this.split.el.hide();
36229         }
36230     },
36231
36232     show : function(){
36233         if(this.split){
36234             this.split.el.show();
36235         }
36236         Roo.bootstrap.layout.Split.superclass.show.call(this);
36237     },
36238     
36239     beforeSlide: function(){
36240         if(Roo.isGecko){// firefox overflow auto bug workaround
36241             this.bodyEl.clip();
36242             if(this.tabs) {
36243                 this.tabs.bodyEl.clip();
36244             }
36245             if(this.activePanel){
36246                 this.activePanel.getEl().clip();
36247                 
36248                 if(this.activePanel.beforeSlide){
36249                     this.activePanel.beforeSlide();
36250                 }
36251             }
36252         }
36253     },
36254     
36255     afterSlide : function(){
36256         if(Roo.isGecko){// firefox overflow auto bug workaround
36257             this.bodyEl.unclip();
36258             if(this.tabs) {
36259                 this.tabs.bodyEl.unclip();
36260             }
36261             if(this.activePanel){
36262                 this.activePanel.getEl().unclip();
36263                 if(this.activePanel.afterSlide){
36264                     this.activePanel.afterSlide();
36265                 }
36266             }
36267         }
36268     },
36269
36270     initAutoHide : function(){
36271         if(this.autoHide !== false){
36272             if(!this.autoHideHd){
36273                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36274                 this.autoHideHd = {
36275                     "mouseout": function(e){
36276                         if(!e.within(this.el, true)){
36277                             st.delay(500);
36278                         }
36279                     },
36280                     "mouseover" : function(e){
36281                         st.cancel();
36282                     },
36283                     scope : this
36284                 };
36285             }
36286             this.el.on(this.autoHideHd);
36287         }
36288     },
36289
36290     clearAutoHide : function(){
36291         if(this.autoHide !== false){
36292             this.el.un("mouseout", this.autoHideHd.mouseout);
36293             this.el.un("mouseover", this.autoHideHd.mouseover);
36294         }
36295     },
36296
36297     clearMonitor : function(){
36298         Roo.get(document).un("click", this.slideInIf, this);
36299     },
36300
36301     // these names are backwards but not changed for compat
36302     slideOut : function(){
36303         if(this.isSlid || this.el.hasActiveFx()){
36304             return;
36305         }
36306         this.isSlid = true;
36307         if(this.collapseBtn){
36308             this.collapseBtn.hide();
36309         }
36310         this.closeBtnState = this.closeBtn.getStyle('display');
36311         this.closeBtn.hide();
36312         if(this.stickBtn){
36313             this.stickBtn.show();
36314         }
36315         this.el.show();
36316         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36317         this.beforeSlide();
36318         this.el.setStyle("z-index", 10001);
36319         this.el.slideIn(this.getSlideAnchor(), {
36320             callback: function(){
36321                 this.afterSlide();
36322                 this.initAutoHide();
36323                 Roo.get(document).on("click", this.slideInIf, this);
36324                 this.fireEvent("slideshow", this);
36325             },
36326             scope: this,
36327             block: true
36328         });
36329     },
36330
36331     afterSlideIn : function(){
36332         this.clearAutoHide();
36333         this.isSlid = false;
36334         this.clearMonitor();
36335         this.el.setStyle("z-index", "");
36336         if(this.collapseBtn){
36337             this.collapseBtn.show();
36338         }
36339         this.closeBtn.setStyle('display', this.closeBtnState);
36340         if(this.stickBtn){
36341             this.stickBtn.hide();
36342         }
36343         this.fireEvent("slidehide", this);
36344     },
36345
36346     slideIn : function(cb){
36347         if(!this.isSlid || this.el.hasActiveFx()){
36348             Roo.callback(cb);
36349             return;
36350         }
36351         this.isSlid = false;
36352         this.beforeSlide();
36353         this.el.slideOut(this.getSlideAnchor(), {
36354             callback: function(){
36355                 this.el.setLeftTop(-10000, -10000);
36356                 this.afterSlide();
36357                 this.afterSlideIn();
36358                 Roo.callback(cb);
36359             },
36360             scope: this,
36361             block: true
36362         });
36363     },
36364     
36365     slideInIf : function(e){
36366         if(!e.within(this.el)){
36367             this.slideIn();
36368         }
36369     },
36370
36371     animateCollapse : function(){
36372         this.beforeSlide();
36373         this.el.setStyle("z-index", 20000);
36374         var anchor = this.getSlideAnchor();
36375         this.el.slideOut(anchor, {
36376             callback : function(){
36377                 this.el.setStyle("z-index", "");
36378                 this.collapsedEl.slideIn(anchor, {duration:.3});
36379                 this.afterSlide();
36380                 this.el.setLocation(-10000,-10000);
36381                 this.el.hide();
36382                 this.fireEvent("collapsed", this);
36383             },
36384             scope: this,
36385             block: true
36386         });
36387     },
36388
36389     animateExpand : function(){
36390         this.beforeSlide();
36391         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36392         this.el.setStyle("z-index", 20000);
36393         this.collapsedEl.hide({
36394             duration:.1
36395         });
36396         this.el.slideIn(this.getSlideAnchor(), {
36397             callback : function(){
36398                 this.el.setStyle("z-index", "");
36399                 this.afterSlide();
36400                 if(this.split){
36401                     this.split.el.show();
36402                 }
36403                 this.fireEvent("invalidated", this);
36404                 this.fireEvent("expanded", this);
36405             },
36406             scope: this,
36407             block: true
36408         });
36409     },
36410
36411     anchors : {
36412         "west" : "left",
36413         "east" : "right",
36414         "north" : "top",
36415         "south" : "bottom"
36416     },
36417
36418     sanchors : {
36419         "west" : "l",
36420         "east" : "r",
36421         "north" : "t",
36422         "south" : "b"
36423     },
36424
36425     canchors : {
36426         "west" : "tl-tr",
36427         "east" : "tr-tl",
36428         "north" : "tl-bl",
36429         "south" : "bl-tl"
36430     },
36431
36432     getAnchor : function(){
36433         return this.anchors[this.position];
36434     },
36435
36436     getCollapseAnchor : function(){
36437         return this.canchors[this.position];
36438     },
36439
36440     getSlideAnchor : function(){
36441         return this.sanchors[this.position];
36442     },
36443
36444     getAlignAdj : function(){
36445         var cm = this.cmargins;
36446         switch(this.position){
36447             case "west":
36448                 return [0, 0];
36449             break;
36450             case "east":
36451                 return [0, 0];
36452             break;
36453             case "north":
36454                 return [0, 0];
36455             break;
36456             case "south":
36457                 return [0, 0];
36458             break;
36459         }
36460     },
36461
36462     getExpandAdj : function(){
36463         var c = this.collapsedEl, cm = this.cmargins;
36464         switch(this.position){
36465             case "west":
36466                 return [-(cm.right+c.getWidth()+cm.left), 0];
36467             break;
36468             case "east":
36469                 return [cm.right+c.getWidth()+cm.left, 0];
36470             break;
36471             case "north":
36472                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36473             break;
36474             case "south":
36475                 return [0, cm.top+cm.bottom+c.getHeight()];
36476             break;
36477         }
36478     }
36479 });/*
36480  * Based on:
36481  * Ext JS Library 1.1.1
36482  * Copyright(c) 2006-2007, Ext JS, LLC.
36483  *
36484  * Originally Released Under LGPL - original licence link has changed is not relivant.
36485  *
36486  * Fork - LGPL
36487  * <script type="text/javascript">
36488  */
36489 /*
36490  * These classes are private internal classes
36491  */
36492 Roo.bootstrap.layout.Center = function(config){
36493     config.region = "center";
36494     Roo.bootstrap.layout.Region.call(this, config);
36495     this.visible = true;
36496     this.minWidth = config.minWidth || 20;
36497     this.minHeight = config.minHeight || 20;
36498 };
36499
36500 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36501     hide : function(){
36502         // center panel can't be hidden
36503     },
36504     
36505     show : function(){
36506         // center panel can't be hidden
36507     },
36508     
36509     getMinWidth: function(){
36510         return this.minWidth;
36511     },
36512     
36513     getMinHeight: function(){
36514         return this.minHeight;
36515     }
36516 });
36517
36518
36519
36520
36521  
36522
36523
36524
36525
36526
36527 Roo.bootstrap.layout.North = function(config)
36528 {
36529     config.region = 'north';
36530     config.cursor = 'n-resize';
36531     
36532     Roo.bootstrap.layout.Split.call(this, config);
36533     
36534     
36535     if(this.split){
36536         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36537         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36538         this.split.el.addClass("roo-layout-split-v");
36539     }
36540     var size = config.initialSize || config.height;
36541     if(typeof size != "undefined"){
36542         this.el.setHeight(size);
36543     }
36544 };
36545 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36546 {
36547     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36548     
36549     
36550     
36551     getBox : function(){
36552         if(this.collapsed){
36553             return this.collapsedEl.getBox();
36554         }
36555         var box = this.el.getBox();
36556         if(this.split){
36557             box.height += this.split.el.getHeight();
36558         }
36559         return box;
36560     },
36561     
36562     updateBox : function(box){
36563         if(this.split && !this.collapsed){
36564             box.height -= this.split.el.getHeight();
36565             this.split.el.setLeft(box.x);
36566             this.split.el.setTop(box.y+box.height);
36567             this.split.el.setWidth(box.width);
36568         }
36569         if(this.collapsed){
36570             this.updateBody(box.width, null);
36571         }
36572         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36573     }
36574 });
36575
36576
36577
36578
36579
36580 Roo.bootstrap.layout.South = function(config){
36581     config.region = 'south';
36582     config.cursor = 's-resize';
36583     Roo.bootstrap.layout.Split.call(this, config);
36584     if(this.split){
36585         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36586         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36587         this.split.el.addClass("roo-layout-split-v");
36588     }
36589     var size = config.initialSize || config.height;
36590     if(typeof size != "undefined"){
36591         this.el.setHeight(size);
36592     }
36593 };
36594
36595 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36596     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36597     getBox : function(){
36598         if(this.collapsed){
36599             return this.collapsedEl.getBox();
36600         }
36601         var box = this.el.getBox();
36602         if(this.split){
36603             var sh = this.split.el.getHeight();
36604             box.height += sh;
36605             box.y -= sh;
36606         }
36607         return box;
36608     },
36609     
36610     updateBox : function(box){
36611         if(this.split && !this.collapsed){
36612             var sh = this.split.el.getHeight();
36613             box.height -= sh;
36614             box.y += sh;
36615             this.split.el.setLeft(box.x);
36616             this.split.el.setTop(box.y-sh);
36617             this.split.el.setWidth(box.width);
36618         }
36619         if(this.collapsed){
36620             this.updateBody(box.width, null);
36621         }
36622         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36623     }
36624 });
36625
36626 Roo.bootstrap.layout.East = function(config){
36627     config.region = "east";
36628     config.cursor = "e-resize";
36629     Roo.bootstrap.layout.Split.call(this, config);
36630     if(this.split){
36631         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36632         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36633         this.split.el.addClass("roo-layout-split-h");
36634     }
36635     var size = config.initialSize || config.width;
36636     if(typeof size != "undefined"){
36637         this.el.setWidth(size);
36638     }
36639 };
36640 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36641     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36642     getBox : function(){
36643         if(this.collapsed){
36644             return this.collapsedEl.getBox();
36645         }
36646         var box = this.el.getBox();
36647         if(this.split){
36648             var sw = this.split.el.getWidth();
36649             box.width += sw;
36650             box.x -= sw;
36651         }
36652         return box;
36653     },
36654
36655     updateBox : function(box){
36656         if(this.split && !this.collapsed){
36657             var sw = this.split.el.getWidth();
36658             box.width -= sw;
36659             this.split.el.setLeft(box.x);
36660             this.split.el.setTop(box.y);
36661             this.split.el.setHeight(box.height);
36662             box.x += sw;
36663         }
36664         if(this.collapsed){
36665             this.updateBody(null, box.height);
36666         }
36667         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36668     }
36669 });
36670
36671 Roo.bootstrap.layout.West = function(config){
36672     config.region = "west";
36673     config.cursor = "w-resize";
36674     
36675     Roo.bootstrap.layout.Split.call(this, config);
36676     if(this.split){
36677         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36678         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36679         this.split.el.addClass("roo-layout-split-h");
36680     }
36681     
36682 };
36683 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36684     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36685     
36686     onRender: function(ctr, pos)
36687     {
36688         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36689         var size = this.config.initialSize || this.config.width;
36690         if(typeof size != "undefined"){
36691             this.el.setWidth(size);
36692         }
36693     },
36694     
36695     getBox : function(){
36696         if(this.collapsed){
36697             return this.collapsedEl.getBox();
36698         }
36699         var box = this.el.getBox();
36700         if(this.split){
36701             box.width += this.split.el.getWidth();
36702         }
36703         return box;
36704     },
36705     
36706     updateBox : function(box){
36707         if(this.split && !this.collapsed){
36708             var sw = this.split.el.getWidth();
36709             box.width -= sw;
36710             this.split.el.setLeft(box.x+box.width);
36711             this.split.el.setTop(box.y);
36712             this.split.el.setHeight(box.height);
36713         }
36714         if(this.collapsed){
36715             this.updateBody(null, box.height);
36716         }
36717         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36718     }
36719 });
36720 Roo.namespace("Roo.bootstrap.panel");/*
36721  * Based on:
36722  * Ext JS Library 1.1.1
36723  * Copyright(c) 2006-2007, Ext JS, LLC.
36724  *
36725  * Originally Released Under LGPL - original licence link has changed is not relivant.
36726  *
36727  * Fork - LGPL
36728  * <script type="text/javascript">
36729  */
36730 /**
36731  * @class Roo.ContentPanel
36732  * @extends Roo.util.Observable
36733  * A basic ContentPanel element.
36734  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36735  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36736  * @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
36737  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36738  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36739  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36740  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36741  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36742  * @cfg {String} title          The title for this panel
36743  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36744  * @cfg {String} url            Calls {@link #setUrl} with this value
36745  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36746  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36747  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36748  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36749  * @cfg {Boolean} badges render the badges
36750
36751  * @constructor
36752  * Create a new ContentPanel.
36753  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36754  * @param {String/Object} config A string to set only the title or a config object
36755  * @param {String} content (optional) Set the HTML content for this panel
36756  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36757  */
36758 Roo.bootstrap.panel.Content = function( config){
36759     
36760     this.tpl = config.tpl || false;
36761     
36762     var el = config.el;
36763     var content = config.content;
36764
36765     if(config.autoCreate){ // xtype is available if this is called from factory
36766         el = Roo.id();
36767     }
36768     this.el = Roo.get(el);
36769     if(!this.el && config && config.autoCreate){
36770         if(typeof config.autoCreate == "object"){
36771             if(!config.autoCreate.id){
36772                 config.autoCreate.id = config.id||el;
36773             }
36774             this.el = Roo.DomHelper.append(document.body,
36775                         config.autoCreate, true);
36776         }else{
36777             var elcfg =  {   tag: "div",
36778                             cls: "roo-layout-inactive-content",
36779                             id: config.id||el
36780                             };
36781             if (config.html) {
36782                 elcfg.html = config.html;
36783                 
36784             }
36785                         
36786             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36787         }
36788     } 
36789     this.closable = false;
36790     this.loaded = false;
36791     this.active = false;
36792    
36793       
36794     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36795         
36796         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36797         
36798         this.wrapEl = this.el; //this.el.wrap();
36799         var ti = [];
36800         if (config.toolbar.items) {
36801             ti = config.toolbar.items ;
36802             delete config.toolbar.items ;
36803         }
36804         
36805         var nitems = [];
36806         this.toolbar.render(this.wrapEl, 'before');
36807         for(var i =0;i < ti.length;i++) {
36808           //  Roo.log(['add child', items[i]]);
36809             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36810         }
36811         this.toolbar.items = nitems;
36812         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36813         delete config.toolbar;
36814         
36815     }
36816     /*
36817     // xtype created footer. - not sure if will work as we normally have to render first..
36818     if (this.footer && !this.footer.el && this.footer.xtype) {
36819         if (!this.wrapEl) {
36820             this.wrapEl = this.el.wrap();
36821         }
36822     
36823         this.footer.container = this.wrapEl.createChild();
36824          
36825         this.footer = Roo.factory(this.footer, Roo);
36826         
36827     }
36828     */
36829     
36830      if(typeof config == "string"){
36831         this.title = config;
36832     }else{
36833         Roo.apply(this, config);
36834     }
36835     
36836     if(this.resizeEl){
36837         this.resizeEl = Roo.get(this.resizeEl, true);
36838     }else{
36839         this.resizeEl = this.el;
36840     }
36841     // handle view.xtype
36842     
36843  
36844     
36845     
36846     this.addEvents({
36847         /**
36848          * @event activate
36849          * Fires when this panel is activated. 
36850          * @param {Roo.ContentPanel} this
36851          */
36852         "activate" : true,
36853         /**
36854          * @event deactivate
36855          * Fires when this panel is activated. 
36856          * @param {Roo.ContentPanel} this
36857          */
36858         "deactivate" : true,
36859
36860         /**
36861          * @event resize
36862          * Fires when this panel is resized if fitToFrame is true.
36863          * @param {Roo.ContentPanel} this
36864          * @param {Number} width The width after any component adjustments
36865          * @param {Number} height The height after any component adjustments
36866          */
36867         "resize" : true,
36868         
36869          /**
36870          * @event render
36871          * Fires when this tab is created
36872          * @param {Roo.ContentPanel} this
36873          */
36874         "render" : true
36875         
36876         
36877         
36878     });
36879     
36880
36881     
36882     
36883     if(this.autoScroll){
36884         this.resizeEl.setStyle("overflow", "auto");
36885     } else {
36886         // fix randome scrolling
36887         //this.el.on('scroll', function() {
36888         //    Roo.log('fix random scolling');
36889         //    this.scrollTo('top',0); 
36890         //});
36891     }
36892     content = content || this.content;
36893     if(content){
36894         this.setContent(content);
36895     }
36896     if(config && config.url){
36897         this.setUrl(this.url, this.params, this.loadOnce);
36898     }
36899     
36900     
36901     
36902     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36903     
36904     if (this.view && typeof(this.view.xtype) != 'undefined') {
36905         this.view.el = this.el.appendChild(document.createElement("div"));
36906         this.view = Roo.factory(this.view); 
36907         this.view.render  &&  this.view.render(false, '');  
36908     }
36909     
36910     
36911     this.fireEvent('render', this);
36912 };
36913
36914 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36915     
36916     tabTip : '',
36917     
36918     setRegion : function(region){
36919         this.region = region;
36920         this.setActiveClass(region && !this.background);
36921     },
36922     
36923     
36924     setActiveClass: function(state)
36925     {
36926         if(state){
36927            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36928            this.el.setStyle('position','relative');
36929         }else{
36930            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36931            this.el.setStyle('position', 'absolute');
36932         } 
36933     },
36934     
36935     /**
36936      * Returns the toolbar for this Panel if one was configured. 
36937      * @return {Roo.Toolbar} 
36938      */
36939     getToolbar : function(){
36940         return this.toolbar;
36941     },
36942     
36943     setActiveState : function(active)
36944     {
36945         this.active = active;
36946         this.setActiveClass(active);
36947         if(!active){
36948             if(this.fireEvent("deactivate", this) === false){
36949                 return false;
36950             }
36951             return true;
36952         }
36953         this.fireEvent("activate", this);
36954         return true;
36955     },
36956     /**
36957      * Updates this panel's element
36958      * @param {String} content The new content
36959      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36960     */
36961     setContent : function(content, loadScripts){
36962         this.el.update(content, loadScripts);
36963     },
36964
36965     ignoreResize : function(w, h){
36966         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36967             return true;
36968         }else{
36969             this.lastSize = {width: w, height: h};
36970             return false;
36971         }
36972     },
36973     /**
36974      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36975      * @return {Roo.UpdateManager} The UpdateManager
36976      */
36977     getUpdateManager : function(){
36978         return this.el.getUpdateManager();
36979     },
36980      /**
36981      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36982      * @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:
36983 <pre><code>
36984 panel.load({
36985     url: "your-url.php",
36986     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36987     callback: yourFunction,
36988     scope: yourObject, //(optional scope)
36989     discardUrl: false,
36990     nocache: false,
36991     text: "Loading...",
36992     timeout: 30,
36993     scripts: false
36994 });
36995 </code></pre>
36996      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36997      * 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.
36998      * @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}
36999      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37000      * @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.
37001      * @return {Roo.ContentPanel} this
37002      */
37003     load : function(){
37004         var um = this.el.getUpdateManager();
37005         um.update.apply(um, arguments);
37006         return this;
37007     },
37008
37009
37010     /**
37011      * 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.
37012      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37013      * @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)
37014      * @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)
37015      * @return {Roo.UpdateManager} The UpdateManager
37016      */
37017     setUrl : function(url, params, loadOnce){
37018         if(this.refreshDelegate){
37019             this.removeListener("activate", this.refreshDelegate);
37020         }
37021         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37022         this.on("activate", this.refreshDelegate);
37023         return this.el.getUpdateManager();
37024     },
37025     
37026     _handleRefresh : function(url, params, loadOnce){
37027         if(!loadOnce || !this.loaded){
37028             var updater = this.el.getUpdateManager();
37029             updater.update(url, params, this._setLoaded.createDelegate(this));
37030         }
37031     },
37032     
37033     _setLoaded : function(){
37034         this.loaded = true;
37035     }, 
37036     
37037     /**
37038      * Returns this panel's id
37039      * @return {String} 
37040      */
37041     getId : function(){
37042         return this.el.id;
37043     },
37044     
37045     /** 
37046      * Returns this panel's element - used by regiosn to add.
37047      * @return {Roo.Element} 
37048      */
37049     getEl : function(){
37050         return this.wrapEl || this.el;
37051     },
37052     
37053    
37054     
37055     adjustForComponents : function(width, height)
37056     {
37057         //Roo.log('adjustForComponents ');
37058         if(this.resizeEl != this.el){
37059             width -= this.el.getFrameWidth('lr');
37060             height -= this.el.getFrameWidth('tb');
37061         }
37062         if(this.toolbar){
37063             var te = this.toolbar.getEl();
37064             te.setWidth(width);
37065             height -= te.getHeight();
37066         }
37067         if(this.footer){
37068             var te = this.footer.getEl();
37069             te.setWidth(width);
37070             height -= te.getHeight();
37071         }
37072         
37073         
37074         if(this.adjustments){
37075             width += this.adjustments[0];
37076             height += this.adjustments[1];
37077         }
37078         return {"width": width, "height": height};
37079     },
37080     
37081     setSize : function(width, height){
37082         if(this.fitToFrame && !this.ignoreResize(width, height)){
37083             if(this.fitContainer && this.resizeEl != this.el){
37084                 this.el.setSize(width, height);
37085             }
37086             var size = this.adjustForComponents(width, height);
37087             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37088             this.fireEvent('resize', this, size.width, size.height);
37089         }
37090     },
37091     
37092     /**
37093      * Returns this panel's title
37094      * @return {String} 
37095      */
37096     getTitle : function(){
37097         
37098         if (typeof(this.title) != 'object') {
37099             return this.title;
37100         }
37101         
37102         var t = '';
37103         for (var k in this.title) {
37104             if (!this.title.hasOwnProperty(k)) {
37105                 continue;
37106             }
37107             
37108             if (k.indexOf('-') >= 0) {
37109                 var s = k.split('-');
37110                 for (var i = 0; i<s.length; i++) {
37111                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37112                 }
37113             } else {
37114                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37115             }
37116         }
37117         return t;
37118     },
37119     
37120     /**
37121      * Set this panel's title
37122      * @param {String} title
37123      */
37124     setTitle : function(title){
37125         this.title = title;
37126         if(this.region){
37127             this.region.updatePanelTitle(this, title);
37128         }
37129     },
37130     
37131     /**
37132      * Returns true is this panel was configured to be closable
37133      * @return {Boolean} 
37134      */
37135     isClosable : function(){
37136         return this.closable;
37137     },
37138     
37139     beforeSlide : function(){
37140         this.el.clip();
37141         this.resizeEl.clip();
37142     },
37143     
37144     afterSlide : function(){
37145         this.el.unclip();
37146         this.resizeEl.unclip();
37147     },
37148     
37149     /**
37150      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37151      *   Will fail silently if the {@link #setUrl} method has not been called.
37152      *   This does not activate the panel, just updates its content.
37153      */
37154     refresh : function(){
37155         if(this.refreshDelegate){
37156            this.loaded = false;
37157            this.refreshDelegate();
37158         }
37159     },
37160     
37161     /**
37162      * Destroys this panel
37163      */
37164     destroy : function(){
37165         this.el.removeAllListeners();
37166         var tempEl = document.createElement("span");
37167         tempEl.appendChild(this.el.dom);
37168         tempEl.innerHTML = "";
37169         this.el.remove();
37170         this.el = null;
37171     },
37172     
37173     /**
37174      * form - if the content panel contains a form - this is a reference to it.
37175      * @type {Roo.form.Form}
37176      */
37177     form : false,
37178     /**
37179      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37180      *    This contains a reference to it.
37181      * @type {Roo.View}
37182      */
37183     view : false,
37184     
37185       /**
37186      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37187      * <pre><code>
37188
37189 layout.addxtype({
37190        xtype : 'Form',
37191        items: [ .... ]
37192    }
37193 );
37194
37195 </code></pre>
37196      * @param {Object} cfg Xtype definition of item to add.
37197      */
37198     
37199     
37200     getChildContainer: function () {
37201         return this.getEl();
37202     }
37203     
37204     
37205     /*
37206         var  ret = new Roo.factory(cfg);
37207         return ret;
37208         
37209         
37210         // add form..
37211         if (cfg.xtype.match(/^Form$/)) {
37212             
37213             var el;
37214             //if (this.footer) {
37215             //    el = this.footer.container.insertSibling(false, 'before');
37216             //} else {
37217                 el = this.el.createChild();
37218             //}
37219
37220             this.form = new  Roo.form.Form(cfg);
37221             
37222             
37223             if ( this.form.allItems.length) {
37224                 this.form.render(el.dom);
37225             }
37226             return this.form;
37227         }
37228         // should only have one of theses..
37229         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37230             // views.. should not be just added - used named prop 'view''
37231             
37232             cfg.el = this.el.appendChild(document.createElement("div"));
37233             // factory?
37234             
37235             var ret = new Roo.factory(cfg);
37236              
37237              ret.render && ret.render(false, ''); // render blank..
37238             this.view = ret;
37239             return ret;
37240         }
37241         return false;
37242     }
37243     \*/
37244 });
37245  
37246 /**
37247  * @class Roo.bootstrap.panel.Grid
37248  * @extends Roo.bootstrap.panel.Content
37249  * @constructor
37250  * Create a new GridPanel.
37251  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37252  * @param {Object} config A the config object
37253   
37254  */
37255
37256
37257
37258 Roo.bootstrap.panel.Grid = function(config)
37259 {
37260     
37261       
37262     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37263         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37264
37265     config.el = this.wrapper;
37266     //this.el = this.wrapper;
37267     
37268       if (config.container) {
37269         // ctor'ed from a Border/panel.grid
37270         
37271         
37272         this.wrapper.setStyle("overflow", "hidden");
37273         this.wrapper.addClass('roo-grid-container');
37274
37275     }
37276     
37277     
37278     if(config.toolbar){
37279         var tool_el = this.wrapper.createChild();    
37280         this.toolbar = Roo.factory(config.toolbar);
37281         var ti = [];
37282         if (config.toolbar.items) {
37283             ti = config.toolbar.items ;
37284             delete config.toolbar.items ;
37285         }
37286         
37287         var nitems = [];
37288         this.toolbar.render(tool_el);
37289         for(var i =0;i < ti.length;i++) {
37290           //  Roo.log(['add child', items[i]]);
37291             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37292         }
37293         this.toolbar.items = nitems;
37294         
37295         delete config.toolbar;
37296     }
37297     
37298     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37299     config.grid.scrollBody = true;;
37300     config.grid.monitorWindowResize = false; // turn off autosizing
37301     config.grid.autoHeight = false;
37302     config.grid.autoWidth = false;
37303     
37304     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37305     
37306     if (config.background) {
37307         // render grid on panel activation (if panel background)
37308         this.on('activate', function(gp) {
37309             if (!gp.grid.rendered) {
37310                 gp.grid.render(this.wrapper);
37311                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37312             }
37313         });
37314             
37315     } else {
37316         this.grid.render(this.wrapper);
37317         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37318
37319     }
37320     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37321     // ??? needed ??? config.el = this.wrapper;
37322     
37323     
37324     
37325   
37326     // xtype created footer. - not sure if will work as we normally have to render first..
37327     if (this.footer && !this.footer.el && this.footer.xtype) {
37328         
37329         var ctr = this.grid.getView().getFooterPanel(true);
37330         this.footer.dataSource = this.grid.dataSource;
37331         this.footer = Roo.factory(this.footer, Roo);
37332         this.footer.render(ctr);
37333         
37334     }
37335     
37336     
37337     
37338     
37339      
37340 };
37341
37342 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37343     getId : function(){
37344         return this.grid.id;
37345     },
37346     
37347     /**
37348      * Returns the grid for this panel
37349      * @return {Roo.bootstrap.Table} 
37350      */
37351     getGrid : function(){
37352         return this.grid;    
37353     },
37354     
37355     setSize : function(width, height){
37356         if(!this.ignoreResize(width, height)){
37357             var grid = this.grid;
37358             var size = this.adjustForComponents(width, height);
37359             var gridel = grid.getGridEl();
37360             gridel.setSize(size.width, size.height);
37361             /*
37362             var thd = grid.getGridEl().select('thead',true).first();
37363             var tbd = grid.getGridEl().select('tbody', true).first();
37364             if (tbd) {
37365                 tbd.setSize(width, height - thd.getHeight());
37366             }
37367             */
37368             grid.autoSize();
37369         }
37370     },
37371      
37372     
37373     
37374     beforeSlide : function(){
37375         this.grid.getView().scroller.clip();
37376     },
37377     
37378     afterSlide : function(){
37379         this.grid.getView().scroller.unclip();
37380     },
37381     
37382     destroy : function(){
37383         this.grid.destroy();
37384         delete this.grid;
37385         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37386     }
37387 });
37388
37389 /**
37390  * @class Roo.bootstrap.panel.Nest
37391  * @extends Roo.bootstrap.panel.Content
37392  * @constructor
37393  * Create a new Panel, that can contain a layout.Border.
37394  * 
37395  * 
37396  * @param {Roo.BorderLayout} layout The layout for this panel
37397  * @param {String/Object} config A string to set only the title or a config object
37398  */
37399 Roo.bootstrap.panel.Nest = function(config)
37400 {
37401     // construct with only one argument..
37402     /* FIXME - implement nicer consturctors
37403     if (layout.layout) {
37404         config = layout;
37405         layout = config.layout;
37406         delete config.layout;
37407     }
37408     if (layout.xtype && !layout.getEl) {
37409         // then layout needs constructing..
37410         layout = Roo.factory(layout, Roo);
37411     }
37412     */
37413     
37414     config.el =  config.layout.getEl();
37415     
37416     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37417     
37418     config.layout.monitorWindowResize = false; // turn off autosizing
37419     this.layout = config.layout;
37420     this.layout.getEl().addClass("roo-layout-nested-layout");
37421     
37422     
37423     
37424     
37425 };
37426
37427 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37428
37429     setSize : function(width, height){
37430         if(!this.ignoreResize(width, height)){
37431             var size = this.adjustForComponents(width, height);
37432             var el = this.layout.getEl();
37433             if (size.height < 1) {
37434                 el.setWidth(size.width);   
37435             } else {
37436                 el.setSize(size.width, size.height);
37437             }
37438             var touch = el.dom.offsetWidth;
37439             this.layout.layout();
37440             // ie requires a double layout on the first pass
37441             if(Roo.isIE && !this.initialized){
37442                 this.initialized = true;
37443                 this.layout.layout();
37444             }
37445         }
37446     },
37447     
37448     // activate all subpanels if not currently active..
37449     
37450     setActiveState : function(active){
37451         this.active = active;
37452         this.setActiveClass(active);
37453         
37454         if(!active){
37455             this.fireEvent("deactivate", this);
37456             return;
37457         }
37458         
37459         this.fireEvent("activate", this);
37460         // not sure if this should happen before or after..
37461         if (!this.layout) {
37462             return; // should not happen..
37463         }
37464         var reg = false;
37465         for (var r in this.layout.regions) {
37466             reg = this.layout.getRegion(r);
37467             if (reg.getActivePanel()) {
37468                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37469                 reg.setActivePanel(reg.getActivePanel());
37470                 continue;
37471             }
37472             if (!reg.panels.length) {
37473                 continue;
37474             }
37475             reg.showPanel(reg.getPanel(0));
37476         }
37477         
37478         
37479         
37480         
37481     },
37482     
37483     /**
37484      * Returns the nested BorderLayout for this panel
37485      * @return {Roo.BorderLayout} 
37486      */
37487     getLayout : function(){
37488         return this.layout;
37489     },
37490     
37491      /**
37492      * Adds a xtype elements to the layout of the nested panel
37493      * <pre><code>
37494
37495 panel.addxtype({
37496        xtype : 'ContentPanel',
37497        region: 'west',
37498        items: [ .... ]
37499    }
37500 );
37501
37502 panel.addxtype({
37503         xtype : 'NestedLayoutPanel',
37504         region: 'west',
37505         layout: {
37506            center: { },
37507            west: { }   
37508         },
37509         items : [ ... list of content panels or nested layout panels.. ]
37510    }
37511 );
37512 </code></pre>
37513      * @param {Object} cfg Xtype definition of item to add.
37514      */
37515     addxtype : function(cfg) {
37516         return this.layout.addxtype(cfg);
37517     
37518     }
37519 });        /*
37520  * Based on:
37521  * Ext JS Library 1.1.1
37522  * Copyright(c) 2006-2007, Ext JS, LLC.
37523  *
37524  * Originally Released Under LGPL - original licence link has changed is not relivant.
37525  *
37526  * Fork - LGPL
37527  * <script type="text/javascript">
37528  */
37529 /**
37530  * @class Roo.TabPanel
37531  * @extends Roo.util.Observable
37532  * A lightweight tab container.
37533  * <br><br>
37534  * Usage:
37535  * <pre><code>
37536 // basic tabs 1, built from existing content
37537 var tabs = new Roo.TabPanel("tabs1");
37538 tabs.addTab("script", "View Script");
37539 tabs.addTab("markup", "View Markup");
37540 tabs.activate("script");
37541
37542 // more advanced tabs, built from javascript
37543 var jtabs = new Roo.TabPanel("jtabs");
37544 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37545
37546 // set up the UpdateManager
37547 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37548 var updater = tab2.getUpdateManager();
37549 updater.setDefaultUrl("ajax1.htm");
37550 tab2.on('activate', updater.refresh, updater, true);
37551
37552 // Use setUrl for Ajax loading
37553 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37554 tab3.setUrl("ajax2.htm", null, true);
37555
37556 // Disabled tab
37557 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37558 tab4.disable();
37559
37560 jtabs.activate("jtabs-1");
37561  * </code></pre>
37562  * @constructor
37563  * Create a new TabPanel.
37564  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37565  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37566  */
37567 Roo.bootstrap.panel.Tabs = function(config){
37568     /**
37569     * The container element for this TabPanel.
37570     * @type Roo.Element
37571     */
37572     this.el = Roo.get(config.el);
37573     delete config.el;
37574     if(config){
37575         if(typeof config == "boolean"){
37576             this.tabPosition = config ? "bottom" : "top";
37577         }else{
37578             Roo.apply(this, config);
37579         }
37580     }
37581     
37582     if(this.tabPosition == "bottom"){
37583         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37584         this.el.addClass("roo-tabs-bottom");
37585     }
37586     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37587     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37588     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37589     if(Roo.isIE){
37590         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37591     }
37592     if(this.tabPosition != "bottom"){
37593         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37594          * @type Roo.Element
37595          */
37596         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37597         this.el.addClass("roo-tabs-top");
37598     }
37599     this.items = [];
37600
37601     this.bodyEl.setStyle("position", "relative");
37602
37603     this.active = null;
37604     this.activateDelegate = this.activate.createDelegate(this);
37605
37606     this.addEvents({
37607         /**
37608          * @event tabchange
37609          * Fires when the active tab changes
37610          * @param {Roo.TabPanel} this
37611          * @param {Roo.TabPanelItem} activePanel The new active tab
37612          */
37613         "tabchange": true,
37614         /**
37615          * @event beforetabchange
37616          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37617          * @param {Roo.TabPanel} this
37618          * @param {Object} e Set cancel to true on this object to cancel the tab change
37619          * @param {Roo.TabPanelItem} tab The tab being changed to
37620          */
37621         "beforetabchange" : true
37622     });
37623
37624     Roo.EventManager.onWindowResize(this.onResize, this);
37625     this.cpad = this.el.getPadding("lr");
37626     this.hiddenCount = 0;
37627
37628
37629     // toolbar on the tabbar support...
37630     if (this.toolbar) {
37631         alert("no toolbar support yet");
37632         this.toolbar  = false;
37633         /*
37634         var tcfg = this.toolbar;
37635         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37636         this.toolbar = new Roo.Toolbar(tcfg);
37637         if (Roo.isSafari) {
37638             var tbl = tcfg.container.child('table', true);
37639             tbl.setAttribute('width', '100%');
37640         }
37641         */
37642         
37643     }
37644    
37645
37646
37647     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37648 };
37649
37650 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37651     /*
37652      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37653      */
37654     tabPosition : "top",
37655     /*
37656      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37657      */
37658     currentTabWidth : 0,
37659     /*
37660      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37661      */
37662     minTabWidth : 40,
37663     /*
37664      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37665      */
37666     maxTabWidth : 250,
37667     /*
37668      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37669      */
37670     preferredTabWidth : 175,
37671     /*
37672      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37673      */
37674     resizeTabs : false,
37675     /*
37676      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37677      */
37678     monitorResize : true,
37679     /*
37680      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37681      */
37682     toolbar : false,
37683
37684     /**
37685      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37686      * @param {String} id The id of the div to use <b>or create</b>
37687      * @param {String} text The text for the tab
37688      * @param {String} content (optional) Content to put in the TabPanelItem body
37689      * @param {Boolean} closable (optional) True to create a close icon on the tab
37690      * @return {Roo.TabPanelItem} The created TabPanelItem
37691      */
37692     addTab : function(id, text, content, closable, tpl)
37693     {
37694         var item = new Roo.bootstrap.panel.TabItem({
37695             panel: this,
37696             id : id,
37697             text : text,
37698             closable : closable,
37699             tpl : tpl
37700         });
37701         this.addTabItem(item);
37702         if(content){
37703             item.setContent(content);
37704         }
37705         return item;
37706     },
37707
37708     /**
37709      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37710      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37711      * @return {Roo.TabPanelItem}
37712      */
37713     getTab : function(id){
37714         return this.items[id];
37715     },
37716
37717     /**
37718      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37719      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37720      */
37721     hideTab : function(id){
37722         var t = this.items[id];
37723         if(!t.isHidden()){
37724            t.setHidden(true);
37725            this.hiddenCount++;
37726            this.autoSizeTabs();
37727         }
37728     },
37729
37730     /**
37731      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37732      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37733      */
37734     unhideTab : function(id){
37735         var t = this.items[id];
37736         if(t.isHidden()){
37737            t.setHidden(false);
37738            this.hiddenCount--;
37739            this.autoSizeTabs();
37740         }
37741     },
37742
37743     /**
37744      * Adds an existing {@link Roo.TabPanelItem}.
37745      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37746      */
37747     addTabItem : function(item){
37748         this.items[item.id] = item;
37749         this.items.push(item);
37750       //  if(this.resizeTabs){
37751     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37752   //         this.autoSizeTabs();
37753 //        }else{
37754 //            item.autoSize();
37755        // }
37756     },
37757
37758     /**
37759      * Removes a {@link Roo.TabPanelItem}.
37760      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37761      */
37762     removeTab : function(id){
37763         var items = this.items;
37764         var tab = items[id];
37765         if(!tab) { return; }
37766         var index = items.indexOf(tab);
37767         if(this.active == tab && items.length > 1){
37768             var newTab = this.getNextAvailable(index);
37769             if(newTab) {
37770                 newTab.activate();
37771             }
37772         }
37773         this.stripEl.dom.removeChild(tab.pnode.dom);
37774         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37775             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37776         }
37777         items.splice(index, 1);
37778         delete this.items[tab.id];
37779         tab.fireEvent("close", tab);
37780         tab.purgeListeners();
37781         this.autoSizeTabs();
37782     },
37783
37784     getNextAvailable : function(start){
37785         var items = this.items;
37786         var index = start;
37787         // look for a next tab that will slide over to
37788         // replace the one being removed
37789         while(index < items.length){
37790             var item = items[++index];
37791             if(item && !item.isHidden()){
37792                 return item;
37793             }
37794         }
37795         // if one isn't found select the previous tab (on the left)
37796         index = start;
37797         while(index >= 0){
37798             var item = items[--index];
37799             if(item && !item.isHidden()){
37800                 return item;
37801             }
37802         }
37803         return null;
37804     },
37805
37806     /**
37807      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37808      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37809      */
37810     disableTab : function(id){
37811         var tab = this.items[id];
37812         if(tab && this.active != tab){
37813             tab.disable();
37814         }
37815     },
37816
37817     /**
37818      * Enables a {@link Roo.TabPanelItem} that is disabled.
37819      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37820      */
37821     enableTab : function(id){
37822         var tab = this.items[id];
37823         tab.enable();
37824     },
37825
37826     /**
37827      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37828      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37829      * @return {Roo.TabPanelItem} The TabPanelItem.
37830      */
37831     activate : function(id){
37832         var tab = this.items[id];
37833         if(!tab){
37834             return null;
37835         }
37836         if(tab == this.active || tab.disabled){
37837             return tab;
37838         }
37839         var e = {};
37840         this.fireEvent("beforetabchange", this, e, tab);
37841         if(e.cancel !== true && !tab.disabled){
37842             if(this.active){
37843                 this.active.hide();
37844             }
37845             this.active = this.items[id];
37846             this.active.show();
37847             this.fireEvent("tabchange", this, this.active);
37848         }
37849         return tab;
37850     },
37851
37852     /**
37853      * Gets the active {@link Roo.TabPanelItem}.
37854      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37855      */
37856     getActiveTab : function(){
37857         return this.active;
37858     },
37859
37860     /**
37861      * Updates the tab body element to fit the height of the container element
37862      * for overflow scrolling
37863      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37864      */
37865     syncHeight : function(targetHeight){
37866         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37867         var bm = this.bodyEl.getMargins();
37868         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37869         this.bodyEl.setHeight(newHeight);
37870         return newHeight;
37871     },
37872
37873     onResize : function(){
37874         if(this.monitorResize){
37875             this.autoSizeTabs();
37876         }
37877     },
37878
37879     /**
37880      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37881      */
37882     beginUpdate : function(){
37883         this.updating = true;
37884     },
37885
37886     /**
37887      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37888      */
37889     endUpdate : function(){
37890         this.updating = false;
37891         this.autoSizeTabs();
37892     },
37893
37894     /**
37895      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37896      */
37897     autoSizeTabs : function(){
37898         var count = this.items.length;
37899         var vcount = count - this.hiddenCount;
37900         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37901             return;
37902         }
37903         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37904         var availWidth = Math.floor(w / vcount);
37905         var b = this.stripBody;
37906         if(b.getWidth() > w){
37907             var tabs = this.items;
37908             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37909             if(availWidth < this.minTabWidth){
37910                 /*if(!this.sleft){    // incomplete scrolling code
37911                     this.createScrollButtons();
37912                 }
37913                 this.showScroll();
37914                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37915             }
37916         }else{
37917             if(this.currentTabWidth < this.preferredTabWidth){
37918                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37919             }
37920         }
37921     },
37922
37923     /**
37924      * Returns the number of tabs in this TabPanel.
37925      * @return {Number}
37926      */
37927      getCount : function(){
37928          return this.items.length;
37929      },
37930
37931     /**
37932      * Resizes all the tabs to the passed width
37933      * @param {Number} The new width
37934      */
37935     setTabWidth : function(width){
37936         this.currentTabWidth = width;
37937         for(var i = 0, len = this.items.length; i < len; i++) {
37938                 if(!this.items[i].isHidden()) {
37939                 this.items[i].setWidth(width);
37940             }
37941         }
37942     },
37943
37944     /**
37945      * Destroys this TabPanel
37946      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37947      */
37948     destroy : function(removeEl){
37949         Roo.EventManager.removeResizeListener(this.onResize, this);
37950         for(var i = 0, len = this.items.length; i < len; i++){
37951             this.items[i].purgeListeners();
37952         }
37953         if(removeEl === true){
37954             this.el.update("");
37955             this.el.remove();
37956         }
37957     },
37958     
37959     createStrip : function(container)
37960     {
37961         var strip = document.createElement("nav");
37962         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37963         container.appendChild(strip);
37964         return strip;
37965     },
37966     
37967     createStripList : function(strip)
37968     {
37969         // div wrapper for retard IE
37970         // returns the "tr" element.
37971         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37972         //'<div class="x-tabs-strip-wrap">'+
37973           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37974           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37975         return strip.firstChild; //.firstChild.firstChild.firstChild;
37976     },
37977     createBody : function(container)
37978     {
37979         var body = document.createElement("div");
37980         Roo.id(body, "tab-body");
37981         //Roo.fly(body).addClass("x-tabs-body");
37982         Roo.fly(body).addClass("tab-content");
37983         container.appendChild(body);
37984         return body;
37985     },
37986     createItemBody :function(bodyEl, id){
37987         var body = Roo.getDom(id);
37988         if(!body){
37989             body = document.createElement("div");
37990             body.id = id;
37991         }
37992         //Roo.fly(body).addClass("x-tabs-item-body");
37993         Roo.fly(body).addClass("tab-pane");
37994          bodyEl.insertBefore(body, bodyEl.firstChild);
37995         return body;
37996     },
37997     /** @private */
37998     createStripElements :  function(stripEl, text, closable, tpl)
37999     {
38000         var td = document.createElement("li"); // was td..
38001         
38002         
38003         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38004         
38005         
38006         stripEl.appendChild(td);
38007         /*if(closable){
38008             td.className = "x-tabs-closable";
38009             if(!this.closeTpl){
38010                 this.closeTpl = new Roo.Template(
38011                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38012                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38013                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38014                 );
38015             }
38016             var el = this.closeTpl.overwrite(td, {"text": text});
38017             var close = el.getElementsByTagName("div")[0];
38018             var inner = el.getElementsByTagName("em")[0];
38019             return {"el": el, "close": close, "inner": inner};
38020         } else {
38021         */
38022         // not sure what this is..
38023 //            if(!this.tabTpl){
38024                 //this.tabTpl = new Roo.Template(
38025                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38026                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38027                 //);
38028 //                this.tabTpl = new Roo.Template(
38029 //                   '<a href="#">' +
38030 //                   '<span unselectable="on"' +
38031 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38032 //                            ' >{text}</span></a>'
38033 //                );
38034 //                
38035 //            }
38036
38037
38038             var template = tpl || this.tabTpl || false;
38039             
38040             if(!template){
38041                 
38042                 template = new Roo.Template(
38043                    '<a href="#">' +
38044                    '<span unselectable="on"' +
38045                             (this.disableTooltips ? '' : ' title="{text}"') +
38046                             ' >{text}</span></a>'
38047                 );
38048             }
38049             
38050             switch (typeof(template)) {
38051                 case 'object' :
38052                     break;
38053                 case 'string' :
38054                     template = new Roo.Template(template);
38055                     break;
38056                 default :
38057                     break;
38058             }
38059             
38060             var el = template.overwrite(td, {"text": text});
38061             
38062             var inner = el.getElementsByTagName("span")[0];
38063             
38064             return {"el": el, "inner": inner};
38065             
38066     }
38067         
38068     
38069 });
38070
38071 /**
38072  * @class Roo.TabPanelItem
38073  * @extends Roo.util.Observable
38074  * Represents an individual item (tab plus body) in a TabPanel.
38075  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38076  * @param {String} id The id of this TabPanelItem
38077  * @param {String} text The text for the tab of this TabPanelItem
38078  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38079  */
38080 Roo.bootstrap.panel.TabItem = function(config){
38081     /**
38082      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38083      * @type Roo.TabPanel
38084      */
38085     this.tabPanel = config.panel;
38086     /**
38087      * The id for this TabPanelItem
38088      * @type String
38089      */
38090     this.id = config.id;
38091     /** @private */
38092     this.disabled = false;
38093     /** @private */
38094     this.text = config.text;
38095     /** @private */
38096     this.loaded = false;
38097     this.closable = config.closable;
38098
38099     /**
38100      * The body element for this TabPanelItem.
38101      * @type Roo.Element
38102      */
38103     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38104     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38105     this.bodyEl.setStyle("display", "block");
38106     this.bodyEl.setStyle("zoom", "1");
38107     //this.hideAction();
38108
38109     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38110     /** @private */
38111     this.el = Roo.get(els.el);
38112     this.inner = Roo.get(els.inner, true);
38113     this.textEl = Roo.get(this.el.dom.firstChild, true);
38114     this.pnode = Roo.get(els.el.parentNode, true);
38115 //    this.el.on("mousedown", this.onTabMouseDown, this);
38116     this.el.on("click", this.onTabClick, this);
38117     /** @private */
38118     if(config.closable){
38119         var c = Roo.get(els.close, true);
38120         c.dom.title = this.closeText;
38121         c.addClassOnOver("close-over");
38122         c.on("click", this.closeClick, this);
38123      }
38124
38125     this.addEvents({
38126          /**
38127          * @event activate
38128          * Fires when this tab becomes the active tab.
38129          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38130          * @param {Roo.TabPanelItem} this
38131          */
38132         "activate": true,
38133         /**
38134          * @event beforeclose
38135          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38136          * @param {Roo.TabPanelItem} this
38137          * @param {Object} e Set cancel to true on this object to cancel the close.
38138          */
38139         "beforeclose": true,
38140         /**
38141          * @event close
38142          * Fires when this tab is closed.
38143          * @param {Roo.TabPanelItem} this
38144          */
38145          "close": true,
38146         /**
38147          * @event deactivate
38148          * Fires when this tab is no longer the active tab.
38149          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38150          * @param {Roo.TabPanelItem} this
38151          */
38152          "deactivate" : true
38153     });
38154     this.hidden = false;
38155
38156     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38157 };
38158
38159 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38160            {
38161     purgeListeners : function(){
38162        Roo.util.Observable.prototype.purgeListeners.call(this);
38163        this.el.removeAllListeners();
38164     },
38165     /**
38166      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38167      */
38168     show : function(){
38169         this.pnode.addClass("active");
38170         this.showAction();
38171         if(Roo.isOpera){
38172             this.tabPanel.stripWrap.repaint();
38173         }
38174         this.fireEvent("activate", this.tabPanel, this);
38175     },
38176
38177     /**
38178      * Returns true if this tab is the active tab.
38179      * @return {Boolean}
38180      */
38181     isActive : function(){
38182         return this.tabPanel.getActiveTab() == this;
38183     },
38184
38185     /**
38186      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38187      */
38188     hide : function(){
38189         this.pnode.removeClass("active");
38190         this.hideAction();
38191         this.fireEvent("deactivate", this.tabPanel, this);
38192     },
38193
38194     hideAction : function(){
38195         this.bodyEl.hide();
38196         this.bodyEl.setStyle("position", "absolute");
38197         this.bodyEl.setLeft("-20000px");
38198         this.bodyEl.setTop("-20000px");
38199     },
38200
38201     showAction : function(){
38202         this.bodyEl.setStyle("position", "relative");
38203         this.bodyEl.setTop("");
38204         this.bodyEl.setLeft("");
38205         this.bodyEl.show();
38206     },
38207
38208     /**
38209      * Set the tooltip for the tab.
38210      * @param {String} tooltip The tab's tooltip
38211      */
38212     setTooltip : function(text){
38213         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38214             this.textEl.dom.qtip = text;
38215             this.textEl.dom.removeAttribute('title');
38216         }else{
38217             this.textEl.dom.title = text;
38218         }
38219     },
38220
38221     onTabClick : function(e){
38222         e.preventDefault();
38223         this.tabPanel.activate(this.id);
38224     },
38225
38226     onTabMouseDown : function(e){
38227         e.preventDefault();
38228         this.tabPanel.activate(this.id);
38229     },
38230 /*
38231     getWidth : function(){
38232         return this.inner.getWidth();
38233     },
38234
38235     setWidth : function(width){
38236         var iwidth = width - this.pnode.getPadding("lr");
38237         this.inner.setWidth(iwidth);
38238         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38239         this.pnode.setWidth(width);
38240     },
38241 */
38242     /**
38243      * Show or hide the tab
38244      * @param {Boolean} hidden True to hide or false to show.
38245      */
38246     setHidden : function(hidden){
38247         this.hidden = hidden;
38248         this.pnode.setStyle("display", hidden ? "none" : "");
38249     },
38250
38251     /**
38252      * Returns true if this tab is "hidden"
38253      * @return {Boolean}
38254      */
38255     isHidden : function(){
38256         return this.hidden;
38257     },
38258
38259     /**
38260      * Returns the text for this tab
38261      * @return {String}
38262      */
38263     getText : function(){
38264         return this.text;
38265     },
38266     /*
38267     autoSize : function(){
38268         //this.el.beginMeasure();
38269         this.textEl.setWidth(1);
38270         /*
38271          *  #2804 [new] Tabs in Roojs
38272          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38273          */
38274         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38275         //this.el.endMeasure();
38276     //},
38277
38278     /**
38279      * Sets the text for the tab (Note: this also sets the tooltip text)
38280      * @param {String} text The tab's text and tooltip
38281      */
38282     setText : function(text){
38283         this.text = text;
38284         this.textEl.update(text);
38285         this.setTooltip(text);
38286         //if(!this.tabPanel.resizeTabs){
38287         //    this.autoSize();
38288         //}
38289     },
38290     /**
38291      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38292      */
38293     activate : function(){
38294         this.tabPanel.activate(this.id);
38295     },
38296
38297     /**
38298      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38299      */
38300     disable : function(){
38301         if(this.tabPanel.active != this){
38302             this.disabled = true;
38303             this.pnode.addClass("disabled");
38304         }
38305     },
38306
38307     /**
38308      * Enables this TabPanelItem if it was previously disabled.
38309      */
38310     enable : function(){
38311         this.disabled = false;
38312         this.pnode.removeClass("disabled");
38313     },
38314
38315     /**
38316      * Sets the content for this TabPanelItem.
38317      * @param {String} content The content
38318      * @param {Boolean} loadScripts true to look for and load scripts
38319      */
38320     setContent : function(content, loadScripts){
38321         this.bodyEl.update(content, loadScripts);
38322     },
38323
38324     /**
38325      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38326      * @return {Roo.UpdateManager} The UpdateManager
38327      */
38328     getUpdateManager : function(){
38329         return this.bodyEl.getUpdateManager();
38330     },
38331
38332     /**
38333      * Set a URL to be used to load the content for this TabPanelItem.
38334      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38335      * @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)
38336      * @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)
38337      * @return {Roo.UpdateManager} The UpdateManager
38338      */
38339     setUrl : function(url, params, loadOnce){
38340         if(this.refreshDelegate){
38341             this.un('activate', this.refreshDelegate);
38342         }
38343         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38344         this.on("activate", this.refreshDelegate);
38345         return this.bodyEl.getUpdateManager();
38346     },
38347
38348     /** @private */
38349     _handleRefresh : function(url, params, loadOnce){
38350         if(!loadOnce || !this.loaded){
38351             var updater = this.bodyEl.getUpdateManager();
38352             updater.update(url, params, this._setLoaded.createDelegate(this));
38353         }
38354     },
38355
38356     /**
38357      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38358      *   Will fail silently if the setUrl method has not been called.
38359      *   This does not activate the panel, just updates its content.
38360      */
38361     refresh : function(){
38362         if(this.refreshDelegate){
38363            this.loaded = false;
38364            this.refreshDelegate();
38365         }
38366     },
38367
38368     /** @private */
38369     _setLoaded : function(){
38370         this.loaded = true;
38371     },
38372
38373     /** @private */
38374     closeClick : function(e){
38375         var o = {};
38376         e.stopEvent();
38377         this.fireEvent("beforeclose", this, o);
38378         if(o.cancel !== true){
38379             this.tabPanel.removeTab(this.id);
38380         }
38381     },
38382     /**
38383      * The text displayed in the tooltip for the close icon.
38384      * @type String
38385      */
38386     closeText : "Close this tab"
38387 });
38388 /**
38389 *    This script refer to:
38390 *    Title: International Telephone Input
38391 *    Author: Jack O'Connor
38392 *    Code version:  v12.1.12
38393 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38394 **/
38395
38396 Roo.bootstrap.PhoneInputData = function() {
38397     var d = [
38398       [
38399         "Afghanistan (‫افغانستان‬‎)",
38400         "af",
38401         "93"
38402       ],
38403       [
38404         "Albania (Shqipëri)",
38405         "al",
38406         "355"
38407       ],
38408       [
38409         "Algeria (‫الجزائر‬‎)",
38410         "dz",
38411         "213"
38412       ],
38413       [
38414         "American Samoa",
38415         "as",
38416         "1684"
38417       ],
38418       [
38419         "Andorra",
38420         "ad",
38421         "376"
38422       ],
38423       [
38424         "Angola",
38425         "ao",
38426         "244"
38427       ],
38428       [
38429         "Anguilla",
38430         "ai",
38431         "1264"
38432       ],
38433       [
38434         "Antigua and Barbuda",
38435         "ag",
38436         "1268"
38437       ],
38438       [
38439         "Argentina",
38440         "ar",
38441         "54"
38442       ],
38443       [
38444         "Armenia (Հայաստան)",
38445         "am",
38446         "374"
38447       ],
38448       [
38449         "Aruba",
38450         "aw",
38451         "297"
38452       ],
38453       [
38454         "Australia",
38455         "au",
38456         "61",
38457         0
38458       ],
38459       [
38460         "Austria (Österreich)",
38461         "at",
38462         "43"
38463       ],
38464       [
38465         "Azerbaijan (Azərbaycan)",
38466         "az",
38467         "994"
38468       ],
38469       [
38470         "Bahamas",
38471         "bs",
38472         "1242"
38473       ],
38474       [
38475         "Bahrain (‫البحرين‬‎)",
38476         "bh",
38477         "973"
38478       ],
38479       [
38480         "Bangladesh (বাংলাদেশ)",
38481         "bd",
38482         "880"
38483       ],
38484       [
38485         "Barbados",
38486         "bb",
38487         "1246"
38488       ],
38489       [
38490         "Belarus (Беларусь)",
38491         "by",
38492         "375"
38493       ],
38494       [
38495         "Belgium (België)",
38496         "be",
38497         "32"
38498       ],
38499       [
38500         "Belize",
38501         "bz",
38502         "501"
38503       ],
38504       [
38505         "Benin (Bénin)",
38506         "bj",
38507         "229"
38508       ],
38509       [
38510         "Bermuda",
38511         "bm",
38512         "1441"
38513       ],
38514       [
38515         "Bhutan (འབྲུག)",
38516         "bt",
38517         "975"
38518       ],
38519       [
38520         "Bolivia",
38521         "bo",
38522         "591"
38523       ],
38524       [
38525         "Bosnia and Herzegovina (Босна и Херцеговина)",
38526         "ba",
38527         "387"
38528       ],
38529       [
38530         "Botswana",
38531         "bw",
38532         "267"
38533       ],
38534       [
38535         "Brazil (Brasil)",
38536         "br",
38537         "55"
38538       ],
38539       [
38540         "British Indian Ocean Territory",
38541         "io",
38542         "246"
38543       ],
38544       [
38545         "British Virgin Islands",
38546         "vg",
38547         "1284"
38548       ],
38549       [
38550         "Brunei",
38551         "bn",
38552         "673"
38553       ],
38554       [
38555         "Bulgaria (България)",
38556         "bg",
38557         "359"
38558       ],
38559       [
38560         "Burkina Faso",
38561         "bf",
38562         "226"
38563       ],
38564       [
38565         "Burundi (Uburundi)",
38566         "bi",
38567         "257"
38568       ],
38569       [
38570         "Cambodia (កម្ពុជា)",
38571         "kh",
38572         "855"
38573       ],
38574       [
38575         "Cameroon (Cameroun)",
38576         "cm",
38577         "237"
38578       ],
38579       [
38580         "Canada",
38581         "ca",
38582         "1",
38583         1,
38584         ["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"]
38585       ],
38586       [
38587         "Cape Verde (Kabu Verdi)",
38588         "cv",
38589         "238"
38590       ],
38591       [
38592         "Caribbean Netherlands",
38593         "bq",
38594         "599",
38595         1
38596       ],
38597       [
38598         "Cayman Islands",
38599         "ky",
38600         "1345"
38601       ],
38602       [
38603         "Central African Republic (République centrafricaine)",
38604         "cf",
38605         "236"
38606       ],
38607       [
38608         "Chad (Tchad)",
38609         "td",
38610         "235"
38611       ],
38612       [
38613         "Chile",
38614         "cl",
38615         "56"
38616       ],
38617       [
38618         "China (中国)",
38619         "cn",
38620         "86"
38621       ],
38622       [
38623         "Christmas Island",
38624         "cx",
38625         "61",
38626         2
38627       ],
38628       [
38629         "Cocos (Keeling) Islands",
38630         "cc",
38631         "61",
38632         1
38633       ],
38634       [
38635         "Colombia",
38636         "co",
38637         "57"
38638       ],
38639       [
38640         "Comoros (‫جزر القمر‬‎)",
38641         "km",
38642         "269"
38643       ],
38644       [
38645         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38646         "cd",
38647         "243"
38648       ],
38649       [
38650         "Congo (Republic) (Congo-Brazzaville)",
38651         "cg",
38652         "242"
38653       ],
38654       [
38655         "Cook Islands",
38656         "ck",
38657         "682"
38658       ],
38659       [
38660         "Costa Rica",
38661         "cr",
38662         "506"
38663       ],
38664       [
38665         "Côte d’Ivoire",
38666         "ci",
38667         "225"
38668       ],
38669       [
38670         "Croatia (Hrvatska)",
38671         "hr",
38672         "385"
38673       ],
38674       [
38675         "Cuba",
38676         "cu",
38677         "53"
38678       ],
38679       [
38680         "Curaçao",
38681         "cw",
38682         "599",
38683         0
38684       ],
38685       [
38686         "Cyprus (Κύπρος)",
38687         "cy",
38688         "357"
38689       ],
38690       [
38691         "Czech Republic (Česká republika)",
38692         "cz",
38693         "420"
38694       ],
38695       [
38696         "Denmark (Danmark)",
38697         "dk",
38698         "45"
38699       ],
38700       [
38701         "Djibouti",
38702         "dj",
38703         "253"
38704       ],
38705       [
38706         "Dominica",
38707         "dm",
38708         "1767"
38709       ],
38710       [
38711         "Dominican Republic (República Dominicana)",
38712         "do",
38713         "1",
38714         2,
38715         ["809", "829", "849"]
38716       ],
38717       [
38718         "Ecuador",
38719         "ec",
38720         "593"
38721       ],
38722       [
38723         "Egypt (‫مصر‬‎)",
38724         "eg",
38725         "20"
38726       ],
38727       [
38728         "El Salvador",
38729         "sv",
38730         "503"
38731       ],
38732       [
38733         "Equatorial Guinea (Guinea Ecuatorial)",
38734         "gq",
38735         "240"
38736       ],
38737       [
38738         "Eritrea",
38739         "er",
38740         "291"
38741       ],
38742       [
38743         "Estonia (Eesti)",
38744         "ee",
38745         "372"
38746       ],
38747       [
38748         "Ethiopia",
38749         "et",
38750         "251"
38751       ],
38752       [
38753         "Falkland Islands (Islas Malvinas)",
38754         "fk",
38755         "500"
38756       ],
38757       [
38758         "Faroe Islands (Føroyar)",
38759         "fo",
38760         "298"
38761       ],
38762       [
38763         "Fiji",
38764         "fj",
38765         "679"
38766       ],
38767       [
38768         "Finland (Suomi)",
38769         "fi",
38770         "358",
38771         0
38772       ],
38773       [
38774         "France",
38775         "fr",
38776         "33"
38777       ],
38778       [
38779         "French Guiana (Guyane française)",
38780         "gf",
38781         "594"
38782       ],
38783       [
38784         "French Polynesia (Polynésie française)",
38785         "pf",
38786         "689"
38787       ],
38788       [
38789         "Gabon",
38790         "ga",
38791         "241"
38792       ],
38793       [
38794         "Gambia",
38795         "gm",
38796         "220"
38797       ],
38798       [
38799         "Georgia (საქართველო)",
38800         "ge",
38801         "995"
38802       ],
38803       [
38804         "Germany (Deutschland)",
38805         "de",
38806         "49"
38807       ],
38808       [
38809         "Ghana (Gaana)",
38810         "gh",
38811         "233"
38812       ],
38813       [
38814         "Gibraltar",
38815         "gi",
38816         "350"
38817       ],
38818       [
38819         "Greece (Ελλάδα)",
38820         "gr",
38821         "30"
38822       ],
38823       [
38824         "Greenland (Kalaallit Nunaat)",
38825         "gl",
38826         "299"
38827       ],
38828       [
38829         "Grenada",
38830         "gd",
38831         "1473"
38832       ],
38833       [
38834         "Guadeloupe",
38835         "gp",
38836         "590",
38837         0
38838       ],
38839       [
38840         "Guam",
38841         "gu",
38842         "1671"
38843       ],
38844       [
38845         "Guatemala",
38846         "gt",
38847         "502"
38848       ],
38849       [
38850         "Guernsey",
38851         "gg",
38852         "44",
38853         1
38854       ],
38855       [
38856         "Guinea (Guinée)",
38857         "gn",
38858         "224"
38859       ],
38860       [
38861         "Guinea-Bissau (Guiné Bissau)",
38862         "gw",
38863         "245"
38864       ],
38865       [
38866         "Guyana",
38867         "gy",
38868         "592"
38869       ],
38870       [
38871         "Haiti",
38872         "ht",
38873         "509"
38874       ],
38875       [
38876         "Honduras",
38877         "hn",
38878         "504"
38879       ],
38880       [
38881         "Hong Kong (香港)",
38882         "hk",
38883         "852"
38884       ],
38885       [
38886         "Hungary (Magyarország)",
38887         "hu",
38888         "36"
38889       ],
38890       [
38891         "Iceland (Ísland)",
38892         "is",
38893         "354"
38894       ],
38895       [
38896         "India (भारत)",
38897         "in",
38898         "91"
38899       ],
38900       [
38901         "Indonesia",
38902         "id",
38903         "62"
38904       ],
38905       [
38906         "Iran (‫ایران‬‎)",
38907         "ir",
38908         "98"
38909       ],
38910       [
38911         "Iraq (‫العراق‬‎)",
38912         "iq",
38913         "964"
38914       ],
38915       [
38916         "Ireland",
38917         "ie",
38918         "353"
38919       ],
38920       [
38921         "Isle of Man",
38922         "im",
38923         "44",
38924         2
38925       ],
38926       [
38927         "Israel (‫ישראל‬‎)",
38928         "il",
38929         "972"
38930       ],
38931       [
38932         "Italy (Italia)",
38933         "it",
38934         "39",
38935         0
38936       ],
38937       [
38938         "Jamaica",
38939         "jm",
38940         "1876"
38941       ],
38942       [
38943         "Japan (日本)",
38944         "jp",
38945         "81"
38946       ],
38947       [
38948         "Jersey",
38949         "je",
38950         "44",
38951         3
38952       ],
38953       [
38954         "Jordan (‫الأردن‬‎)",
38955         "jo",
38956         "962"
38957       ],
38958       [
38959         "Kazakhstan (Казахстан)",
38960         "kz",
38961         "7",
38962         1
38963       ],
38964       [
38965         "Kenya",
38966         "ke",
38967         "254"
38968       ],
38969       [
38970         "Kiribati",
38971         "ki",
38972         "686"
38973       ],
38974       [
38975         "Kosovo",
38976         "xk",
38977         "383"
38978       ],
38979       [
38980         "Kuwait (‫الكويت‬‎)",
38981         "kw",
38982         "965"
38983       ],
38984       [
38985         "Kyrgyzstan (Кыргызстан)",
38986         "kg",
38987         "996"
38988       ],
38989       [
38990         "Laos (ລາວ)",
38991         "la",
38992         "856"
38993       ],
38994       [
38995         "Latvia (Latvija)",
38996         "lv",
38997         "371"
38998       ],
38999       [
39000         "Lebanon (‫لبنان‬‎)",
39001         "lb",
39002         "961"
39003       ],
39004       [
39005         "Lesotho",
39006         "ls",
39007         "266"
39008       ],
39009       [
39010         "Liberia",
39011         "lr",
39012         "231"
39013       ],
39014       [
39015         "Libya (‫ليبيا‬‎)",
39016         "ly",
39017         "218"
39018       ],
39019       [
39020         "Liechtenstein",
39021         "li",
39022         "423"
39023       ],
39024       [
39025         "Lithuania (Lietuva)",
39026         "lt",
39027         "370"
39028       ],
39029       [
39030         "Luxembourg",
39031         "lu",
39032         "352"
39033       ],
39034       [
39035         "Macau (澳門)",
39036         "mo",
39037         "853"
39038       ],
39039       [
39040         "Macedonia (FYROM) (Македонија)",
39041         "mk",
39042         "389"
39043       ],
39044       [
39045         "Madagascar (Madagasikara)",
39046         "mg",
39047         "261"
39048       ],
39049       [
39050         "Malawi",
39051         "mw",
39052         "265"
39053       ],
39054       [
39055         "Malaysia",
39056         "my",
39057         "60"
39058       ],
39059       [
39060         "Maldives",
39061         "mv",
39062         "960"
39063       ],
39064       [
39065         "Mali",
39066         "ml",
39067         "223"
39068       ],
39069       [
39070         "Malta",
39071         "mt",
39072         "356"
39073       ],
39074       [
39075         "Marshall Islands",
39076         "mh",
39077         "692"
39078       ],
39079       [
39080         "Martinique",
39081         "mq",
39082         "596"
39083       ],
39084       [
39085         "Mauritania (‫موريتانيا‬‎)",
39086         "mr",
39087         "222"
39088       ],
39089       [
39090         "Mauritius (Moris)",
39091         "mu",
39092         "230"
39093       ],
39094       [
39095         "Mayotte",
39096         "yt",
39097         "262",
39098         1
39099       ],
39100       [
39101         "Mexico (México)",
39102         "mx",
39103         "52"
39104       ],
39105       [
39106         "Micronesia",
39107         "fm",
39108         "691"
39109       ],
39110       [
39111         "Moldova (Republica Moldova)",
39112         "md",
39113         "373"
39114       ],
39115       [
39116         "Monaco",
39117         "mc",
39118         "377"
39119       ],
39120       [
39121         "Mongolia (Монгол)",
39122         "mn",
39123         "976"
39124       ],
39125       [
39126         "Montenegro (Crna Gora)",
39127         "me",
39128         "382"
39129       ],
39130       [
39131         "Montserrat",
39132         "ms",
39133         "1664"
39134       ],
39135       [
39136         "Morocco (‫المغرب‬‎)",
39137         "ma",
39138         "212",
39139         0
39140       ],
39141       [
39142         "Mozambique (Moçambique)",
39143         "mz",
39144         "258"
39145       ],
39146       [
39147         "Myanmar (Burma) (မြန်မာ)",
39148         "mm",
39149         "95"
39150       ],
39151       [
39152         "Namibia (Namibië)",
39153         "na",
39154         "264"
39155       ],
39156       [
39157         "Nauru",
39158         "nr",
39159         "674"
39160       ],
39161       [
39162         "Nepal (नेपाल)",
39163         "np",
39164         "977"
39165       ],
39166       [
39167         "Netherlands (Nederland)",
39168         "nl",
39169         "31"
39170       ],
39171       [
39172         "New Caledonia (Nouvelle-Calédonie)",
39173         "nc",
39174         "687"
39175       ],
39176       [
39177         "New Zealand",
39178         "nz",
39179         "64"
39180       ],
39181       [
39182         "Nicaragua",
39183         "ni",
39184         "505"
39185       ],
39186       [
39187         "Niger (Nijar)",
39188         "ne",
39189         "227"
39190       ],
39191       [
39192         "Nigeria",
39193         "ng",
39194         "234"
39195       ],
39196       [
39197         "Niue",
39198         "nu",
39199         "683"
39200       ],
39201       [
39202         "Norfolk Island",
39203         "nf",
39204         "672"
39205       ],
39206       [
39207         "North Korea (조선 민주주의 인민 공화국)",
39208         "kp",
39209         "850"
39210       ],
39211       [
39212         "Northern Mariana Islands",
39213         "mp",
39214         "1670"
39215       ],
39216       [
39217         "Norway (Norge)",
39218         "no",
39219         "47",
39220         0
39221       ],
39222       [
39223         "Oman (‫عُمان‬‎)",
39224         "om",
39225         "968"
39226       ],
39227       [
39228         "Pakistan (‫پاکستان‬‎)",
39229         "pk",
39230         "92"
39231       ],
39232       [
39233         "Palau",
39234         "pw",
39235         "680"
39236       ],
39237       [
39238         "Palestine (‫فلسطين‬‎)",
39239         "ps",
39240         "970"
39241       ],
39242       [
39243         "Panama (Panamá)",
39244         "pa",
39245         "507"
39246       ],
39247       [
39248         "Papua New Guinea",
39249         "pg",
39250         "675"
39251       ],
39252       [
39253         "Paraguay",
39254         "py",
39255         "595"
39256       ],
39257       [
39258         "Peru (Perú)",
39259         "pe",
39260         "51"
39261       ],
39262       [
39263         "Philippines",
39264         "ph",
39265         "63"
39266       ],
39267       [
39268         "Poland (Polska)",
39269         "pl",
39270         "48"
39271       ],
39272       [
39273         "Portugal",
39274         "pt",
39275         "351"
39276       ],
39277       [
39278         "Puerto Rico",
39279         "pr",
39280         "1",
39281         3,
39282         ["787", "939"]
39283       ],
39284       [
39285         "Qatar (‫قطر‬‎)",
39286         "qa",
39287         "974"
39288       ],
39289       [
39290         "Réunion (La Réunion)",
39291         "re",
39292         "262",
39293         0
39294       ],
39295       [
39296         "Romania (România)",
39297         "ro",
39298         "40"
39299       ],
39300       [
39301         "Russia (Россия)",
39302         "ru",
39303         "7",
39304         0
39305       ],
39306       [
39307         "Rwanda",
39308         "rw",
39309         "250"
39310       ],
39311       [
39312         "Saint Barthélemy",
39313         "bl",
39314         "590",
39315         1
39316       ],
39317       [
39318         "Saint Helena",
39319         "sh",
39320         "290"
39321       ],
39322       [
39323         "Saint Kitts and Nevis",
39324         "kn",
39325         "1869"
39326       ],
39327       [
39328         "Saint Lucia",
39329         "lc",
39330         "1758"
39331       ],
39332       [
39333         "Saint Martin (Saint-Martin (partie française))",
39334         "mf",
39335         "590",
39336         2
39337       ],
39338       [
39339         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39340         "pm",
39341         "508"
39342       ],
39343       [
39344         "Saint Vincent and the Grenadines",
39345         "vc",
39346         "1784"
39347       ],
39348       [
39349         "Samoa",
39350         "ws",
39351         "685"
39352       ],
39353       [
39354         "San Marino",
39355         "sm",
39356         "378"
39357       ],
39358       [
39359         "São Tomé and Príncipe (São Tomé e Príncipe)",
39360         "st",
39361         "239"
39362       ],
39363       [
39364         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39365         "sa",
39366         "966"
39367       ],
39368       [
39369         "Senegal (Sénégal)",
39370         "sn",
39371         "221"
39372       ],
39373       [
39374         "Serbia (Србија)",
39375         "rs",
39376         "381"
39377       ],
39378       [
39379         "Seychelles",
39380         "sc",
39381         "248"
39382       ],
39383       [
39384         "Sierra Leone",
39385         "sl",
39386         "232"
39387       ],
39388       [
39389         "Singapore",
39390         "sg",
39391         "65"
39392       ],
39393       [
39394         "Sint Maarten",
39395         "sx",
39396         "1721"
39397       ],
39398       [
39399         "Slovakia (Slovensko)",
39400         "sk",
39401         "421"
39402       ],
39403       [
39404         "Slovenia (Slovenija)",
39405         "si",
39406         "386"
39407       ],
39408       [
39409         "Solomon Islands",
39410         "sb",
39411         "677"
39412       ],
39413       [
39414         "Somalia (Soomaaliya)",
39415         "so",
39416         "252"
39417       ],
39418       [
39419         "South Africa",
39420         "za",
39421         "27"
39422       ],
39423       [
39424         "South Korea (대한민국)",
39425         "kr",
39426         "82"
39427       ],
39428       [
39429         "South Sudan (‫جنوب السودان‬‎)",
39430         "ss",
39431         "211"
39432       ],
39433       [
39434         "Spain (España)",
39435         "es",
39436         "34"
39437       ],
39438       [
39439         "Sri Lanka (ශ්‍රී ලංකාව)",
39440         "lk",
39441         "94"
39442       ],
39443       [
39444         "Sudan (‫السودان‬‎)",
39445         "sd",
39446         "249"
39447       ],
39448       [
39449         "Suriname",
39450         "sr",
39451         "597"
39452       ],
39453       [
39454         "Svalbard and Jan Mayen",
39455         "sj",
39456         "47",
39457         1
39458       ],
39459       [
39460         "Swaziland",
39461         "sz",
39462         "268"
39463       ],
39464       [
39465         "Sweden (Sverige)",
39466         "se",
39467         "46"
39468       ],
39469       [
39470         "Switzerland (Schweiz)",
39471         "ch",
39472         "41"
39473       ],
39474       [
39475         "Syria (‫سوريا‬‎)",
39476         "sy",
39477         "963"
39478       ],
39479       [
39480         "Taiwan (台灣)",
39481         "tw",
39482         "886"
39483       ],
39484       [
39485         "Tajikistan",
39486         "tj",
39487         "992"
39488       ],
39489       [
39490         "Tanzania",
39491         "tz",
39492         "255"
39493       ],
39494       [
39495         "Thailand (ไทย)",
39496         "th",
39497         "66"
39498       ],
39499       [
39500         "Timor-Leste",
39501         "tl",
39502         "670"
39503       ],
39504       [
39505         "Togo",
39506         "tg",
39507         "228"
39508       ],
39509       [
39510         "Tokelau",
39511         "tk",
39512         "690"
39513       ],
39514       [
39515         "Tonga",
39516         "to",
39517         "676"
39518       ],
39519       [
39520         "Trinidad and Tobago",
39521         "tt",
39522         "1868"
39523       ],
39524       [
39525         "Tunisia (‫تونس‬‎)",
39526         "tn",
39527         "216"
39528       ],
39529       [
39530         "Turkey (Türkiye)",
39531         "tr",
39532         "90"
39533       ],
39534       [
39535         "Turkmenistan",
39536         "tm",
39537         "993"
39538       ],
39539       [
39540         "Turks and Caicos Islands",
39541         "tc",
39542         "1649"
39543       ],
39544       [
39545         "Tuvalu",
39546         "tv",
39547         "688"
39548       ],
39549       [
39550         "U.S. Virgin Islands",
39551         "vi",
39552         "1340"
39553       ],
39554       [
39555         "Uganda",
39556         "ug",
39557         "256"
39558       ],
39559       [
39560         "Ukraine (Україна)",
39561         "ua",
39562         "380"
39563       ],
39564       [
39565         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39566         "ae",
39567         "971"
39568       ],
39569       [
39570         "United Kingdom",
39571         "gb",
39572         "44",
39573         0
39574       ],
39575       [
39576         "United States",
39577         "us",
39578         "1",
39579         0
39580       ],
39581       [
39582         "Uruguay",
39583         "uy",
39584         "598"
39585       ],
39586       [
39587         "Uzbekistan (Oʻzbekiston)",
39588         "uz",
39589         "998"
39590       ],
39591       [
39592         "Vanuatu",
39593         "vu",
39594         "678"
39595       ],
39596       [
39597         "Vatican City (Città del Vaticano)",
39598         "va",
39599         "39",
39600         1
39601       ],
39602       [
39603         "Venezuela",
39604         "ve",
39605         "58"
39606       ],
39607       [
39608         "Vietnam (Việt Nam)",
39609         "vn",
39610         "84"
39611       ],
39612       [
39613         "Wallis and Futuna (Wallis-et-Futuna)",
39614         "wf",
39615         "681"
39616       ],
39617       [
39618         "Western Sahara (‫الصحراء الغربية‬‎)",
39619         "eh",
39620         "212",
39621         1
39622       ],
39623       [
39624         "Yemen (‫اليمن‬‎)",
39625         "ye",
39626         "967"
39627       ],
39628       [
39629         "Zambia",
39630         "zm",
39631         "260"
39632       ],
39633       [
39634         "Zimbabwe",
39635         "zw",
39636         "263"
39637       ],
39638       [
39639         "Åland Islands",
39640         "ax",
39641         "358",
39642         1
39643       ]
39644   ];
39645   
39646   return d;
39647 }/**
39648 *    This script refer to:
39649 *    Title: International Telephone Input
39650 *    Author: Jack O'Connor
39651 *    Code version:  v12.1.12
39652 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39653 **/
39654
39655 /**
39656  * @class Roo.bootstrap.PhoneInput
39657  * @extends Roo.bootstrap.TriggerField
39658  * An input with International dial-code selection
39659  
39660  * @cfg {String} defaultDialCode default '+852'
39661  * @cfg {Array} preferedCountries default []
39662   
39663  * @constructor
39664  * Create a new PhoneInput.
39665  * @param {Object} config Configuration options
39666  */
39667
39668 Roo.bootstrap.PhoneInput = function(config) {
39669     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39670 };
39671
39672 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39673         
39674         listWidth: undefined,
39675         
39676         selectedClass: 'active',
39677         
39678         invalidClass : "has-warning",
39679         
39680         validClass: 'has-success',
39681         
39682         allowed: '0123456789',
39683         
39684         /**
39685          * @cfg {String} defaultDialCode The default dial code when initializing the input
39686          */
39687         defaultDialCode: '+852',
39688         
39689         /**
39690          * @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
39691          */
39692         preferedCountries: false,
39693         
39694         getAutoCreate : function()
39695         {
39696             var data = Roo.bootstrap.PhoneInputData();
39697             var align = this.labelAlign || this.parentLabelAlign();
39698             var id = Roo.id();
39699             
39700             this.allCountries = [];
39701             this.dialCodeMapping = [];
39702             
39703             for (var i = 0; i < data.length; i++) {
39704               var c = data[i];
39705               this.allCountries[i] = {
39706                 name: c[0],
39707                 iso2: c[1],
39708                 dialCode: c[2],
39709                 priority: c[3] || 0,
39710                 areaCodes: c[4] || null
39711               };
39712               this.dialCodeMapping[c[2]] = {
39713                   name: c[0],
39714                   iso2: c[1],
39715                   priority: c[3] || 0,
39716                   areaCodes: c[4] || null
39717               };
39718             }
39719             
39720             var cfg = {
39721                 cls: 'form-group',
39722                 cn: []
39723             };
39724             
39725             var input =  {
39726                 tag: 'input',
39727                 id : id,
39728                 cls : 'form-control tel-input',
39729                 autocomplete: 'new-password'
39730             };
39731             
39732             var hiddenInput = {
39733                 tag: 'input',
39734                 type: 'hidden',
39735                 cls: 'hidden-tel-input'
39736             };
39737             
39738             if (this.name) {
39739                 hiddenInput.name = this.name;
39740             }
39741             
39742             if (this.disabled) {
39743                 input.disabled = true;
39744             }
39745             
39746             var flag_container = {
39747                 tag: 'div',
39748                 cls: 'flag-box',
39749                 cn: [
39750                     {
39751                         tag: 'div',
39752                         cls: 'flag'
39753                     },
39754                     {
39755                         tag: 'div',
39756                         cls: 'caret'
39757                     }
39758                 ]
39759             };
39760             
39761             var box = {
39762                 tag: 'div',
39763                 cls: this.hasFeedback ? 'has-feedback' : '',
39764                 cn: [
39765                     hiddenInput,
39766                     input,
39767                     {
39768                         tag: 'input',
39769                         cls: 'dial-code-holder',
39770                         disabled: true
39771                     }
39772                 ]
39773             };
39774             
39775             var container = {
39776                 cls: 'roo-select2-container input-group',
39777                 cn: [
39778                     flag_container,
39779                     box
39780                 ]
39781             };
39782             
39783             if (this.fieldLabel.length) {
39784                 var indicator = {
39785                     tag: 'i',
39786                     tooltip: 'This field is required'
39787                 };
39788                 
39789                 var label = {
39790                     tag: 'label',
39791                     'for':  id,
39792                     cls: 'control-label',
39793                     cn: []
39794                 };
39795                 
39796                 var label_text = {
39797                     tag: 'span',
39798                     html: this.fieldLabel
39799                 };
39800                 
39801                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39802                 label.cn = [
39803                     indicator,
39804                     label_text
39805                 ];
39806                 
39807                 if(this.indicatorpos == 'right') {
39808                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39809                     label.cn = [
39810                         label_text,
39811                         indicator
39812                     ];
39813                 }
39814                 
39815                 if(align == 'left') {
39816                     container = {
39817                         tag: 'div',
39818                         cn: [
39819                             container
39820                         ]
39821                     };
39822                     
39823                     if(this.labelWidth > 12){
39824                         label.style = "width: " + this.labelWidth + 'px';
39825                     }
39826                     if(this.labelWidth < 13 && this.labelmd == 0){
39827                         this.labelmd = this.labelWidth;
39828                     }
39829                     if(this.labellg > 0){
39830                         label.cls += ' col-lg-' + this.labellg;
39831                         input.cls += ' col-lg-' + (12 - this.labellg);
39832                     }
39833                     if(this.labelmd > 0){
39834                         label.cls += ' col-md-' + this.labelmd;
39835                         container.cls += ' col-md-' + (12 - this.labelmd);
39836                     }
39837                     if(this.labelsm > 0){
39838                         label.cls += ' col-sm-' + this.labelsm;
39839                         container.cls += ' col-sm-' + (12 - this.labelsm);
39840                     }
39841                     if(this.labelxs > 0){
39842                         label.cls += ' col-xs-' + this.labelxs;
39843                         container.cls += ' col-xs-' + (12 - this.labelxs);
39844                     }
39845                 }
39846             }
39847             
39848             cfg.cn = [
39849                 label,
39850                 container
39851             ];
39852             
39853             var settings = this;
39854             
39855             ['xs','sm','md','lg'].map(function(size){
39856                 if (settings[size]) {
39857                     cfg.cls += ' col-' + size + '-' + settings[size];
39858                 }
39859             });
39860             
39861             this.store = new Roo.data.Store({
39862                 proxy : new Roo.data.MemoryProxy({}),
39863                 reader : new Roo.data.JsonReader({
39864                     fields : [
39865                         {
39866                             'name' : 'name',
39867                             'type' : 'string'
39868                         },
39869                         {
39870                             'name' : 'iso2',
39871                             'type' : 'string'
39872                         },
39873                         {
39874                             'name' : 'dialCode',
39875                             'type' : 'string'
39876                         },
39877                         {
39878                             'name' : 'priority',
39879                             'type' : 'string'
39880                         },
39881                         {
39882                             'name' : 'areaCodes',
39883                             'type' : 'string'
39884                         }
39885                     ]
39886                 })
39887             });
39888             
39889             if(!this.preferedCountries) {
39890                 this.preferedCountries = [
39891                     'hk',
39892                     'gb',
39893                     'us'
39894                 ];
39895             }
39896             
39897             var p = this.preferedCountries.reverse();
39898             
39899             if(p) {
39900                 for (var i = 0; i < p.length; i++) {
39901                     for (var j = 0; j < this.allCountries.length; j++) {
39902                         if(this.allCountries[j].iso2 == p[i]) {
39903                             var t = this.allCountries[j];
39904                             this.allCountries.splice(j,1);
39905                             this.allCountries.unshift(t);
39906                         }
39907                     } 
39908                 }
39909             }
39910             
39911             this.store.proxy.data = {
39912                 success: true,
39913                 data: this.allCountries
39914             };
39915             
39916             return cfg;
39917         },
39918         
39919         initEvents : function()
39920         {
39921             this.createList();
39922             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39923             
39924             this.indicator = this.indicatorEl();
39925             this.flag = this.flagEl();
39926             this.dialCodeHolder = this.dialCodeHolderEl();
39927             
39928             this.trigger = this.el.select('div.flag-box',true).first();
39929             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39930             
39931             var _this = this;
39932             
39933             (function(){
39934                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39935                 _this.list.setWidth(lw);
39936             }).defer(100);
39937             
39938             this.list.on('mouseover', this.onViewOver, this);
39939             this.list.on('mousemove', this.onViewMove, this);
39940             this.inputEl().on("keyup", this.onKeyUp, this);
39941             
39942             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39943
39944             this.view = new Roo.View(this.list, this.tpl, {
39945                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39946             });
39947             
39948             this.view.on('click', this.onViewClick, this);
39949             this.setValue(this.defaultDialCode);
39950         },
39951         
39952         onTriggerClick : function(e)
39953         {
39954             Roo.log('trigger click');
39955             if(this.disabled){
39956                 return;
39957             }
39958             
39959             if(this.isExpanded()){
39960                 this.collapse();
39961                 this.hasFocus = false;
39962             }else {
39963                 this.store.load({});
39964                 this.hasFocus = true;
39965                 this.expand();
39966             }
39967         },
39968         
39969         isExpanded : function()
39970         {
39971             return this.list.isVisible();
39972         },
39973         
39974         collapse : function()
39975         {
39976             if(!this.isExpanded()){
39977                 return;
39978             }
39979             this.list.hide();
39980             Roo.get(document).un('mousedown', this.collapseIf, this);
39981             Roo.get(document).un('mousewheel', this.collapseIf, this);
39982             this.fireEvent('collapse', this);
39983             this.validate();
39984         },
39985         
39986         expand : function()
39987         {
39988             Roo.log('expand');
39989
39990             if(this.isExpanded() || !this.hasFocus){
39991                 return;
39992             }
39993             
39994             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39995             this.list.setWidth(lw);
39996             
39997             this.list.show();
39998             this.restrictHeight();
39999             
40000             Roo.get(document).on('mousedown', this.collapseIf, this);
40001             Roo.get(document).on('mousewheel', this.collapseIf, this);
40002             
40003             this.fireEvent('expand', this);
40004         },
40005         
40006         restrictHeight : function()
40007         {
40008             this.list.alignTo(this.inputEl(), this.listAlign);
40009             this.list.alignTo(this.inputEl(), this.listAlign);
40010         },
40011         
40012         onViewOver : function(e, t)
40013         {
40014             if(this.inKeyMode){
40015                 return;
40016             }
40017             var item = this.view.findItemFromChild(t);
40018             
40019             if(item){
40020                 var index = this.view.indexOf(item);
40021                 this.select(index, false);
40022             }
40023         },
40024
40025         // private
40026         onViewClick : function(view, doFocus, el, e)
40027         {
40028             var index = this.view.getSelectedIndexes()[0];
40029             
40030             var r = this.store.getAt(index);
40031             
40032             if(r){
40033                 this.onSelect(r, index);
40034             }
40035             if(doFocus !== false && !this.blockFocus){
40036                 this.inputEl().focus();
40037             }
40038         },
40039         
40040         onViewMove : function(e, t)
40041         {
40042             this.inKeyMode = false;
40043         },
40044         
40045         select : function(index, scrollIntoView)
40046         {
40047             this.selectedIndex = index;
40048             this.view.select(index);
40049             if(scrollIntoView !== false){
40050                 var el = this.view.getNode(index);
40051                 if(el){
40052                     this.list.scrollChildIntoView(el, false);
40053                 }
40054             }
40055         },
40056         
40057         createList : function()
40058         {
40059             this.list = Roo.get(document.body).createChild({
40060                 tag: 'ul',
40061                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40062                 style: 'display:none'
40063             });
40064             
40065             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40066         },
40067         
40068         collapseIf : function(e)
40069         {
40070             var in_combo  = e.within(this.el);
40071             var in_list =  e.within(this.list);
40072             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40073             
40074             if (in_combo || in_list || is_list) {
40075                 return;
40076             }
40077             this.collapse();
40078         },
40079         
40080         onSelect : function(record, index)
40081         {
40082             if(this.fireEvent('beforeselect', this, record, index) !== false){
40083                 
40084                 this.setFlagClass(record.data.iso2);
40085                 this.setDialCode(record.data.dialCode);
40086                 this.hasFocus = false;
40087                 this.collapse();
40088                 this.fireEvent('select', this, record, index);
40089             }
40090         },
40091         
40092         flagEl : function()
40093         {
40094             var flag = this.el.select('div.flag',true).first();
40095             if(!flag){
40096                 return false;
40097             }
40098             return flag;
40099         },
40100         
40101         dialCodeHolderEl : function()
40102         {
40103             var d = this.el.select('input.dial-code-holder',true).first();
40104             if(!d){
40105                 return false;
40106             }
40107             return d;
40108         },
40109         
40110         setDialCode : function(v)
40111         {
40112             this.dialCodeHolder.dom.value = '+'+v;
40113         },
40114         
40115         setFlagClass : function(n)
40116         {
40117             this.flag.dom.className = 'flag '+n;
40118         },
40119         
40120         getValue : function()
40121         {
40122             var v = this.inputEl().getValue();
40123             if(this.dialCodeHolder) {
40124                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40125             }
40126             return v;
40127         },
40128         
40129         setValue : function(v)
40130         {
40131             var d = this.getDialCode(v);
40132             
40133             //invalid dial code
40134             if(v.length == 0 || !d || d.length == 0) {
40135                 if(this.rendered){
40136                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40137                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40138                 }
40139                 return;
40140             }
40141             
40142             //valid dial code
40143             this.setFlagClass(this.dialCodeMapping[d].iso2);
40144             this.setDialCode(d);
40145             this.inputEl().dom.value = v.replace('+'+d,'');
40146             this.hiddenEl().dom.value = this.getValue();
40147             
40148             this.validate();
40149         },
40150         
40151         getDialCode : function(v)
40152         {
40153             v = v ||  '';
40154             
40155             if (v.length == 0) {
40156                 return this.dialCodeHolder.dom.value;
40157             }
40158             
40159             var dialCode = "";
40160             if (v.charAt(0) != "+") {
40161                 return false;
40162             }
40163             var numericChars = "";
40164             for (var i = 1; i < v.length; i++) {
40165               var c = v.charAt(i);
40166               if (!isNaN(c)) {
40167                 numericChars += c;
40168                 if (this.dialCodeMapping[numericChars]) {
40169                   dialCode = v.substr(1, i);
40170                 }
40171                 if (numericChars.length == 4) {
40172                   break;
40173                 }
40174               }
40175             }
40176             return dialCode;
40177         },
40178         
40179         reset : function()
40180         {
40181             this.setValue(this.defaultDialCode);
40182             this.validate();
40183         },
40184         
40185         hiddenEl : function()
40186         {
40187             return this.el.select('input.hidden-tel-input',true).first();
40188         },
40189         
40190         onKeyUp : function(e){
40191             
40192             var k = e.getKey();
40193             var c = e.getCharCode();
40194             
40195             if(
40196                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40197                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40198             ){
40199                 e.stopEvent();
40200             }
40201             
40202             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40203             //     return;
40204             // }
40205             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40206                 e.stopEvent();
40207             }
40208             
40209             this.setValue(this.getValue());
40210         }
40211         
40212 });
40213 /**
40214  * @class Roo.bootstrap.MoneyField
40215  * @extends Roo.bootstrap.ComboBox
40216  * Bootstrap MoneyField class
40217  * 
40218  * @constructor
40219  * Create a new MoneyField.
40220  * @param {Object} config Configuration options
40221  */
40222
40223 Roo.bootstrap.MoneyField = function(config) {
40224     
40225     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40226     
40227 };
40228
40229 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40230     
40231     /**
40232      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40233      */
40234     allowDecimals : true,
40235     /**
40236      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40237      */
40238     decimalSeparator : ".",
40239     /**
40240      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40241      */
40242     decimalPrecision : 0,
40243     /**
40244      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40245      */
40246     allowNegative : true,
40247     /**
40248      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40249      */
40250     allowZero: true,
40251     /**
40252      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40253      */
40254     minValue : Number.NEGATIVE_INFINITY,
40255     /**
40256      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40257      */
40258     maxValue : Number.MAX_VALUE,
40259     /**
40260      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40261      */
40262     minText : "The minimum value for this field is {0}",
40263     /**
40264      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40265      */
40266     maxText : "The maximum value for this field is {0}",
40267     /**
40268      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40269      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40270      */
40271     nanText : "{0} is not a valid number",
40272     /**
40273      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40274      */
40275     castInt : true,
40276     /**
40277      * @cfg {String} defaults currency of the MoneyField
40278      * value should be in lkey
40279      */
40280     defaultCurrency : false,
40281     /**
40282      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40283      */
40284     thousandsDelimiter : false,
40285     
40286     
40287     inputlg : 9,
40288     inputmd : 9,
40289     inputsm : 9,
40290     inputxs : 6,
40291     
40292     store : false,
40293     
40294     getAutoCreate : function()
40295     {
40296         var align = this.labelAlign || this.parentLabelAlign();
40297         
40298         var id = Roo.id();
40299
40300         var cfg = {
40301             cls: 'form-group',
40302             cn: []
40303         };
40304
40305         var input =  {
40306             tag: 'input',
40307             id : id,
40308             cls : 'form-control roo-money-amount-input',
40309             autocomplete: 'new-password'
40310         };
40311         
40312         var hiddenInput = {
40313             tag: 'input',
40314             type: 'hidden',
40315             id: Roo.id(),
40316             cls: 'hidden-number-input'
40317         };
40318         
40319         if (this.name) {
40320             hiddenInput.name = this.name;
40321         }
40322
40323         if (this.disabled) {
40324             input.disabled = true;
40325         }
40326
40327         var clg = 12 - this.inputlg;
40328         var cmd = 12 - this.inputmd;
40329         var csm = 12 - this.inputsm;
40330         var cxs = 12 - this.inputxs;
40331         
40332         var container = {
40333             tag : 'div',
40334             cls : 'row roo-money-field',
40335             cn : [
40336                 {
40337                     tag : 'div',
40338                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40339                     cn : [
40340                         {
40341                             tag : 'div',
40342                             cls: 'roo-select2-container input-group',
40343                             cn: [
40344                                 {
40345                                     tag : 'input',
40346                                     cls : 'form-control roo-money-currency-input',
40347                                     autocomplete: 'new-password',
40348                                     readOnly : 1,
40349                                     name : this.currencyName
40350                                 },
40351                                 {
40352                                     tag :'span',
40353                                     cls : 'input-group-addon',
40354                                     cn : [
40355                                         {
40356                                             tag: 'span',
40357                                             cls: 'caret'
40358                                         }
40359                                     ]
40360                                 }
40361                             ]
40362                         }
40363                     ]
40364                 },
40365                 {
40366                     tag : 'div',
40367                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40368                     cn : [
40369                         {
40370                             tag: 'div',
40371                             cls: this.hasFeedback ? 'has-feedback' : '',
40372                             cn: [
40373                                 input
40374                             ]
40375                         }
40376                     ]
40377                 }
40378             ]
40379             
40380         };
40381         
40382         if (this.fieldLabel.length) {
40383             var indicator = {
40384                 tag: 'i',
40385                 tooltip: 'This field is required'
40386             };
40387
40388             var label = {
40389                 tag: 'label',
40390                 'for':  id,
40391                 cls: 'control-label',
40392                 cn: []
40393             };
40394
40395             var label_text = {
40396                 tag: 'span',
40397                 html: this.fieldLabel
40398             };
40399
40400             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40401             label.cn = [
40402                 indicator,
40403                 label_text
40404             ];
40405
40406             if(this.indicatorpos == 'right') {
40407                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40408                 label.cn = [
40409                     label_text,
40410                     indicator
40411                 ];
40412             }
40413
40414             if(align == 'left') {
40415                 container = {
40416                     tag: 'div',
40417                     cn: [
40418                         container
40419                     ]
40420                 };
40421
40422                 if(this.labelWidth > 12){
40423                     label.style = "width: " + this.labelWidth + 'px';
40424                 }
40425                 if(this.labelWidth < 13 && this.labelmd == 0){
40426                     this.labelmd = this.labelWidth;
40427                 }
40428                 if(this.labellg > 0){
40429                     label.cls += ' col-lg-' + this.labellg;
40430                     input.cls += ' col-lg-' + (12 - this.labellg);
40431                 }
40432                 if(this.labelmd > 0){
40433                     label.cls += ' col-md-' + this.labelmd;
40434                     container.cls += ' col-md-' + (12 - this.labelmd);
40435                 }
40436                 if(this.labelsm > 0){
40437                     label.cls += ' col-sm-' + this.labelsm;
40438                     container.cls += ' col-sm-' + (12 - this.labelsm);
40439                 }
40440                 if(this.labelxs > 0){
40441                     label.cls += ' col-xs-' + this.labelxs;
40442                     container.cls += ' col-xs-' + (12 - this.labelxs);
40443                 }
40444             }
40445         }
40446
40447         cfg.cn = [
40448             label,
40449             container,
40450             hiddenInput
40451         ];
40452         
40453         var settings = this;
40454
40455         ['xs','sm','md','lg'].map(function(size){
40456             if (settings[size]) {
40457                 cfg.cls += ' col-' + size + '-' + settings[size];
40458             }
40459         });
40460         
40461         return cfg;
40462     },
40463     
40464     initEvents : function()
40465     {
40466         this.indicator = this.indicatorEl();
40467         
40468         this.initCurrencyEvent();
40469         
40470         this.initNumberEvent();
40471     },
40472     
40473     initCurrencyEvent : function()
40474     {
40475         if (!this.store) {
40476             throw "can not find store for combo";
40477         }
40478         
40479         this.store = Roo.factory(this.store, Roo.data);
40480         this.store.parent = this;
40481         
40482         this.createList();
40483         
40484         this.triggerEl = this.el.select('.input-group-addon', true).first();
40485         
40486         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40487         
40488         var _this = this;
40489         
40490         (function(){
40491             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40492             _this.list.setWidth(lw);
40493         }).defer(100);
40494         
40495         this.list.on('mouseover', this.onViewOver, this);
40496         this.list.on('mousemove', this.onViewMove, this);
40497         this.list.on('scroll', this.onViewScroll, this);
40498         
40499         if(!this.tpl){
40500             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40501         }
40502         
40503         this.view = new Roo.View(this.list, this.tpl, {
40504             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40505         });
40506         
40507         this.view.on('click', this.onViewClick, this);
40508         
40509         this.store.on('beforeload', this.onBeforeLoad, this);
40510         this.store.on('load', this.onLoad, this);
40511         this.store.on('loadexception', this.onLoadException, this);
40512         
40513         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40514             "up" : function(e){
40515                 this.inKeyMode = true;
40516                 this.selectPrev();
40517             },
40518
40519             "down" : function(e){
40520                 if(!this.isExpanded()){
40521                     this.onTriggerClick();
40522                 }else{
40523                     this.inKeyMode = true;
40524                     this.selectNext();
40525                 }
40526             },
40527
40528             "enter" : function(e){
40529                 this.collapse();
40530                 
40531                 if(this.fireEvent("specialkey", this, e)){
40532                     this.onViewClick(false);
40533                 }
40534                 
40535                 return true;
40536             },
40537
40538             "esc" : function(e){
40539                 this.collapse();
40540             },
40541
40542             "tab" : function(e){
40543                 this.collapse();
40544                 
40545                 if(this.fireEvent("specialkey", this, e)){
40546                     this.onViewClick(false);
40547                 }
40548                 
40549                 return true;
40550             },
40551
40552             scope : this,
40553
40554             doRelay : function(foo, bar, hname){
40555                 if(hname == 'down' || this.scope.isExpanded()){
40556                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40557                 }
40558                 return true;
40559             },
40560
40561             forceKeyDown: true
40562         });
40563         
40564         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40565         
40566     },
40567     
40568     initNumberEvent : function(e)
40569     {
40570         this.inputEl().on("keydown" , this.fireKey,  this);
40571         this.inputEl().on("focus", this.onFocus,  this);
40572         this.inputEl().on("blur", this.onBlur,  this);
40573         
40574         this.inputEl().relayEvent('keyup', this);
40575         
40576         if(this.indicator){
40577             this.indicator.addClass('invisible');
40578         }
40579  
40580         this.originalValue = this.getValue();
40581         
40582         if(this.validationEvent == 'keyup'){
40583             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40584             this.inputEl().on('keyup', this.filterValidation, this);
40585         }
40586         else if(this.validationEvent !== false){
40587             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40588         }
40589         
40590         if(this.selectOnFocus){
40591             this.on("focus", this.preFocus, this);
40592             
40593         }
40594         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40595             this.inputEl().on("keypress", this.filterKeys, this);
40596         } else {
40597             this.inputEl().relayEvent('keypress', this);
40598         }
40599         
40600         var allowed = "0123456789";
40601         
40602         if(this.allowDecimals){
40603             allowed += this.decimalSeparator;
40604         }
40605         
40606         if(this.allowNegative){
40607             allowed += "-";
40608         }
40609         
40610         if(this.thousandsDelimiter) {
40611             allowed += ",";
40612         }
40613         
40614         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40615         
40616         var keyPress = function(e){
40617             
40618             var k = e.getKey();
40619             
40620             var c = e.getCharCode();
40621             
40622             if(
40623                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40624                     allowed.indexOf(String.fromCharCode(c)) === -1
40625             ){
40626                 e.stopEvent();
40627                 return;
40628             }
40629             
40630             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40631                 return;
40632             }
40633             
40634             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40635                 e.stopEvent();
40636             }
40637         };
40638         
40639         this.inputEl().on("keypress", keyPress, this);
40640         
40641     },
40642     
40643     onTriggerClick : function(e)
40644     {   
40645         if(this.disabled){
40646             return;
40647         }
40648         
40649         this.page = 0;
40650         this.loadNext = false;
40651         
40652         if(this.isExpanded()){
40653             this.collapse();
40654             return;
40655         }
40656         
40657         this.hasFocus = true;
40658         
40659         if(this.triggerAction == 'all') {
40660             this.doQuery(this.allQuery, true);
40661             return;
40662         }
40663         
40664         this.doQuery(this.getRawValue());
40665     },
40666     
40667     getCurrency : function()
40668     {   
40669         var v = this.currencyEl().getValue();
40670         
40671         return v;
40672     },
40673     
40674     restrictHeight : function()
40675     {
40676         this.list.alignTo(this.currencyEl(), this.listAlign);
40677         this.list.alignTo(this.currencyEl(), this.listAlign);
40678     },
40679     
40680     onViewClick : function(view, doFocus, el, e)
40681     {
40682         var index = this.view.getSelectedIndexes()[0];
40683         
40684         var r = this.store.getAt(index);
40685         
40686         if(r){
40687             this.onSelect(r, index);
40688         }
40689     },
40690     
40691     onSelect : function(record, index){
40692         
40693         if(this.fireEvent('beforeselect', this, record, index) !== false){
40694         
40695             this.setFromCurrencyData(index > -1 ? record.data : false);
40696             
40697             this.collapse();
40698             
40699             this.fireEvent('select', this, record, index);
40700         }
40701     },
40702     
40703     setFromCurrencyData : function(o)
40704     {
40705         var currency = '';
40706         
40707         this.lastCurrency = o;
40708         
40709         if (this.currencyField) {
40710             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40711         } else {
40712             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40713         }
40714         
40715         this.lastSelectionText = currency;
40716         
40717         //setting default currency
40718         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40719             this.setCurrency(this.defaultCurrency);
40720             return;
40721         }
40722         
40723         this.setCurrency(currency);
40724     },
40725     
40726     setFromData : function(o)
40727     {
40728         var c = {};
40729         
40730         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40731         
40732         this.setFromCurrencyData(c);
40733         
40734         var value = '';
40735         
40736         if (this.name) {
40737             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40738         } else {
40739             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40740         }
40741         
40742         this.setValue(value);
40743         
40744     },
40745     
40746     setCurrency : function(v)
40747     {   
40748         this.currencyValue = v;
40749         
40750         if(this.rendered){
40751             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40752             this.validate();
40753         }
40754     },
40755     
40756     setValue : function(v)
40757     {
40758         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40759         
40760         this.value = v;
40761         
40762         if(this.rendered){
40763             
40764             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40765             
40766             this.inputEl().dom.value = (v == '') ? '' :
40767                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40768             
40769             if(!this.allowZero && v === '0') {
40770                 this.hiddenEl().dom.value = '';
40771                 this.inputEl().dom.value = '';
40772             }
40773             
40774             this.validate();
40775         }
40776     },
40777     
40778     getRawValue : function()
40779     {
40780         var v = this.inputEl().getValue();
40781         
40782         return v;
40783     },
40784     
40785     getValue : function()
40786     {
40787         return this.fixPrecision(this.parseValue(this.getRawValue()));
40788     },
40789     
40790     parseValue : function(value)
40791     {
40792         if(this.thousandsDelimiter) {
40793             value += "";
40794             r = new RegExp(",", "g");
40795             value = value.replace(r, "");
40796         }
40797         
40798         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40799         return isNaN(value) ? '' : value;
40800         
40801     },
40802     
40803     fixPrecision : function(value)
40804     {
40805         if(this.thousandsDelimiter) {
40806             value += "";
40807             r = new RegExp(",", "g");
40808             value = value.replace(r, "");
40809         }
40810         
40811         var nan = isNaN(value);
40812         
40813         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40814             return nan ? '' : value;
40815         }
40816         return parseFloat(value).toFixed(this.decimalPrecision);
40817     },
40818     
40819     decimalPrecisionFcn : function(v)
40820     {
40821         return Math.floor(v);
40822     },
40823     
40824     validateValue : function(value)
40825     {
40826         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40827             return false;
40828         }
40829         
40830         var num = this.parseValue(value);
40831         
40832         if(isNaN(num)){
40833             this.markInvalid(String.format(this.nanText, value));
40834             return false;
40835         }
40836         
40837         if(num < this.minValue){
40838             this.markInvalid(String.format(this.minText, this.minValue));
40839             return false;
40840         }
40841         
40842         if(num > this.maxValue){
40843             this.markInvalid(String.format(this.maxText, this.maxValue));
40844             return false;
40845         }
40846         
40847         return true;
40848     },
40849     
40850     validate : function()
40851     {
40852         if(this.disabled || this.allowBlank){
40853             this.markValid();
40854             return true;
40855         }
40856         
40857         var currency = this.getCurrency();
40858         
40859         if(this.validateValue(this.getRawValue()) && currency.length){
40860             this.markValid();
40861             return true;
40862         }
40863         
40864         this.markInvalid();
40865         return false;
40866     },
40867     
40868     getName: function()
40869     {
40870         return this.name;
40871     },
40872     
40873     beforeBlur : function()
40874     {
40875         if(!this.castInt){
40876             return;
40877         }
40878         
40879         var v = this.parseValue(this.getRawValue());
40880         
40881         if(v || v == 0){
40882             this.setValue(v);
40883         }
40884     },
40885     
40886     onBlur : function()
40887     {
40888         this.beforeBlur();
40889         
40890         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40891             //this.el.removeClass(this.focusClass);
40892         }
40893         
40894         this.hasFocus = false;
40895         
40896         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40897             this.validate();
40898         }
40899         
40900         var v = this.getValue();
40901         
40902         if(String(v) !== String(this.startValue)){
40903             this.fireEvent('change', this, v, this.startValue);
40904         }
40905         
40906         this.fireEvent("blur", this);
40907     },
40908     
40909     inputEl : function()
40910     {
40911         return this.el.select('.roo-money-amount-input', true).first();
40912     },
40913     
40914     currencyEl : function()
40915     {
40916         return this.el.select('.roo-money-currency-input', true).first();
40917     },
40918     
40919     hiddenEl : function()
40920     {
40921         return this.el.select('input.hidden-number-input',true).first();
40922     }
40923     
40924 });